[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6WYABSEGAF5FRV97X3QXBGT":3,"skill-same:01M6WYABSEGAF5FRV97X3QXBGT":93},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":62,"commitSha":63,"skillDirectory":64,"forksCount":14,"repositoryHtmlUrl":65,"repositoryHomepageUrl":66,"ownerHtmlUrl":67,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":68,"install":69,"resources":82},"01M6WYABSEGAF5FRV97X3QXBGT","sql-patterns",null,"SQL query patterns, schema design, and optimization — joins, CTEs, window functions, indexing, and anti-patterns. Use when writing or optimizing SQL queries, designing or reviewing a relational schema, reading EXPLAIN plans, choosing indexes, or implementing pagination, deduplication, or analytical queries.","projectious-work\u002Fprocesskit","projectious-work","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F263889726?v=4","01M6N97TJJ118WQ35CHRV8RD0E","projectious.work","01M6N97TPWJPF0BMKYQ4PBY5W8",0,"MIT","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.13.0\u002Fcontext\u002Fskills\u002Fengineering\u002Fsql-patterns\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"sql","sql patterns","patterns","when","design","sql query","window","implementing","reading","indexing","analytical","query","Optimizing SQL Queries","sql queries","schema design","anti-patterns","work","deduplication","Optimization","writing","2026-11-10 10:12:30",false,"---\nname: sql-patterns\ndescription: |\n  SQL query patterns, schema design, and optimization — joins, CTEs, window functions, indexing, and anti-patterns. Use when writing or optimizing SQL queries, designing or reviewing a relational schema, reading EXPLAIN plans, choosing indexes, or implementing pagination, deduplication, or analytical queries.\nmetadata:\n  processkit:\n    apiVersion: processkit.projectious.work\u002Fv1\n    id: SKILL-sql-patterns\n    version: \"1.1.0\"\n    created: 2026-04-06T00:00:00Z\n    category: engineering\n---\n\n# SQL Patterns\n\n## Intro\n\nSQL rewards thinking in sets and reading execution plans. Build\nqueries incrementally from the core logic outward, lean on CTEs and\nwindow functions for clarity, and verify cost with `EXPLAIN` before\noptimizing anything.\n\n## Overview\n\n### Choose the right join\n\n- `INNER JOIN` — only matching rows in both tables (default, most\n  common).\n- `LEFT JOIN` — all rows from the left table, NULLs where right has\n  no match.\n- `RIGHT JOIN` — rare; rewrite as `LEFT JOIN` for clarity.\n- `FULL OUTER JOIN` — all rows from both, NULLs on the unmatched\n  side.\n- `CROSS JOIN` — cartesian product; use intentionally for generating\n  combinations.\n\n### CTEs over nested subqueries\n\nCTEs (`WITH` clauses) read top-to-bottom and let each step name its\nintent. Prefer them over nested subqueries when a query has more\nthan two levels.\n\n```sql\nWITH active_users AS (\n    SELECT user_id, MAX(login_at) AS last_login\n    FROM logins\n    WHERE login_at > CURRENT_DATE - INTERVAL '30 days'\n    GROUP BY user_id\n),\nuser_orders AS (\n    SELECT user_id, COUNT(*) AS order_count, SUM(total) AS revenue\n    FROM orders\n    GROUP BY user_id\n)\nSELECT u.name, au.last_login, uo.order_count, uo.revenue\nFROM users u\nJOIN active_users au USING (user_id)\nLEFT JOIN user_orders uo USING (user_id);\n```\n\n### Window functions\n\nUse windows for rankings, running totals, and per-row comparisons\nwithout collapsing rows:\n\n```sql\nSELECT\n    department,\n    employee,\n    salary,\n    RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,\n    salary - LAG(salary) OVER (PARTITION BY department ORDER BY salary) AS diff_from_prev,\n    SUM(salary) OVER (PARTITION BY department) AS dept_total\nFROM employees;\n```\n\nRemember the evaluation order: `WHERE` filters rows, `GROUP BY`\ngroups them, `HAVING` filters groups, then window functions run\nafter `HAVING`.\n\n### Optimization workflow\n\n1. **Read the EXPLAIN plan.** Look for sequential scans on large\n   tables, nested loops on big result sets, sort operations without\n   indexes.\n2. **Check indexes.** Ensure `WHERE`, `JOIN`, and `ORDER BY` columns\n   are covered.\n3. **Reduce data early.** Filter in `WHERE` before joining; join on\n   indexed columns.\n4. **Rewrite anti-patterns.** Functions on indexed columns,\n   correlated subqueries, `NOT IN` with NULLs, implicit type casts —\n   all defeat indexes.\n\n### Indexing strategy\n\n- **B-tree** (default) — equality, range, `ORDER BY`, `LIKE\n  'prefix%'`.\n- **Hash** — equality only, smaller than B-tree (Postgres).\n- **GIN** — full-text search, JSONB containment, array operations.\n- **GiST** — geometric data, range types, nearest-neighbor.\n- **Composite** — column order matters (leftmost prefix rule).\n- **Partial** — `WHERE is_active = true` for selective predicates.\n- **Covering** — `INCLUDE` columns to enable index-only scans.\n\nSee `references\u002Fschema-design.md` for full schema-design patterns\nand `references\u002Fquery-patterns.md` for query recipes.\n\n## Gotchas\n\nAgent-specific failure modes — provider-neutral pause-and-self-check items:\n\n- **SELECT * in production queries.** Selecting all columns returns data the application doesn't use, breaks when columns are added or removed, and prevents index-only scans. Always name the columns you need explicitly so the query is stable, the network payload is minimal, and the planner can use covering indexes.\n- **Functions on indexed columns in WHERE clauses.** Writing `WHERE LOWER(email) = ?` or `WHERE DATE(created_at) = ?` forces the planner to evaluate the function on every row, making the index on `email` or `created_at` unusable. Use a functional index, normalize the data at write time, or rewrite the predicate to operate on the raw column.\n- **NOT IN with a subquery that can return NULLs.** `WHERE id NOT IN (SELECT user_id FROM orders)` returns zero rows if any `user_id` in `orders` is NULL, because `x NOT IN (..., NULL, ...)` evaluates to NULL for every x. Use `NOT EXISTS` or filter out NULLs in the subquery explicitly.\n- **Correlated subqueries that execute once per row.** A subquery in the SELECT list or WHERE clause that references the outer query's row runs once for every row in the outer result set — O(n) database round-trips. Rewrite as a JOIN or a lateral join so the planner can execute it once.\n- **Missing LIMIT on exploratory queries run against production.** A full-table scan without a LIMIT clause can lock up a production database for minutes and saturate I\u002FO. Add `LIMIT 100` to any ad hoc query until you know the cardinality, and run exploratory queries against a read replica.\n- **Implicit type coercions in join conditions.** Joining `orders.user_id` (INTEGER) to `users.id` (BIGINT) or `VARCHAR` forces the planner to cast one side for every comparison, often disabling index use. Match column types on both sides of every join; fix mismatches in the schema rather than relying on implicit casting.\n- **OFFSET pagination for deep pages.** `LIMIT 20 OFFSET 10000` forces the database to scan and discard 10,000 rows to return 20. For large offsets, performance degrades linearly. Use keyset (cursor) pagination — `WHERE id > last_seen_id ORDER BY id LIMIT 20` — which uses the index and runs in constant time regardless of page depth.\n\n## Full reference\n\n### Pagination: keyset over offset\n\nOffset pagination is simple but degrades on deep pages because the\ndatabase must read and discard the skipped rows. Keyset (cursor)\npagination is constant-time at any depth:\n\n```sql\n-- First page\nSELECT id, name, created_at\nFROM products\nORDER BY created_at DESC, id DESC\nLIMIT 20;\n\n-- Next page (using last row's values as cursor)\nSELECT id, name, created_at\nFROM products\nWHERE (created_at, id) \u003C ('2025-06-15 10:30:00', 4502)\nORDER BY created_at DESC, id DESC\nLIMIT 20;\n```\n\nAdd a composite index on `(created_at DESC, id DESC)` to support\nthe sort.\n\n### Deduplication\n\nKeep the latest row per key with a window function:\n\n```sql\nWITH ranked AS (\n    SELECT *,\n        ROW_NUMBER() OVER (\n            PARTITION BY email ORDER BY updated_at DESC\n        ) AS rn\n    FROM contacts\n)\nSELECT * FROM ranked WHERE rn = 1;\n```\n\nOr delete duplicates in place:\n\n```sql\nDELETE FROM contacts c\nUSING (\n    SELECT email, MAX(updated_at) AS keep_date\n    FROM contacts GROUP BY email HAVING COUNT(*) > 1\n) dupes\nWHERE c.email = dupes.email AND c.updated_at \u003C dupes.keep_date;\n```\n\n### Pivot via conditional aggregation\n\n```sql\nSELECT\n    department,\n    COUNT(*) FILTER (WHERE status = 'active')   AS active_count,\n    COUNT(*) FILTER (WHERE status = 'inactive') AS inactive_count,\n    COUNT(*) FILTER (WHERE status = 'pending')  AS pending_count\nFROM employees\nGROUP BY department;\n```\n\nFor engines without `FILTER`, fall back to `SUM(CASE WHEN ... THEN\n1 ELSE 0 END)`.\n\n### Recursive CTEs\n\nTree traversal with a depth limit to prevent runaway recursion on\ncyclic data:\n\n```sql\nWITH RECURSIVE org_tree AS (\n    SELECT id, name, manager_id, 1 AS depth, ARRAY[name] AS path\n    FROM employees\n    WHERE manager_id IS NULL\n\n    UNION ALL\n\n    SELECT e.id, e.name, e.manager_id, t.depth + 1, t.path || e.name\n    FROM employees e\n    JOIN org_tree t ON e.manager_id = t.id\n    WHERE t.depth \u003C 10\n)\nSELECT id, name, depth, array_to_string(path, ' > ') AS chain\nFROM org_tree\nORDER BY path;\n```\n\n### Upsert\n\n```sql\nINSERT INTO metrics (sensor_id, reading_date, value)\nVALUES ('temp-01', '2025-06-15', 23.5)\nON CONFLICT (sensor_id, reading_date)\nDO UPDATE SET value = EXCLUDED.value, updated_at = NOW();\n```\n\nMySQL uses `ON DUPLICATE KEY UPDATE`; SQLite uses similar\n`ON CONFLICT` syntax.\n\n### Anti-join (rows in A not in B)\n\nPrefer `NOT EXISTS` over `NOT IN` — `NOT IN` returns no rows if any\nNULL appears in the subquery result:\n\n```sql\nSELECT c.id, c.name\nFROM customers c\nWHERE NOT EXISTS (\n    SELECT 1 FROM orders o WHERE o.customer_id = c.id\n);\n```\n\n### Composite index column order\n\nThe leftmost-prefix rule determines which queries an index supports.\nGiven `(status, created_at, category_id)`:\n\n```sql\nWHERE status = 'active'                                      -- yes\nWHERE status = 'active' AND created_at > '2025-01-01'        -- yes\nWHERE status = 'active' AND created_at > '2025-01-01'\n  AND category_id = 5                                        -- yes\nWHERE created_at > '2025-01-01'                              -- no\nWHERE category_id = 5                                        -- no\n```\n\nPut equality columns first, then range columns, then sort columns.\n\n### Standard table boilerplate\n\n```sql\nCREATE TABLE example (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    -- domain columns here\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n```\n\nUse `TIMESTAMPTZ`, never `TIMESTAMP` without zone. Name constraints\nexplicitly (`chk_orders_positive_total`, `uq_users_email`) so error\nmessages are readable.\n\n### Transactions and concurrency\n\n- Use explicit transactions for multi-statement operations.\n- Pick the right isolation level: `READ COMMITTED` (default, fine\n  for most), `REPEATABLE READ` (consistent snapshots),\n  `SERIALIZABLE` (full isolation, retry on conflict).\n- Keep transactions short — never wait for user input inside one.\n- Use `SELECT ... FOR UPDATE` when reading rows you intend to\n  modify.\n- Handle deadlocks with retry logic, not longer timeouts.\n\n### Anti-patterns\n\n- **`SELECT *` in production** — fetches columns the caller does not\n  need and breaks when schemas evolve.\n- **Functions on indexed columns** — `WHERE YEAR(created_at) = 2025`\n  defeats the index; use a range instead.\n- **`NOT IN` with nullable subqueries** — use `NOT EXISTS`.\n- **Correlated subqueries that run per row** — rewrite as a join or\n  CTE.\n- **Missing `LIMIT` on exploratory queries** — easy way to OOM a\n  client.\n- **Implicit type conversions** — comparing a `BIGINT` column to a\n  string literal can prevent index use.\n- **Storing money as floats** — use `DECIMAL(19,4)` or integer\n  cents.\n- **Missing foreign keys \"for flexibility\"** — let the database\n  enforce integrity.\n","# SQL Patterns\n\n## Intro\n\nSQL rewards thinking in sets and reading execution plans. Build\nqueries incrementally from the core logic outward, lean on CTEs and\nwindow functions for clarity, and verify cost with `EXPLAIN` before\noptimizing anything.\n\n## Overview\n\n### Choose the right join\n\n- `INNER JOIN` — only matching rows in both tables (default, most\n  common).\n- `LEFT JOIN` — all rows from the left table, NULLs where right has\n  no match.\n- `RIGHT JOIN` — rare; rewrite as `LEFT JOIN` for clarity.\n- `FULL OUTER JOIN` — all rows from both, NULLs on the unmatched\n  side.\n- `CROSS JOIN` — cartesian product; use intentionally for generating\n  combinations.\n\n### CTEs over nested subqueries\n\nCTEs (`WITH` clauses) read top-to-bottom and let each step name its\nintent. Prefer them over nested subqueries when a query has more\nthan two levels.\n\n```sql\nWITH active_users AS (\n    SELECT user_id, MAX(login_at) AS last_login\n    FROM logins\n    WHERE login_at > CURRENT_DATE - INTERVAL '30 days'\n    GROUP BY user_id\n),\nuser_orders AS (\n    SELECT user_id, COUNT(*) AS order_count, SUM(total) AS revenue\n    FROM orders\n    GROUP BY user_id\n)\nSELECT u.name, au.last_login, uo.order_count, uo.revenue\nFROM users u\nJOIN active_users au USING (user_id)\nLEFT JOIN user_orders uo USING (user_id);\n```\n\n### Window functions\n\nUse windows for rankings, running totals, and per-row comparisons\nwithout collapsing rows:\n\n```sql\nSELECT\n    department,\n    employee,\n    salary,\n    RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,\n    salary - LAG(salary) OVER (PARTITION BY department ORDER BY salary) AS diff_from_prev,\n    SUM(salary) OVER (PARTITION BY department) AS dept_total\nFROM employees;\n```\n\nRemember the evaluation order: `WHERE` filters rows, `GROUP BY`\ngroups them, `HAVING` filters groups, then window functions run\nafter `HAVING`.\n\n### Optimization workflow\n\n1. **Read the EXPLAIN plan.** Look for sequential scans on large\n   tables, nested loops on big result sets, sort operations without\n   indexes.\n2. **Check indexes.** Ensure `WHERE`, `JOIN`, and `ORDER BY` columns\n   are covered.\n3. **Reduce data early.** Filter in `WHERE` before joining; join on\n   indexed columns.\n4. **Rewrite anti-patterns.** Functions on indexed columns,\n   correlated subqueries, `NOT IN` with NULLs, implicit type casts —\n   all defeat indexes.\n\n### Indexing strategy\n\n- **B-tree** (default) — equality, range, `ORDER BY`, `LIKE\n  'prefix%'`.\n- **Hash** — equality only, smaller than B-tree (Postgres).\n- **GIN** — full-text search, JSONB containment, array operations.\n- **GiST** — geometric data, range types, nearest-neighbor.\n- **Composite** — column order matters (leftmost prefix rule).\n- **Partial** — `WHERE is_active = true` for selective predicates.\n- **Covering** — `INCLUDE` columns to enable index-only scans.\n\nSee `references\u002Fschema-design.md` for full schema-design patterns\nand `references\u002Fquery-patterns.md` for query recipes.\n\n## Gotchas\n\nAgent-specific failure modes — provider-neutral pause-and-self-check items:\n\n- **SELECT * in production queries.** Selecting all columns returns data the application doesn't use, breaks when columns are added or removed, and prevents index-only scans. Always name the columns you need explicitly so the query is stable, the network payload is minimal, and the planner can use covering indexes.\n- **Functions on indexed columns in WHERE clauses.** Writing `WHERE LOWER(email) = ?` or `WHERE DATE(created_at) = ?` forces the planner to evaluate the function on every row, making the index on `email` or `created_at` unusable. Use a functional index, normalize the data at write time, or rewrite the predicate to operate on the raw column.\n- **NOT IN with a subquery that can return NULLs.** `WHERE id NOT IN (SELECT user_id FROM orders)` returns zero rows if any `user_id` in `orders` is NULL, because `x NOT IN (..., NULL, ...)` evaluates to NULL for every x. Use `NOT EXISTS` or filter out NULLs in the subquery explicitly.\n- **Correlated subqueries that execute once per row.** A subquery in the SELECT list or WHERE clause that references the outer query's row runs once for every row in the outer result set — O(n) database round-trips. Rewrite as a JOIN or a lateral join so the planner can execute it once.\n- **Missing LIMIT on exploratory queries run against production.** A full-table scan without a LIMIT clause can lock up a production database for minutes and saturate I\u002FO. Add `LIMIT 100` to any ad hoc query until you know the cardinality, and run exploratory queries against a read replica.\n- **Implicit type coercions in join conditions.** Joining `orders.user_id` (INTEGER) to `users.id` (BIGINT) or `VARCHAR` forces the planner to cast one side for every comparison, often disabling index use. Match column types on both sides of every join; fix mismatches in the schema rather than relying on implicit casting.\n- **OFFSET pagination for deep pages.** `LIMIT 20 OFFSET 10000` forces the database to scan and discard 10,000 rows to return 20. For large offsets, performance degrades linearly. Use keyset (cursor) pagination — `WHERE id > last_seen_id ORDER BY id LIMIT 20` — which uses the index and runs in constant time regardless of page depth.\n\n## Full reference\n\n### Pagination: keyset over offset\n\nOffset pagination is simple but degrades on deep pages because the\ndatabase must read and discard the skipped rows. Keyset (cursor)\npagination is constant-time at any depth:\n\n```sql\n-- First page\nSELECT id, name, created_at\nFROM products\nORDER BY created_at DESC, id DESC\nLIMIT 20;\n\n-- Next page (using last row's values as cursor)\nSELECT id, name, created_at\nFROM products\nWHERE (created_at, id) \u003C ('2025-06-15 10:30:00', 4502)\nORDER BY created_at DESC, id DESC\nLIMIT 20;\n```\n\nAdd a composite index on `(created_at DESC, id DESC)` to support\nthe sort.\n\n### Deduplication\n\nKeep the latest row per key with a window function:\n\n```sql\nWITH ranked AS (\n    SELECT *,\n        ROW_NUMBER() OVER (\n            PARTITION BY email ORDER BY updated_at DESC\n        ) AS rn\n    FROM contacts\n)\nSELECT * FROM ranked WHERE rn = 1;\n```\n\nOr delete duplicates in place:\n\n```sql\nDELETE FROM contacts c\nUSING (\n    SELECT email, MAX(updated_at) AS keep_date\n    FROM contacts GROUP BY email HAVING COUNT(*) > 1\n) dupes\nWHERE c.email = dupes.email AND c.updated_at \u003C dupes.keep_date;\n```\n\n### Pivot via conditional aggregation\n\n```sql\nSELECT\n    department,\n    COUNT(*) FILTER (WHERE status = 'active')   AS active_count,\n    COUNT(*) FILTER (WHERE status = 'inactive') AS inactive_count,\n    COUNT(*) FILTER (WHERE status = 'pending')  AS pending_count\nFROM employees\nGROUP BY department;\n```\n\nFor engines without `FILTER`, fall back to `SUM(CASE WHEN ... THEN\n1 ELSE 0 END)`.\n\n### Recursive CTEs\n\nTree traversal with a depth limit to prevent runaway recursion on\ncyclic data:\n\n```sql\nWITH RECURSIVE org_tree AS (\n    SELECT id, name, manager_id, 1 AS depth, ARRAY[name] AS path\n    FROM employees\n    WHERE manager_id IS NULL\n\n    UNION ALL\n\n    SELECT e.id, e.name, e.manager_id, t.depth + 1, t.path || e.name\n    FROM employees e\n    JOIN org_tree t ON e.manager_id = t.id\n    WHERE t.depth \u003C 10\n)\nSELECT id, name, depth, array_to_string(path, ' > ') AS chain\nFROM org_tree\nORDER BY path;\n```\n\n### Upsert\n\n```sql\nINSERT INTO metrics (sensor_id, reading_date, value)\nVALUES ('temp-01', '2025-06-15', 23.5)\nON CONFLICT (sensor_id, reading_date)\nDO UPDATE SET value = EXCLUDED.value, updated_at = NOW();\n```\n\nMySQL uses `ON DUPLICATE KEY UPDATE`; SQLite uses similar\n`ON CONFLICT` syntax.\n\n### Anti-join (rows in A not in B)\n\nPrefer `NOT EXISTS` over `NOT IN` — `NOT IN` returns no rows if any\nNULL appears in the subquery result:\n\n```sql\nSELECT c.id, c.name\nFROM customers c\nWHERE NOT EXISTS (\n    SELECT 1 FROM orders o WHERE o.customer_id = c.id\n);\n```\n\n### Composite index column order\n\nThe leftmost-prefix rule determines which queries an index supports.\nGiven `(status, created_at, category_id)`:\n\n```sql\nWHERE status = 'active'                                      -- yes\nWHERE status = 'active' AND created_at > '2025-01-01'        -- yes\nWHERE status = 'active' AND created_at > '2025-01-01'\n  AND category_id = 5                                        -- yes\nWHERE created_at > '2025-01-01'                              -- no\nWHERE category_id = 5                                        -- no\n```\n\nPut equality columns first, then range columns, then sort columns.\n\n### Standard table boilerplate\n\n```sql\nCREATE TABLE example (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    -- domain columns here\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n```\n\nUse `TIMESTAMPTZ`, never `TIMESTAMP` without zone. Name constraints\nexplicitly (`chk_orders_positive_total`, `uq_users_email`) so error\nmessages are readable.\n\n### Transactions and concurrency\n\n- Use explicit transactions for multi-statement operations.\n- Pick the right isolation level: `READ COMMITTED` (default, fine\n  for most), `REPEATABLE READ` (consistent snapshots),\n  `SERIALIZABLE` (full isolation, retry on conflict).\n- Keep transactions short — never wait for user input inside one.\n- Use `SELECT ... FOR UPDATE` when reading rows you intend to\n  modify.\n- Handle deadlocks with retry logic, not longer timeouts.\n\n### Anti-patterns\n\n- **`SELECT *` in production** — fetches columns the caller does not\n  need and breaks when schemas evolve.\n- **Functions on indexed columns** — `WHERE YEAR(created_at) = 2025`\n  defeats the index; use a range instead.\n- **`NOT IN` with nullable subqueries** — use `NOT EXISTS`.\n- **Correlated subqueries that run per row** — rewrite as a join or\n  CTE.\n- **Missing `LIMIT` on exploratory queries** — easy way to OOM a\n  client.\n- **Implicit type conversions** — comparing a `BIGINT` column to a\n  string literal can prevent index use.\n- **Storing money as floats** — use `DECIMAL(19,4)` or integer\n  cents.\n- **Missing foreign keys \"for flexibility\"** — let the database\n  enforce integrity.\n",{"name":5,"metadata":54,"description":61},{"processkit":55},{"id":56,"created":57,"version":58,"category":59,"apiVersion":60},"SKILL-sql-patterns",1775433600000,"1.1.0","engineering","processkit.projectious.work\u002Fv1","SQL query patterns, schema design, and optimization — joins, CTEs, window functions, indexing, and anti-patterns. Use when writing or optimizing SQL queries, designing or reviewing a relational schema, reading EXPLAIN plans, choosing indexes, or implementing pagination, deduplication, or analytical queries.\n","main","4876a92620ee792aac2ac9a1a806322d8a572367","archive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.13.0\u002Fcontext\u002Fskills\u002Fengineering\u002Fsql-patterns","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit","https:\u002F\u002Fprojectious-work.github.io\u002Fprocesskit\u002F","https:\u002F\u002Fgithub.com\u002Fprojectious-work","2026-11-11 23:24:18",{"source":8,"skillName":5,"commands":70,"prompt":80,"zipAvailable":81},[71,74,77],{"tool":72,"command":73},"npx","npx skills add projectious-work\u002Fprocesskit --skill sql-patterns",{"tool":75,"command":76},"bunx","bunx skills add projectious-work\u002Fprocesskit --skill sql-patterns",{"tool":78,"command":79},"pnpm","pnpm dlx skills add projectious-work\u002Fprocesskit --skill sql-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.13.0\u002Fcontext\u002Fskills\u002Fengineering\u002Fsql-patterns\u002FSKILL.md",true,[83,89],{"path":84,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":87,"sourceHtmlUrl":88},"references\u002Fquery-patterns.md","REFERENCE","参考文档",4542,"https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.13.0\u002Fcontext\u002Fskills\u002Fengineering\u002Fsql-patterns\u002Freferences\u002Fquery-patterns.md",{"path":90,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":91,"sourceHtmlUrl":92},"references\u002Fschema-design.md",5557,"https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Farchive\u002Fprocesskit-install-20261101\u002Fcontext\u002Ftemplates\u002Fprocesskit\u002Fv0.13.0\u002Fcontext\u002Fskills\u002Fengineering\u002Fsql-patterns\u002Freferences\u002Fschema-design.md",[94,119,139,159,176,194],{"skillNo":95,"name":96,"nameZh":6,"description":97,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":103,"publishedAt":49},"01M6X5MTC89B4ZGXW3JVSQCPF8","skill-management","Manage Skill package manifests as v1 ontology entities.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fskill-management\u002FSKILL.md",[100],{"categoryNo":101,"categoryName":102,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[104,105,106,107,108,109,45,110,111,112,96,113,114,115,116,117,118],"management","skill management","create","update","package","alpha","list","manage","ontology","get","as","manifests","v1","skill package","transition",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":126,"publishedAt":49},"01M6X5MCV74NHW5MEDNHZRF2C8","schema-management","Manage generated processkit schema contracts through deterministic MCP rebuild and inspection workflows.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fschema-management\u002FSKILL.md",[125],{"categoryNo":101,"categoryName":102,"taxonomyType":21,"taxonomyTypeDesc":22},[104,127,128,129,130,131,109,45,132,133,111,134,135,136,113,137,138],"schema","schema management","MCP","workflows","validation","generated","rebuild","contracts","wed","deterministic","contract","mode",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":145,"publishedAt":49},"01M6X5M5G6YJ96AMZGSCZJ1D13","proposition-management","Record claims, risks, beliefs, world facts, WSJF estimates, and assumptions through the Proposition interface.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fproposition-management\u002FSKILL.md",[],[104,146,147,106,107,148,109,45,149,150,151,40,113,152,153,154,155,156,157,158],"proposition","proposition management","index","log","world","record","creation","event","interface","updates","facts","claims","WSJF",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":165,"publishedAt":49},"01M6X5KMQ83QN9M6MPWZM7344F","ontology-management","Manage generated v1 ontology entities that do not require a dedicated domain server.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fontology-management\u002FSKILL.md",[],[104,112,166,106,109,167,168,132,169,149,111,170,171,113,172,173,153,174,116,175],"ontology management","lifecycle","archive","roadmap","do","domain","iteration","keep","measurement","generic",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":182,"publishedAt":49},"01M6X5KCY2DN3SM21RERSFNEBT","okf-compatibility","Import, export, and validate conformant Open Knowledge Format bundles.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fokf-compatibility\u002FSKILL.md",[],[183,184,185,186,109,187,188,189,45,190,191,192,193],"okf","compatibility","okf compatibility","validate","open","knowledge","format","export","bundles","bundle","import",{"skillNo":195,"name":196,"nameZh":6,"description":197,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":198,"categories":199,"keywords":200,"publishedAt":49},"01M6X5JA8S5PNGJVFW4EQY8T83","capability-management","Manage versioned Capability entities and their governed lifecycle.","https:\u002F\u002Fgithub.com\u002Fprojectious-work\u002Fprocesskit\u002Fblob\u002Fmain\u002Fsrc\u002Fcontext\u002Fskills\u002Fprocesskit\u002Fcapability-management\u002FSKILL.md",[],[104,201,202,106,107,148,109,167,45,110,149,203,111,151,113,173,153,154,204,118],"capability","capability management","queries","changes"]