Recommended Free Tools
This SQL cheat sheet covers the query patterns developers use most: selecting and filtering rows, joining tables, grouping results, working with CTEs and window functions, and adapting syntax across PostgreSQL, MySQL, SQLite, and SQL Server. SQL has a shared relational foundation, but date functions, pagination, quoting, and other details vary by engine. Use the portable patterns first, then check the dialect notes before running version-specific syntax.
Start with the SELECT query skeleton
A SELECT query chooses a data source, optionally filters and groups its rows, and returns a result set. This skeleton shows the usual clause order; square-bracketed portions are optional placeholders, not literal SQL.
SELECT [DISTINCT] column_or_expression AS alias
FROM table_or_view AS t
[JOIN other_table AS o ON o.key = t.key]
[WHERE row_condition]
[GROUP BY grouping_columns]
[HAVING group_condition]
[ORDER BY sort_expression [ASC | DESC]]
[LIMIT count OFFSET skip];
Replace the placeholders with actual expressions and table names. For example:
SELECT id, email
FROM customers
WHERE status = 'active'
ORDER BY created_at DESC;
Use an explicit column list rather than SELECT * in application queries when you know which fields you need. It makes the result shape clearer and avoids returning unrelated columns if a table changes.
#1 Best Overall
Clause order and logical processing
Write clauses in this order: SELECT, FROM and joins, WHERE, GROUP BY, HAVING, ORDER BY, then pagination. A useful teaching model for how a simple query is logically processed is:
FROM/JOIN → WHERE → GROUP BY/HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET
This is a way to reason about the result, not a promise about the database’s physical execution plan. SQLite’s documented account of simple SELECT processing describes the source rows, filtering, grouping and result-column processing, followed by DISTINCT handling.
Filter rows, handle NULL, and write conditions safely
WHERE keeps or removes individual input rows before grouping. Combine conditions with AND, OR, and NOT; use parentheses when mixing AND and OR so the intended logic is unmistakable.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →SELECT order_id, customer_id, amount
FROM orders
WHERE status = 'paid'
AND (amount >= 100 OR priority = 'high');
NULL is not an ordinary value
Test for missing values with IS NULL or IS NOT NULL, not = NULL. A comparison with NULL does not behave like a comparison with a concrete value.
Rank #2
SELECT id, email
FROM customers
WHERE email IS NULL;
Use COALESCE(value, fallback) to return the first non-NULL argument, and CASE to produce conditional values:
SELECT
order_id,
COALESCE(promo_code, 'no promotion') AS promotion,
CASE WHEN amount >= 500 THEN 'large' ELSE 'standard' END AS order_size
FROM orders;
Join tables without hiding duplicate matches
A join combines rows from tables according to a matching condition. Choose the join based on which unmatched rows should remain in the result.
| Join | What it keeps | Typical use |
|---|---|---|
INNER JOIN |
Only rows with a match on both sides | Orders that have a matching customer |
LEFT JOIN |
Every row from the left table, plus matching right-side data; unmatched right-side columns are NULL | All customers, including those with no orders |
RIGHT JOIN |
Every row from the right table, plus matching left-side data | Preserve the right-side table’s unmatched rows |
FULL OUTER JOIN |
Matched rows and unmatched rows from both sides | Compare two sets while retaining gaps on either side |
Example: return every customer and any matching order.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
SELECT c.customer_id, c.name, o.order_id, o.amount
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id;
More than one matching row on the right creates more than one output row for a left-side record. If a join unexpectedly multiplies results, inspect the relationship and key cardinality before adding DISTINCT. DISTINCT can conceal a many-to-many match without fixing it. Check the selected database engine and version before relying on RIGHT or FULL OUTER JOIN syntax; join support is not uniform across all four engines.
Group rows and filter aggregate results
GROUP BY turns input rows into groups so aggregate functions such as COUNT, SUM, AVG, MIN, and MAX can produce one result per group. WHERE filters rows before that grouping; HAVING filters groups after aggregation.
Rank #3
SELECT customer_id, COUNT(*) AS order_count, SUM(amount) AS revenue
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
HAVING SUM(amount) > 1000
ORDER BY revenue DESC;
This date-literal form is not a universal cross-dialect promise; see the date and time notes below. In the grouping itself, selected expressions generally need to be grouped or aggregated. PostgreSQL also documents an exception when a selected column is functionally dependent on grouped columns, so do not assume every engine applies identical grouping rules.
Common aggregate patterns
-- Count rows in each status
SELECT status, COUNT(*) AS rows_in_status
FROM orders
GROUP BY status;
-- Find the average order amount by customer
SELECT customer_id, AVG(amount) AS average_amount
FROM orders
GROUP BY customer_id;
Use COUNT(*) to count rows. COUNT(column) counts non-NULL values in that column, which can produce a different result.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsUse CTEs and set operators to organize results
A common table expression (CTE) gives a named query result a place in the statement. It can make a multi-stage query easier to read and reuse within that statement.
WITH recent_orders AS (
SELECT order_id, customer_id, order_date, amount
FROM orders
WHERE order_date >= :start_date
)
SELECT customer_id, COUNT(*) AS order_count
FROM recent_orders
GROUP BY customer_id;
:start_date is an illustrative parameter marker, not a universal placeholder syntax; use the parameter-binding convention of your database driver. Date arithmetic also differs by dialect, so parameterizing the boundary avoids baking one engine’s interval expression into an otherwise portable example.
Set operators combine the result sets of compatible SELECT statements. Their branches should return the same number of columns in corresponding positions, with compatible types.
Rank #4
UNIONcombines results and removes duplicate rows.UNION ALLcombines results without removing duplicates.INTERSECTreturns rows present in both results.EXCEPTreturns rows from the first result that are absent from the second.
SELECT email FROM current_customers
UNION
SELECT email FROM archived_customers;
Keep detail rows while calculating with window functions
GROUP BY reduces rows to one row per group. A window function calculates across related rows while retaining the detail rows in the result. Its central pattern is function(...) OVER (PARTITION BY ... ORDER BY ...). SQLite’s documentation defines a window function as a SQL function whose input values come from a window of one or more rows in a SELECT result set.
SELECT
customer_id,
order_date,
amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC
) AS newest_order_rank,
SUM(amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM orders;
The partition divides rows into customer-specific groups; ordering defines the sequence within each group. The explicit ROWS frame makes the running total’s intended row-by-row boundary clear. Window-frame syntax and supported options vary by engine, so check the target dialect when changing the frame.
Top row per group
Use ROW_NUMBER in a CTE or subquery, then filter the rank in an outer query. Window calculations are not generally filtered in the same query block’s WHERE clause.
WITH ranked_orders AS (
SELECT
customer_id,
order_id,
order_date,
amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC, order_id DESC
) AS rn
FROM orders
)
SELECT customer_id, order_id, order_date, amount
FROM ranked_orders
WHERE rn = 1;
The second ordering key makes the order deterministic when two records share a date, assuming order_id distinguishes them.
Compare PostgreSQL, MySQL, SQLite, and SQL Server syntax
The relational query model is shared, but syntax and feature availability are not. Treat an example as dialect-specific when it uses a vendor-specific function, pagination form, quoting rule, or version-gated clause.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Best Value
| Engine | What to check | Practical note |
|---|---|---|
| PostgreSQL | SELECT grammar, grouping rules, pagination, and NULL ordering | The PostgreSQL manual documents functional-dependency exceptions to grouping requirements and explicit NULLS FIRST/NULLS LAST ordering. |
| MySQL 8.4 | SELECT grammar and MySQL-specific modifiers, functions, and date syntax | Use the 8.4 documentation when targeting MySQL 8.4; do not assume a MySQL extension is portable SQL. |
| SQLite | SELECT and window grammar, join availability, and ALTER TABLE capabilities | Check the SQLite version before relying on features often assumed from server databases, including RIGHT/FULL JOIN support or broader ALTER TABLE behavior. |
| SQL Server | Pagination, date/string functions, and compatibility-level requirements | The named WINDOW clause is for SQL Server 2022 (16.x) and later and requires database compatibility level 160 or higher. |
Pagination
Pagination needs a deterministic order. Without ORDER BY, a database is not required to return rows in a stable sequence. The common LIMIT/OFFSET form is used by PostgreSQL, MySQL, and SQLite; SQL Server uses a different pagination form.
-- PostgreSQL, MySQL, SQLite
SELECT order_id, order_date
FROM orders
ORDER BY order_date DESC, order_id DESC
LIMIT 20 OFFSET 40;
When porting this query to SQL Server, use its ORDER BY ... OFFSET ... ROWS FETCH NEXT ... ROWS ONLY form. Large offsets can still require work to locate and skip earlier rows; if a workload needs deep pagination, compare keyset pagination using the last-seen ordering key with the application’s consistency requirements.
Dates, strings, and identifiers
Date/time functions and interval expressions differ among the engines. String concatenation also differs: PostgreSQL and SQLite support ||, while MySQL commonly uses CONCAT(...); SQL Server commonly uses CONCAT(...) or +, with NULL behavior depending on the operator and settings. Verify the exact form and semantics against the database version you deploy to.
Identifier quoting is another portability trap. PostgreSQL, SQLite, and SQL Server accept double-quoted identifiers in relevant modes; MySQL conventionally uses backticks, and its double-quote behavior depends on SQL mode. Prefer simple, unquoted names that do not collide with reserved words. Never use identifier quoting as a substitute for parameterizing values.
NULL ordering, upserts, and merge operations
PostgreSQL supports explicit NULLS FIRST and NULLS LAST in ordering. Do not assume the same syntax or default NULL position in another engine; express the intended order in a dialect-appropriate way and test it on the target database.
Upsert and merge syntax is especially vendor-specific. PostgreSQL and SQLite use an ON CONFLICT form; MySQL uses ON DUPLICATE KEY UPDATE; SQL Server provides MERGE, though the appropriate write pattern depends on the operation and concurrency requirements. These are not interchangeable text substitutions: verify conflict keys, uniqueness constraints, and update behavior before porting.
Common mistakes and troubleshooting
- A query returns too many rows: inspect each join’s match cardinality and confirm the join condition covers the intended key. Do not add
DISTINCTuntil you know why rows multiplied. - An aggregate query is rejected: ensure each selected non-aggregate expression is grouped, or aggregate it. Check whether your engine recognizes the functional dependency you rely on.
- A filter removes the wrong records: check whether it belongs in
WHERE(before grouping) orHAVING(after grouping), and parenthesize mixed AND/OR conditions. - A NULL check misses records: replace equality tests with
IS NULLorIS NOT NULL. - Pagination changes between runs: include an
ORDER BYwith a tie-breaking column so rows have a deterministic order. - A query works locally but fails in production: compare the actual engine, version, SQL mode, and SQL Server compatibility level; look first for date, pagination, quoting, join, and window syntax differences.
- An upsert overwrites unexpected data: verify the unique constraint or conflict target and inspect exactly which columns the update clause changes.
- A query is slow: reduce unnecessary selected columns and rows, inspect the execution plan in the target database, and check indexes on join and filter keys. The best plan depends on the engine, schema, and data distribution.
Or skip the browser setup
If a database-backed reporting app also needs screenshots of its web pages, ScreenshotNeo is a separate website screenshot API and MCP server; it does not run SQL. One GET request can return a screenshot or PDF. For example, save a screenshot of a page as WebP:
Quick Recap
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots. Sign up free for ScreenshotNeo.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

