Skip to content
Editorial standards

How We Verify Our Content

Reference ยท Updated October 8, 2026

A tutorial that teaches something wrong is worse than no tutorial. Most SQL content on the web is checked by eye, if at all. This site holds itself to a mechanical standard instead: if a claim can be tested by a script, it is, and the tests run before anything goes live. This page explains exactly what “verified” means here, so you can decide how much to trust it.

What the verified badge means

Every tutorial carries a line reading Every example verified. It is not decoration. It means each runnable SQL block on that page was executed against the same sample database you use in the playground, by an automated script, and produced a result without error. Where a block shows the rows it returns, those rows were written out by the script from that run — nobody typed them — and the checks fail if they ever stop matching what the query produces, or if a second, independent copy of SQLite returns different rows. If a block cannot run on the in-browser engine (for example vendor-specific syntax on the dialect differences page), it is labelled not in SQLite instead of being passed off as runnable.

Every example is executed

The tutorials contain more than a hundred SQL examples. Before a page is published or changed, a script extracts every code block marked as runnable and sends each one through the same worker the playground uses — the same sample data, the same statement splitting, the same engine — starting from a fresh database for every block, exactly as a Run click does. Any error stops the change from going out. The Run button comes from that mark rather than from the script: a block gets one only when it is marked runnable, and every block marked runnable is one the script has executed. 245 blocks also publish the rows they return — across the tutorials and the glossary, whose examples go through the same script; those tables are generated by a script, never typed, and compared against a fresh run every time the checks run.

A second engine checks the first

Comparing a published table with a fresh run proves the page matches the engine. It cannot prove the engine is right, and for a while on this site it was not. Until September 2026 every query ran on AlaSQL, a SQL engine written in JavaScript, and some of its answers were wrong without any error: MAX() of a date column came back as NULL, ROW_NUMBER() ignored the ORDER BY inside OVER(), a windowed SUM() never produced a running total, HAVING on a column alias returned no rows, and NOT IN with a NULL in its list still returned rows where the SQL standard says it returns none. Every check passed anyway, because each published table had been produced by the same engine it was compared with. An audit of the examples found the problem in September 2026; two published result tables were wrong because of it.

The site now runs SQLite 3.49.1, compiled to WebAssembly by sql.js 1.14.2 and served from this site, and a new check exists so that a wrong engine cannot pass unnoticed again. npm run verify:oracle runs every runnable example and every exercise’s reference solution twice — once through the site’s own worker, and once in the separate copy of SQLite that comes built into Python, loaded with the same sample data — and compares the rows. The two are separate builds, reached through separate code, so a fault in how the site’s worker splits statements, converts values or loads the data shows up as a disagreement, and a disagreement fails the check. A query the second copy cannot run is listed by name rather than quietly skipped.

A handful of engine behaviours that the tutorials rely on are also pinned by value in npm run verify:grader: that NOT IN with a NULL in its list returns no rows, as the standard says; that the latest date is what MAX() returns; that ROW_NUMBER() follows its own ORDER BY; that 7 / 2 is 3. If an upgrade ever changes one of them, the wording that depends on it would become untrue, so the check fails first.

Every exercise solution is checked end to end

All 42 practice exercises ship with a reference solution. Two checks apply to each one:

  1. The reference solution is executed against a freshly built database and must return rows.
  2. A browser-automation suite opens the real practice page, types the reference solution into the editor, presses Check answer and confirms the checker accepts it — the exact path a visitor takes.

That suite, like the accessibility checks further down, runs against a local server that sends the same response headers as the live site, including its Content-Security-Policy. An ordinary local test server sends none, so a page could pass on a laptop and still be blocked in production — the SQL engine most of all, since WebAssembly only starts if the policy allows it. scripts/verify/serve-like-vercel.py reads the headers from the site’s hosting configuration and applies them, so the browser checks see what visitors’ browsers see.

Answer checking itself never compares your SQL as text. It runs your query and the reference query against the same data and compares the two result sets, so any query producing the correct rows is accepted. When you are wrong, it shows the expected rows next to yours so the difference is visible rather than described.

Comparing results has two consequences worth naming. Where a task asks for a column by name — a column named headcount — the name is part of the answer, so 27 tasks check it and tell you which name they expected; everywhere else you are free to alias however you like. And because the checker only ever sees the rows your query returns, it would otherwise accept those rows typed out as literals. So it runs both queries a second time against an empty copy of the database: a query that reads the tables answers differently, a query that invented its values answers exactly the same, and the second kind is refused. Both behaviours are tested by a published script.

We test that a wrong answer is actually rejected

Checking by result has one weakness, and we would rather name it than hide it: on a small sample database a wrong query can return the right rows by coincidence. So a script takes all 42 reference solutions, mechanically breaks each one — removes the WHERE clause, flips the sort, widens a comparison, turns a LEFT JOIN into an INNER JOIN, drops DISTINCT or HAVING — and reports any broken version the checker still accepts.

Running it changed the sample data. Every department used to have employees, which meant the exercise about “departments with nobody” could not tell LEFT JOIN from INNER JOIN. All eight customers were in eight different countries, so DISTINCT did nothing. No product sat on a price boundary, so more than 400 and 400 or more were the same query. Those are fixed: one department is empty, two customers share a country, one product costs exactly 400, and one order total sits exactly on the CTE exercise’s threshold.

We strengthened the salary task to compare each employee with their own department average, giving the data a strict boundary, and changed the monthly report to request descending order so a missing ORDER BY is visible. One limitation remains: no customer’s combined spend is exactly 10,000, so >= instead of > returns the same rows for that task. The script records this one known blind spot as its baseline and fails if the number grows.

Every program in every language is executed

The lessons, exercises, interview questions and challenges for the languages other than SQL are written as structured content files, and a single script runs every program in them before anything is published: each lesson example, each lesson’s mini exercise, each exercise’s reference solution against every one of its tests, each interview snippet, and each challenge solution in every language it is provided in. A program’s output must match the output the page shows, character for character apart from trailing whitespace. The same script also checks that an exercise’s starter code does not already pass the tests, and that a debugging question’s buggy code does not already produce the right answer.

The toolchains are the real ones, named on every page: CPython for Python, Node.js for JavaScript, and the TypeScript compiler for TypeScript. The verified badge on a lesson or exercise page means that script ran clean for that language.

Where a language runs in your browser — Python on Pyodide, and JavaScript and TypeScript in an isolated Web Worker — a second, browser-automation check runs known programs through the same runner you use, under the site’s own Content-Security-Policy, and checks their output, their error messages, that a never-ending loop is stopped, that common JavaScript network APIs are disabled, and that the exercise page marks a correct answer solved and a wrong one not. The security policy blocks requests to external origins; this is not a claim that every runtime API is unavailable, since Python can make requests to this site’s own origin. JavaScript and TypeScript output is formatted by the same code in the browser and in the build-time check, so the two cannot disagree about what a value looks like when printed.

Structured data must match the page

Search engines are told about this site through schema.org markup: article headlines, FAQ questions and answers, how-to steps, glossary definitions. A script compares every one of those strings, character by character, with the text actually rendered on the page. If a FAQ answer in the markup does not appear on the page, the check fails. Dates in the markup are generated from the same source as the visible Updated line. Nothing is told to a search engine that is not shown to you.

Accessibility and layout are tested, not assumed

Every original SQL and site page is checked with axe-core against WCAG 2.1 AA in both light and dark mode; representative generated page types are checked the same way. Those pages are rendered at phone, tablet and desktop widths to catch horizontal overflow, while a separate production crawl checks every indexed URL at phone width. Layout shift is measured on key pages so that content does not jump as it loads. The local checks run under the live site’s security headers before a change goes out.

Counts are checked against the code

When a page says “42 exercises” or “four paths”, a script confirms that number against the exercise bank itself. If the bank grows and a page still quotes the old figure, the check fails until the text is corrected.

What we will never do

  • No invented social proof. There are no testimonials, star ratings, “trusted by” logos or user counts on this site, because we could not verify them. Every number shown is a count of something that exists in the code.
  • No copied content. Every tutorial, exercise, prompt and explanation is written for this site against its own fictional sample database.
  • Local execution and disclosed measurement. The site runs your code locally and keeps learning progress in your browser. Vercel, our hosting provider, processes cookieless page-view and performance measurements through this domain. The privacy policy explains that processing, browser storage and what happens if you email us; it is checked against the code and observed requests.
  • No pretending an example works when it does not. Dialect differences are stated; unsupported syntax is labelled.

You can run these checks yourself

Claiming that a script checks something is worth very little if nobody can see the script. Every check described on this page is published in the site’s repository under scripts/verify/, with an npm command each:

  • npm run verify:sql — executes every runnable example in the tutorials
  • npm run verify:oracle — runs every example and exercise solution again in a second copy of SQLite and compares the rows
  • npm run verify:exercises — solves all 42 exercises through the real page
  • npm run test:exercises — checks that a wrong answer is actually rejected
  • npm run verify:grader — required column names are enforced, invented answers are refused, and the pinned engine behaviours still hold
  • npm run verify:claims — legal pages, no fabricated proof, schema against the rendered text
  • npm run verify:pages — head tags, internal links, sitemap, published counts
  • npm run verify:a11y — axe-core WCAG 2.1 AA in both themes, overflow, console errors, layout shift
  • npm run verify:code — runs every lesson example, exercise solution, interview snippet and challenge solution for every language with its real toolchain and compares the output
  • npm run verify:runners — known programs through the in-browser Python, JavaScript and TypeScript runners under the production Content-Security-Policy
  • npm run verify:platform — the lesson, exercise, challenge, playground and interview pages driven in a browser, plus overflow and accessibility on the new page types
  • npm run build:check — every generated page and site file is current with its sources

If one of them disagrees with something written here, the page is wrong and we would like to know.

Corrections

None of this makes the site infallible — it makes it correctable. If you find a mistake, a misleading explanation or an example that behaves differently on your database, tell us. Corrections are made in the page itself, the Updated date changes, and the checks above run again before the fix goes live.

Who writes this

SQL Practice is an independent project, built and maintained in India for learners everywhere. Content is written and reviewed by the site’s maintainer with the verification tooling described above; there is no editorial board, no sponsor and no advertiser influence over what is taught. Read more on the About page.

About the author

SQL Practice

SQL Practice publishes free SQL tutorials, an in-browser playground and 42 practice exercises, built so you can run what you read. Every runnable example in our tutorials is executed against the site’s sample database before it is published. Read how we keep tutorials accurate, or report a mistake.