DEV Community

Cover image for #34 50 Advanced SQL Queries Every Developer Should Know
Florian Zeba
Florian Zeba

Posted on • Originally published at fzeba.com

#34 50 Advanced SQL Queries Every Developer Should Know

SQL is a powerful language for managing and querying relational databases. While basic queries like SELECT, INSERT, UPDATE, and DELETE are essential, mastering advanced SQL techniques can significantly enhance your ability to analyze data, optimize performance, and solve complex problems.

In this article, we’ll explore 50 advanced SQL queries that cover window functions, recursive CTEs, pivoting, performance optimization, and more.

1. Window Functions (Analytical Queries)

Window functions allow computations across a set of table rows related to the current row.

1.1. ROW_NUMBER() – Assign a Unique Row Number

SELECT employee_id, name, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS rank FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.2. RANK() – Rank with Gaps for Ties

SELECT employee_id, name, salary, RANK() OVER (ORDER BY salary DESC) AS rank FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.3. DENSE_RANK() – Rank Without Gaps

SELECT employee_id, name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rank FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.4. NTILE() – Divide Rows into Buckets

SELECT employee_id, name, salary, NTILE(4) OVER (ORDER BY salary DESC) AS quartile FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.5. LEAD() – Access Next Row’s Value

SELECT employee_id, name, salary, LEAD(salary, 1) OVER (ORDER BY salary DESC) AS next_salary FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.6. LAG() – Access Previous Row’s Value

SELECT employee_id, name, salary, LAG(salary, 1) OVER (ORDER BY salary DESC) AS prev_salary FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.7. FIRST_VALUE() – Get First Value in a Window

SELECT employee_id, name, salary, FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS highest_in_dept FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.8. LAST_VALUE() – Get Last Value in a Window

SELECT employee_id, name, salary, LAST_VALUE(salary) OVER ( PARTITION BY department ORDER BY salary DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS lowest_in_dept FROM employees; 
Enter fullscreen mode Exit fullscreen mode

1.9. Running Total with SUM() OVER

SELECT date, revenue, SUM(revenue) OVER (ORDER BY date) AS running_total FROM sales; 
Enter fullscreen mode Exit fullscreen mode

1.10. Moving Average

SELECT date, revenue, AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales; 
Enter fullscreen mode Exit fullscreen mode

2. Common Table Expressions (CTEs) and Recursive Queries

CTEs improve readability and allow recursive operations.

2.1. Basic CTE

WITH high_earners AS ( SELECT * FROM employees WHERE salary > 100000 ) SELECT * FROM high_earners; 
Enter fullscreen mode Exit fullscreen mode

2.2. Recursive CTE (Hierarchical Data)

WITH RECURSIVE employee_hierarchy AS ( -- Base case: CEO (no manager) SELECT id, name, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL -- Recursive case: Employees with managers SELECT e.id, e.name, e.manager_id, eh.level + 1 FROM employees e JOIN employee_hierarchy eh ON e.manager_id = eh.id ) SELECT * FROM employee_hierarchy; 
Enter fullscreen mode Exit fullscreen mode

2.3. Multiple CTEs in a Single Query

WITH dept_stats AS ( SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department ), high_paying_depts AS ( SELECT department FROM dept_stats WHERE avg_salary > 80000 ) SELECT e.* FROM employees e JOIN high_paying_depts hpd ON e.department = hpd.department; 
Enter fullscreen mode Exit fullscreen mode

3. Pivoting and Unpivoting Data

3.1. Pivot with CASE

SELECT product_id, SUM(CASE WHEN region = 'North' THEN sales ELSE 0 END) AS north_sales, SUM(CASE WHEN region = 'South' THEN sales ELSE 0 END) AS south_sales, SUM(CASE WHEN region = 'East' THEN sales ELSE 0 END) AS east_sales, SUM(CASE WHEN region = 'West' THEN sales ELSE 0 END) AS west_sales FROM sales GROUP BY product_id; 
Enter fullscreen mode Exit fullscreen mode

3.2. Pivot with PIVOT (SQL Server, Oracle)

SELECT * FROM ( SELECT product_id, region, sales FROM sales ) AS src PIVOT ( SUM(sales) FOR region IN ([North], [South], [East], [West]) ) AS pvt; 
Enter fullscreen mode Exit fullscreen mode

3.3. Unpivot Data

SELECT product_id, region, sales FROM ( SELECT product_id, north_sales, south_sales, east_sales, west_sales FROM pivoted_sales ) AS src UNPIVOT ( sales FOR region IN (north_sales, south_sales, east_sales, west_sales) ) AS unpvt; 
Enter fullscreen mode Exit fullscreen mode

4. Advanced Joins and Subqueries

4.1. Self-Join (Find Employees with Same Manager)

SELECT e1.name AS employee1, e2.name AS employee2, e1.manager_id FROM employees e1 JOIN employees e2 ON e1.manager_id = e2.manager_id AND e1.id < e2.id; 
Enter fullscreen mode Exit fullscreen mode

4.2. Lateral Join (PostgreSQL)

SELECT d.department_name, e.name, e.salary FROM departments d CROSS JOIN LATERAL ( SELECT name, salary FROM employees WHERE department_id = d.id ORDER BY salary DESC LIMIT 3 ) e; 
Enter fullscreen mode Exit fullscreen mode

4.3. Correlated Subquery (Find Employees Earning Above Avg in Dept)

SELECT e1.name, e1.salary, e1.department FROM employees e1 WHERE e1.salary > ( SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department = e1.department ); 
Enter fullscreen mode Exit fullscreen mode

5. Performance Optimization

5.1. Index Hinting (Force Index Usage)

SELECT * FROM employees WITH (INDEX(idx_salary)) WHERE salary > 50000; 
Enter fullscreen mode Exit fullscreen mode

5.2. Query Plan Analysis (EXPLAIN)

EXPLAIN ANALYZE SELECT * FROM employees WHERE department = 'Engineering'; 
Enter fullscreen mode Exit fullscreen mode

5.3. Materialized Views (Precompute Expensive Queries)

CREATE MATERIALIZED VIEW mv_high_earners AS SELECT * FROM employees WHERE salary > 100000; REFRESH MATERIALIZED VIEW mv_high_earners; 
Enter fullscreen mode Exit fullscreen mode

6. Advanced Aggregations

6.1. ROLLUP (Hierarchical Grouping)

SELECT department, job_title, SUM(salary) AS total_salary FROM employees GROUP BY ROLLUP(department, job_title); 
Enter fullscreen mode Exit fullscreen mode

6.2. CUBE (All Possible Groupings)

SELECT department, job_title, SUM(salary) AS total_salary FROM employees GROUP BY CUBE(department, job_title); 
Enter fullscreen mode Exit fullscreen mode

6.3. GROUPING SETS (Custom Groupings)

SELECT department, job_title, SUM(salary) AS total_salary FROM employees GROUP BY GROUPING SETS ( (department, job_title), (department), (job_title), () ); 
Enter fullscreen mode Exit fullscreen mode

7. JSON and XML Handling

7.1. Extract JSON Fields

SELECT id, json_data->>'name' AS name, json_data->>'age' AS age FROM users; 
Enter fullscreen mode Exit fullscreen mode

7.2. Query Nested JSON Arrays

SELECT id, json_array_elements(json_data->'skills') AS skill FROM users; 
Enter fullscreen mode Exit fullscreen mode

7.3. XML Parsing

SELECT id, xpath('//name/text()', xml_data) AS name, xpath('//age/text()', xml_data) AS age FROM users; 
Enter fullscreen mode Exit fullscreen mode

8. Dynamic SQL

8.1. Execute Dynamic Query (SQL Injection Safe)

EXECUTE format('SELECT * FROM %I WHERE salary > %L', 'employees', 50000); 
Enter fullscreen mode Exit fullscreen mode

8.2. Generate and Run SQL in a Loop

DO $$ DECLARE query TEXT; BEGIN FOR i IN 1..10 LOOP query := format('INSERT INTO logs (message) VALUES (%L)', 'Log ' || i); EXECUTE query; END LOOP; END $$; 
Enter fullscreen mode Exit fullscreen mode

9. Advanced Joins and Set Operations

9.1. FULL OUTER JOIN (Find All Matches and Non-Matches)

SELECT e.employee_id, e.name, d.department_name FROM employees e FULL OUTER JOIN departments d ON e.department_id = d.department_id; 
Enter fullscreen mode Exit fullscreen mode

9.2. NATURAL JOIN (Join on Columns with Same Name)

SELECT * FROM employees NATURAL JOIN departments; 
Enter fullscreen mode Exit fullscreen mode

9.3. INTERSECT (Find Common Records Between Two Queries)

SELECT employee_id FROM full_time_employees INTERSECT SELECT employee_id FROM high_performers; 
Enter fullscreen mode Exit fullscreen mode

9.4. EXCEPT (Find Records in First Query but Not Second)

SELECT employee_id FROM all_employees EXCEPT SELECT employee_id FROM terminated_employees; 
Enter fullscreen mode Exit fullscreen mode

9.5. UNION ALL (Combine Results with Duplicates)

SELECT name, salary FROM current_employees UNION ALL SELECT name, salary FROM former_employees; 
Enter fullscreen mode Exit fullscreen mode

10. Advanced Subqueries

10.1. EXISTS (Check for Related Records)

SELECT e.name FROM employees e WHERE EXISTS ( SELECT 1 FROM sales s WHERE s.employee_id = e.employee_id AND s.amount > 10000 ); 
Enter fullscreen mode Exit fullscreen mode

10.2. NOT EXISTS (Find Records Without Related Data)

SELECT d.department_name FROM departments d WHERE NOT EXISTS ( SELECT 1 FROM employees e WHERE e.department_id = d.department_id ); 
Enter fullscreen mode Exit fullscreen mode

10.3. IN with Subquery (Filter Based on Another Query)

SELECT name, salary FROM employees WHERE department_id IN ( SELECT department_id FROM departments WHERE location = 'New York' ); 
Enter fullscreen mode Exit fullscreen mode

10.4. ALL (Compare Against All Values in Subquery)

SELECT name, salary FROM employees WHERE salary > ALL ( SELECT salary FROM employees WHERE department = 'Intern' ); 
Enter fullscreen mode Exit fullscreen mode

10.5. ANY/SOME (Compare Against Any Value in Subquery)

SELECT name, salary FROM employees WHERE salary > ANY ( SELECT salary FROM employees WHERE department = 'Management' ); 
Enter fullscreen mode Exit fullscreen mode

11. Advanced Data Modification

11.1. UPSERT (INSERT or UPDATE on Conflict)

INSERT INTO employees (id, name, salary) VALUES (101, 'John Doe', 75000) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, salary = EXCLUDED.salary; 
Enter fullscreen mode Exit fullscreen mode

11.2. MERGE (Conditional INSERT/UPDATE/DELETE)

MERGE INTO employees e USING updated_employees ue ON e.id = ue.id WHEN MATCHED THEN UPDATE SET e.name = ue.name, e.salary = ue.salary WHEN NOT MATCHED THEN INSERT (id, name, salary) VALUES (ue.id, ue.name, ue.salary); 
Enter fullscreen mode Exit fullscreen mode

11.3. DELETE with JOIN

DELETE FROM employees USING departments WHERE employees.department_id = departments.department_id AND departments.location = 'Remote'; 
Enter fullscreen mode Exit fullscreen mode

11.4. UPDATE from Another Table

UPDATE employees e SET salary = e.salary * 1.1 FROM departments d WHERE e.department_id = d.department_id AND d.budget > 1000000; 
Enter fullscreen mode Exit fullscreen mode

12. Database Administration & Meta-Queries

12.1. List All Tables in a Database

SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; 
Enter fullscreen mode Exit fullscreen mode

12.2. Find Column Names in a Table

SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'employees'; 
Enter fullscreen mode Exit fullscreen mode

12.3. Check Table Size (PostgreSQL)

SELECT table_name, pg_size_pretty(pg_total_relation_size(table_name)) AS size FROM information_schema.tables WHERE table_schema = 'public'; 
Enter fullscreen mode Exit fullscreen mode

12.4. Find Long-Running Queries

SELECT pid, query, now() - query_start AS duration FROM pg_stat_activity WHERE state = 'active' ORDER BY duration DESC; 
Enter fullscreen mode Exit fullscreen mode

12.5. Kill a Running Query

SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE query LIKE '%long_running_query%'; 
Enter fullscreen mode Exit fullscreen mode

13. Advanced Date & Time Operations

13.1. Generate Date Series

SELECT generate_series( '2023-01-01'::date, '2023-12-31'::date, '1 day'::interval ) AS date; 
Enter fullscreen mode Exit fullscreen mode

13.2. Calculate Business Days Between Dates

SELECT date1, date2, COUNT(*) FILTER (WHERE EXTRACT(DOW FROM day) BETWEEN 1 AND 5) AS business_days FROM ( SELECT '2023-01-01'::date AS date1, '2023-01-31'::date AS date2, generate_series( '2023-01-01'::date, '2023-01-31'::date, '1 day'::interval ) AS day ) t; 
Enter fullscreen mode Exit fullscreen mode

13.3. Find Last Day of Month

SELECT date_trunc('month', current_date) + INTERVAL '1 month - 1 day' AS last_day_of_month; 
Enter fullscreen mode Exit fullscreen mode

14. Advanced String Manipulation

14.1. Regex Extract

SELECT regexp_matches(email, '([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+)\.([A-Za-z]{2,})') FROM users; 
Enter fullscreen mode Exit fullscreen mode

14.2. Split String into Rows

SELECT id, unnest(string_to_array(tags, ',')) AS tag FROM products; 
Enter fullscreen mode Exit fullscreen mode

14.3. Concatenate Rows into String

SELECT department_id, string_agg(name, ', ') AS employees FROM employees GROUP BY department_id; 
Enter fullscreen mode Exit fullscreen mode

15. Advanced Security & Permissions

15.1. Grant Column-Level Permissions

GRANT SELECT (name, email) ON employees TO analyst_role; 
Enter fullscreen mode Exit fullscreen mode

15.2. Create a Read-Only User

CREATE USER readonly WITH PASSWORD 'secure_password'; GRANT CONNECT ON DATABASE mydb TO readonly; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly; 
Enter fullscreen mode Exit fullscreen mode

Conclusion

With these 20 additional advanced SQL queries, we now have a complete list of 50 essential SQL techniques covering:

Window Functions

CTEs & Recursive Queries

Pivoting & Unpivoting

Advanced Joins & Subqueries

Performance Optimization

JSON/XML Handling

Dynamic SQL

Database Administration

Top comments (0)