Skip to content
Back to skills

2291 Stored Procedure Optimization Techniques 0c900e00

ASecurity

```sql -- SLOW: Row-by-row cursor processing CREATE FUNCTION update_prices_slow() RETURNS VOID AS $$ DECLARE rec RECORD; BEGIN FOR rec IN SELECT id, price FROM products LOOP UPDATE products SET price = rec.price * 1.1 WHERE id = rec.id; END LOOP; END; $$ LANGUAGE plpgsql;

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
databasessqlperformance

Works with

  • cursor

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2291-stored_procedure_optimization_techniques_0c900e00 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2291 Stored Procedure Optimization Techniques 0c900e00?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2291 Stored Procedure Optimization Techniques 0c900e00
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2291-stored-procedure-optimization-techniques-0c90/badge)](https://www.skillsdirectory.com/skills/tools-only-2291-stored-procedure-optimization-techniques-0c90)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Stored Procedure Optimization Techniques

## Set-Based vs Row-Based Operations

### Avoid Cursors When Possible

```sql
-- SLOW: Row-by-row cursor processing
CREATE FUNCTION update_prices_slow()
RETURNS VOID AS $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT id, price FROM products LOOP
        UPDATE products SET price = rec.price * 1.1 WHERE id = rec.id;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- FAST: Set-based operation
CREATE FUNCTION update_prices_fast()
RETURNS INT AS $$
    UPDATE products SET price = price * 1.1;
    SELECT COUNT(*) FROM products;
$$ LANGUAGE sql;
```

**Performance difference**: Set-based is typically 10-100x faster.

## Reduce Network Round Trips

### Batch Operations

```sql
-- SLOW: Multiple calls
CALL insert_order_item(1, 101, 2);
CALL insert_order_item(1, 102, 1);
CALL insert_order_item(1, 103, 5);

-- FAST: Single call with table parameter (SQL Server)
CREATE PROCEDURE dbo.InsertOrderItems
    @OrderId INT,
    @Items dbo.OrderItemType READONLY
AS
BEGIN
    INSERT INTO OrderItems (OrderId, ProductId, Quantity)
    SELECT @OrderId, ProductId, Quantity FROM @Items;
END;

-- FAST: Single call with JSON (PostgreSQL/MySQL)
CREATE FUNCTION insert_order_items(p_order_id INT, p_items JSONB)
RETURNS VOID AS $$
BEGIN
    INSERT INTO order_items (order_id, product_id, quantity)
    SELECT p_order_id, (item->>'product_id')::INT, (item->>'quantity')::INT
    FROM jsonb_array_elements(p_items) AS item;
END;
$$ LANGUAGE plpgsql;
```

## Efficient Indexing

### Use Covering Indexes

```sql
-- Index covers all columns needed by query
CREATE INDEX idx_orders_covering ON orders (customer_id)
    INCLUDE (order_date, total_amount, status);

-- Procedure benefits from index-only scan
CREATE FUNCTION get_customer_orders(p_customer_id INT)
RETURNS TABLE(order_date DATE, total DECIMAL, status VARCHAR) AS $$
    SELECT order_date, total_amount, status
    FROM orders
    WHERE customer_id = p_customer_id;
$$ LANGUAGE sql STABLE;
```

### Index for Procedure Parameters

```sql
-- If procedure frequently filters by status + date
CREATE INDEX idx_orders_status_date ON orders (status, order_date DESC);

-- Procedure uses this index efficiently
CREATE FUNCTION get_pending_orders(p_days INT)
RETURNS SETOF orders AS $$
    SELECT * FROM orders
    WHERE status = 'pending'
      AND order_date > CURRENT_DATE - p_days;
$$ LANGUAGE sql STABLE;
```

## Query Plan Optimization

### PostgreSQL: Analyze Query Plans

```sql
CREATE FUNCTION analyze_query_example()
RETURNS TEXT AS $$
DECLARE
    v_plan TEXT;
BEGIN
    EXPLAIN (ANALYZE, FORMAT TEXT) INTO v_plan
    SELECT * FROM large_table WHERE indexed_col = 123;

    RETURN v_plan;
END;
$$ LANGUAGE plpgsql;
```

### SQL Server: Query Hints

```sql
CREATE PROCEDURE dbo.OptimizedSearch
    @SearchTerm NVARCHAR(100)
AS
BEGIN
    SELECT ProductId, ProductName
    FROM Products WITH (INDEX(IX_Products_Name))
    WHERE ProductName LIKE @SearchTerm + '%'
    OPTION (RECOMPILE);  -- Fresh plan each execution
END;
```

## Caching and Memoization

### PostgreSQL: Materialized Views

```sql
-- Create materialized view for expensive aggregations
CREATE MATERIALIZED VIEW mv_daily_sales AS
SELECT
    DATE(order_date) AS sale_date,
    SUM(total_amount) AS daily_total,
    COUNT(*) AS order_count
FROM orders
GROUP BY DATE(order_date);

-- Procedure uses cached data
CREATE FUNCTION get_daily_sales(p_date DATE)
RETURNS TABLE(daily_total DECIMAL, order_count BIGINT) AS $$
    SELECT daily_total, order_count
    FROM mv_daily_sales
    WHERE sale_date = p_date;
$$ LANGUAGE sql STABLE;

-- Refresh periodically
CREATE FUNCTION refresh_sales_cache()
RETURNS VOID AS $$
    REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_sales;
$$ LANGUAGE sql;
```

## Volatility Declarations (PostgreSQL)

```sql
-- IMMUTABLE: Same input = same output, always
-- Can be used in indexes, parallelized
CREATE FUNCTION calculate_tax(amount DECIMAL)
RETURNS DECIMAL AS $$
    SELECT amount * 0.08;
$$ LANGUAGE sql IMMUTABLE;

-- STABLE: Same output within single transaction
-- Safe for index scans
CREATE FUNCTION get_current_user_id()
RETURNS INT AS $$
    SELECT current_setting('app.user_id')::INT;
$$ LANGUAGE sql STABLE;

-- VOLATILE: May return different values (default)
-- Prevents certain optimizations
CREATE FUNCTION log_and_return(val INT)
RETURNS INT AS $$
BEGIN
    INSERT INTO log_table (value, logged_at) VALUES (val, NOW());
    RETURN val;
END;
$$ LANGUAGE plpgsql VOLATILE;
```

## Reduce Lock Contention

### Use Appropriate Isolation Levels

```sql
-- PostgreSQL: Read-only procedure with reduced locking
CREATE FUNCTION read_only_report()
RETURNS SETOF report_type AS $$
BEGIN
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED READ ONLY;
    RETURN QUERY SELECT * FROM generate_report();
END;
$$ LANGUAGE plpgsql;
```

### Batch Large Updates

```sql
-- Instead of updating 1M rows at once
-- Process in batches to reduce lock time
CREATE PROCEDURE dbo.BatchUpdate
AS
BEGIN
    DECLARE @BatchSize INT = 10000;
    DECLARE @Affected INT = 1;

    WHILE @Affected > 0
    BEGIN
        UPDATE TOP (@BatchSize) Products
        SET LastUpdated = GETDATE()
        WHERE LastUpdated < DATEADD(YEAR, -1, GETDATE());

        SET @Affected = @@ROWCOUNT;

        -- Allow other transactions to proceed
        WAITFOR DELAY '00:00:00.100';
    END
END;
```

## Statistics and Plan Cache

### Keep Statistics Updated

```sql
-- PostgreSQL
ANALYZE table_name;

-- SQL Server
UPDATE STATISTICS table_name;

-- MySQL
ANALYZE TABLE table_name;
```

### Clear Plan Cache When Needed

```sql
-- SQL Server: Clear specific procedure plan
EXEC sp_recompile 'dbo.ProcedureName';

-- PostgreSQL: No procedure-specific cache, but:
DISCARD PLANS;  -- Clears all plans in session
```

## Optimization Checklist

| Technique | Impact | When to Use |
|-----------|--------|-------------|
| Set-based operations | High | Always prefer over cursors |
| Covering indexes | High | Frequent queries with predictable columns |
| Batch processing | Medium | Large data modifications |
| Volatility hints | Medium | PostgreSQL pure functions |
| Query hints | Low | Last resort after other optimizations |
| Plan recompile | Low | Variable parameter distribution |

## Monitoring and Profiling

```sql
-- PostgreSQL: Enable timing
SET track_functions = 'all';
SELECT * FROM pg_stat_user_functions;

-- SQL Server: Execution statistics
SELECT
    OBJECT_NAME(object_id) AS ProcedureName,
    execution_count,
    total_worker_time / 1000 AS TotalCPU_ms,
    total_elapsed_time / 1000 AS TotalElapsed_ms,
    total_logical_reads
FROM sys.dm_exec_procedure_stats
ORDER BY total_worker_time DESC;

-- MySQL: Performance schema
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.