You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: exercises/intermediate-exercises.md
+264Lines changed: 264 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -744,4 +744,268 @@ HAVING COUNT(DISTINCT DATE_TRUNC('month', order_date)) >= 10;
744
744
*`HAVING` filters only customers active in 10+ months.
745
745
* Useful for VIP programs or retention campaigns.
746
746
747
+
---
748
+
749
+
## 31. Scenario: Employee Hierarchy with Recursion
750
+
751
+
**Context:**
752
+
The HR department wants to list all employees under a particular manager, including indirect reports. The employees table contains `employee_id`, `name`, and `manager_id`.
753
+
754
+
**Question:**
755
+
Write a query to find all employees reporting (directly or indirectly) to manager_id = 1.
756
+
757
+
**Answer:**
758
+
```
759
+
WITH RECURSIVE subordinates AS (
760
+
SELECT employee_id, name, manager_id
761
+
FROM employees
762
+
WHERE manager_id = 1
763
+
UNION ALL
764
+
SELECT e.employee_id, e.name, e.manager_id
765
+
FROM employees e
766
+
INNER JOIN subordinates s ON e.manager_id = s.employee_id
767
+
)
768
+
SELECT * FROM subordinates;
769
+
```
770
+
771
+
**Explanation:**
772
+
* Recursive CTE subordinates finds direct reports first.
773
+
* UNION ALL recursively adds indirect reports.
774
+
* Useful for building organizational hierarchies.
775
+
776
+
---
777
+
778
+
## 32. Scenario: Top 3 Products per Category
779
+
780
+
**Context:**
781
+
Marketing wants to know the top-selling products in each category. The orders table contains product_id, quantity, and price. The products table contains product_id and category.
782
+
783
+
**Question:**
784
+
Write a query to find the top 3 products by revenue in each category.
785
+
**Answer:**
786
+
```
787
+
SELECT category, product_id, total_revenue
788
+
FROM (
789
+
SELECT p.category, o.product_id, SUM(o.quantity * o.price) AS total_revenue,
790
+
RANK() OVER (PARTITION BY p.category ORDER BY SUM(o.quantity * o.price) DESC) AS rnk
791
+
FROM orders o
792
+
JOIN products p ON o.product_id = p.product_id
793
+
GROUP BY p.category, o.product_id
794
+
) t
795
+
WHERE rnk <= 3;
796
+
797
+
```
798
+
799
+
**Explanation:**
800
+
* Uses window function RANK() partitioned by category.
801
+
* Aggregates revenue per product.
802
+
* Filters top 3 per category with WHERE rnk <= 3.
803
+
804
+
---
805
+
806
+
## 33. Scenario: JSONB Nested Filtering
807
+
808
+
**Context:**
809
+
The user table stores preferences in a JSONB column preferences. Each JSON object contains keys like "notifications": {"email": true, "sms": false}.
810
+
811
+
**Question:**
812
+
Write a query to find all users who have email notifications enabled.
813
+
814
+
**Answer:**
815
+
```
816
+
SELECT user_id, preferences
817
+
FROM users
818
+
WHERE preferences -> 'notifications' ->> 'email' = 'true';
819
+
820
+
```
821
+
822
+
**Explanation:**
823
+
* Navigates nested JSONB with -> and ->>.
824
+
* Filters only users with email notifications enabled.
825
+
826
+
---
827
+
828
+
## 34. Scenario: Lateral Join for Latest Order per Customer
829
+
830
+
**Context:**
831
+
The sales team wants each customer’s latest order. The customers table has customer_id. The orders table has order_id, customer_id, and order_date.
832
+
833
+
834
+
**Question:**
835
+
Write a query to retrieve each customer and their most recent order
836
+
837
+
**Answer:**
838
+
```
839
+
SELECT c.customer_id, o.order_id, o.order_date
840
+
FROM customers c
841
+
LEFT JOIN LATERAL (
842
+
SELECT order_id, order_date
843
+
FROM orders
844
+
WHERE customer_id = c.customer_id
845
+
ORDER BY order_date DESC
846
+
LIMIT 1
847
+
) o ON true;
848
+
849
+
```
850
+
851
+
**Explanation:**
852
+
* LATERAL allows referencing the outer query row.
853
+
* Retrieves the latest order per customer efficiently.
747
854
855
+
---
856
+
857
+
## 35. Scenario: Full-Text Search with Ranking
858
+
859
+
**Context:**
860
+
A blog wants to rank articles by relevance to the term "PostgreSQL optimization". The articles table has title and content.
861
+
862
+
**Question:**
863
+
Write a query to rank articles using full-text search.
864
+
865
+
**Answer:**
866
+
```
867
+
SELECT title, ts_rank_cd(to_tsvector(content), to_tsquery('PostgreSQL & optimization')) AS rank
868
+
FROM articles
869
+
WHERE to_tsvector(content) @@ to_tsquery('PostgreSQL & optimization')
870
+
ORDER BY rank DESC;
871
+
872
+
```
873
+
874
+
**Explanation:**
875
+
* ts_rank_cd ranks matches by relevance.
876
+
* @@ filters only relevant rows.
877
+
* Useful for search results sorted by importance.
878
+
879
+
---
880
+
881
+
## 36. Scenario: Materialized View for Monthly Sales
882
+
883
+
**Context:**
884
+
Finance wants a precomputed table of monthly sales totals for faster reporting. Orders table has order_date and total_amount.
885
+
886
+
**Question:**
887
+
Write commands to create a materialized view and refresh it.
888
+
889
+
**Answer:**
890
+
```
891
+
CREATE MATERIALIZED VIEW monthly_sales AS
892
+
SELECT DATE_TRUNC('month', order_date) AS month, SUM(total_amount) AS total_sales
The bank wants to test how SERIALIZABLE isolation prevents lost updates. The accounts table has account_id and balance.
912
+
913
+
**Question:**
914
+
Demonstrate a transaction that safely increments an account balance by 100.
915
+
916
+
**Answer:**
917
+
```
918
+
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
919
+
920
+
UPDATE accounts
921
+
SET balance = balance + 100
922
+
WHERE account_id = 1;
923
+
924
+
COMMIT;
925
+
926
+
```
927
+
928
+
**Explanation:**
929
+
* SERIALIZABLE prevents conflicts with concurrent transactions.
930
+
* Ensures consistency in highly concurrent environments.
931
+
932
+
---
933
+
934
+
## 38. Scenario: Partitioned Table Query
935
+
936
+
**Context:**
937
+
A log system stores millions of rows in a logs table partitioned by month. Each partition is named logs_YYYY_MM.
938
+
939
+
**Question:**
940
+
Write a query to retrieve all logs from October 2025.
941
+
942
+
**Answer:**
943
+
```
944
+
SELECT *
945
+
FROM logs
946
+
WHERE log_date >= '2025-10-01' AND log_date < '2025-11-01';
947
+
948
+
```
949
+
950
+
**Explanation:**
951
+
* Partitioning ensures query only scans relevant data.
952
+
* Improves performance on large tables.
953
+
954
+
---
955
+
956
+
## 39. Scenario: Array Aggregation
957
+
958
+
**Context:**
959
+
Marketing wants a list of all products each customer purchased. Orders table has customer_id and product_id.
960
+
961
+
**Question:**
962
+
Write a query to return each customer with an array of product_ids
963
+
964
+
**Answer:**
965
+
```
966
+
SELECT customer_id, ARRAY_AGG(product_id) AS products
967
+
FROM orders
968
+
GROUP BY customer_id;
969
+
970
+
```
971
+
972
+
**Explanation:**
973
+
* ARRAY_AGG aggregates multiple rows into an array per group.
974
+
* Useful for reporting or exporting data in structured form.
975
+
976
+
---
977
+
978
+
## 40. Scenario: Combined Advanced Query
979
+
980
+
**Context:**
981
+
A platform wants VIP customers (spent > $10k last year) and their top 3 most purchased products. Orders table has customer_id, product_id, and total_amount.
982
+
983
+
**Question:**
984
+
Write a query combining aggregation, ranking, and filtering.
985
+
986
+
**Answer:**
987
+
```
988
+
WITH vip AS (
989
+
SELECT customer_id
990
+
FROM orders
991
+
WHERE order_date >= CURRENT_DATE - INTERVAL '1 year'
992
+
GROUP BY customer_id
993
+
HAVING SUM(total_amount) > 10000
994
+
),
995
+
product_ranks AS (
996
+
SELECT customer_id, product_id, COUNT(*) AS qty,
997
+
RANK() OVER (PARTITION BY customer_id ORDER BY COUNT(*) DESC) AS rnk
998
+
FROM orders
999
+
WHERE customer_id IN (SELECT customer_id FROM vip)
1000
+
GROUP BY customer_id, product_id
1001
+
)
1002
+
SELECT customer_id, product_id, qty
1003
+
FROM product_ranks
1004
+
WHERE rnk <= 3;
1005
+
1006
+
```
1007
+
1008
+
**Explanation:**
1009
+
* CTE vip filters high-value customers.
1010
+
* Window function ranks products per customer.
1011
+
* Combines multiple advanced features in a single query.
0 commit comments