sql-query-correctness
Skill0 starsChanged 3 months agoApache-2.0View on GitHub
Reviews SQL and ORM queries for silent wrongness: NULL semantics, join multiplication, filters that cancel outer joins, aggregation without grouping guarantees, window frames, timezone and date boundaries, and non-deterministic pagination. Use when a query returns zero rows, too many rows, or numbers nobody can reproduce.
In this repository46 other files it gives its agents.
- AGENTS.mdAGENTS.md
- academixSkill
- agent-browserSkill
- ai-engineering-platformSkill
- apocalypseSkill
- aria-apg-reviewSkill
- context-budgetSkill
- crawler-readiness-auditSkill
- data-protection-gateSkill
- decision-criticality-gateSkill
- fact-checkerSkill
- find-skillsSkill
- focus-friendly-outputSkill
- frontend-designSkill
- grill-meSkill
- grill-with-docsSkill
- humanizerSkill
- improve-codebase-architectureSkill
- microsoft-foundrySkill
- output-templatesSkill
- pre-launch-security-gateSkill
- prompt-preflightSkill
- quick-recapSkill
- rag-retrieval-qualitySkill
- rest-api-best-practicesSkill
- rigorous-responseSkill
- security-checklistSkill
- shepherdSkill
- skill-optimizerSkill
- system-design-scaleSkill
- tddSkill
- ui-ux-color-contrastSkill
- ui-ux-component-qualitySkill
- ui-ux-design-systemsSkill
- ui-ux-dropdownsSkill
- ui-ux-form-validationSkill
- ui-ux-generationSkill
- ui-ux-loading-statesSkill
- ui-ux-mobile-cssSkill
- ui-ux-typographySkill
- ux-logic-loopSkill
- ux-pattern-reviewSkill
- vercel-react-best-practicesSkill
- visual-flow-storyboardSkill
- visual-pr-reviewSkill
- web-design-guidelinesSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 98
Verify, for every join:
- Very specific: line 118
Every non-aggregated selected column is in GROUP BY, or is functionally dependent on it.
- Very specific: line 131
Every ORDER BY inside a window that uses a frame states the frame explicitly.
- Very specific: line 157
Every ORDER BY used for pagination is total: it ends in a unique tiebreaker.
- Very specific: line 160
No row order is assumed without ORDER BY, including from a CTE, a subquery, or a view.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SQL Query Correctness
Purpose
Catch queries that run without error and return the wrong answer. These are more dangerous than queries that fail, because nothing reports them.
Core Principle
A query is wrong until its row count, its NULL behavior, and its grouping are explained.
Correct output on today's data is not evidence. Construct the row that breaks it.Activation Triggers
Apply this skill when:
- A query returns zero rows, unexpected duplicates, or a total that does not reconcile
- A
LEFT JOINbehaves like an inner join - A report, dashboard, or export disagrees with another source
- An ORM query is being written for anything beyond a primary-key lookup
- Pagination skips or repeats rows
- Dates, "today", or per-user timezones are involved
Review Checklist
1. NULL Semantics
Verify:
- Comparisons use
IS NULL/IS NOT NULL, never= NULLor<> NULL. NOT IN (subquery)is not used where the subquery can produceNULL. A singleNULLmakes the whole predicate return no rows. PreferNOT EXISTS.<>on a nullable column silently excludesNULLrows. Decide whether that is intended and make it explicit withIS DISTINCT FROMor an explicitOR col IS NULL.- Aggregates ignore
NULL:COUNT(col)andAVG(col)skip them,COUNT(*)does not. - Arithmetic and string concatenation with
NULLyieldNULL, poisoning derived columns. NULLsorts differently per engine. StateNULLS FIRST/NULLS LASTwhen order matters.- Unique constraints usually permit multiple
NULLs. Do not rely on them for absence.
2. Join Cardinality
Verify, for every join:
- The expected relationship: one-to-one, one-to-many, or many-to-many.
- Whether the join can multiply rows. If it can, any downstream
SUM,COUNT, orAVGis inflated. Aggregate in a subquery or lateral join before joining. - Whether a filter on the right table sits in
WHEREinstead ofON. In an outer join, aWHEREpredicate on the right table discards the unmatched rows and turns it into an inner join. Put the predicate inON, or allowIS NULL. - Whether
DISTINCTis masking a cardinality bug.DISTINCTadded to fix duplicates is a symptom, not a fix, and it also hides legitimate duplicates. - Whether any join key is nullable, differently typed, or differently collated. Implicit casts silently drop matches and disable indexes.
Diagnostic: run the query without aggregation and count rows per key. If a key appears more times than expected, the aggregation is already wrong.
3. Aggregation and Grouping
Verify:
- Every non-aggregated selected column is in
GROUP BY, or is functionally dependent on it. - Filters on aggregates are in
HAVING, filters on rows are inWHERE. Moving one to the other changes the result. COUNT(*),COUNT(col), andCOUNT(DISTINCT col)are distinguished deliberately.- Empty groups: an aggregate over no rows returns
NULL, not0. Wrap withCOALESCEwhere a zero is intended. - Percentages and ratios guard against division by zero.
- Averages of averages are not computed. Aggregate from the base rows.
4. Window Functions
Verify:
- Every
ORDER BYinside a window that uses a frame states the frame explicitly. The default frame withORDER BYisRANGE ... CURRENT ROW, which groups ties together and surprises most authors. ROW_NUMBER,RANK, andDENSE_RANKare chosen deliberately for tie behavior.- The
PARTITION BYkey matches the intended reset boundary. - Window functions are not filtered in
WHERE— they are evaluated after it. Wrap in a subquery or useQUALIFYwhere supported.
5. Dates, Times, and Timezones
Verify:
- The column type: instant versus local date-time. Compare like with like.
- The stored timezone and the presentation timezone are both stated. "Today" is not a server-side constant when users span timezones.
- Range predicates use half-open intervals (
>= start AND < next_start), neverBETWEENover timestamps, which includes the endpoint and misses sub-second values. - Functions are not applied to the indexed column in a predicate.
WHERE date(ts) = xdisables the index; use a range instead. - Daylight-saving transitions and month-end arithmetic are handled by interval arithmetic, not by adding fixed second counts.
6. Determinism and Pagination
Verify:
- Every
ORDER BYused for pagination is total: it ends in a unique tiebreaker. LIMIT/OFFSETpagination over changing data skips and repeats rows. Use keyset pagination for anything a user scrolls.- No row order is assumed without
ORDER BY, including from a CTE, a subquery, or a view. - Set operations are chosen deliberately:
UNIONdeduplicates and sorts,UNION ALLdoes not.
7. ORM-Specific Traps
Verify:
- The generated SQL was inspected, not assumed.
- Lazy relations do not create an N+1 pattern on any collection path.
- Eager joins on multiple collections do not produce a cartesian expansion, and are not
combined with
LIMIT, which then limits joined rows rather than entities. - Soft-delete, tenant, and default scopes are applied — and are still applied inside raw fragments, subqueries, and aggregates.
- Raw fragments are parameterized. Never interpolate values or identifiers.
Required Tests
For each finding, construct the rows that expose it:
- A row with
NULLin every nullable column touched by the query - A parent with zero children, one child, and two children
- Two rows tied on the sort key
- A boundary timestamp: exact start, exact end, and one microsecond before the end
- A second tenant or user, to prove scoping holds
- An empty result set, to prove aggregates and ratios behave
A fix without a failing-then-passing test is unverified.
Anti-Patterns
- Adding
DISTINCTto remove duplicates without explaining them BETWEENon timestampsNOT INagainst a nullable subquery- Casting or wrapping an indexed column inside a predicate
- Trusting that the current data proves correctness
- Editing data to make a query return the expected result
Required Final Output
## Query Review
Status: CORRECT | WRONG | UNVERIFIED
### Intent
One sentence: what the query must return.
### Findings
- Issue: ...
Evidence: row or plan that shows it
Impact: wrong count / missing rows / inflated sum
Fix: ...
### Corrected query
```sql
...Tests
- ...
Remaining uncertainty
- ...
## Shared output profile
Compose the `review` profile from [output-templates](../output-templates/references/components.md).
Lead with the verdict and the intent, then findings with row-level evidence, the corrected query, and tests.
Preserve stricter domain rules above, omit empty components, and never fill a component with invented evidence.Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

