SQL JOIN Cheatsheet - SQL JOIN Query Reference

For developers who join tables but still get the row counts wrong. The recurring trap is that a JOIN on a one-to-many parent duplicates parent rows, inflating results unless you dedupe. By the end you can pick INNER vs LEFT by whether un-matched rows should survive, understand the duplication pitfall on one-to-many joins, express candidate-missing conditions with LEFT JOIN ... WHERE ... IS NULL, and choose EXISTS for correlated subqueries.

Databases·41 commands·Last updated 2026-07-21
sqljoinDatabaseQuery

Typical Use Case

For engineers who write reports daily, chase numbers that do not match, or speed up an existing query. The difficulty is never the syntax. Filtering a right-hand condition in WHERE after a LEFT JOIN quietly turns the outer join into an inner one, and joining many tables with the wrong driving table multiplies rows and confuses the optimiser. Covers the practical forms of INNER, LEFT, RIGHT, FULL and CROSS, self-joins, joins against aggregates, EXPLAIN, indexes, FORCE INDEX, and EXISTS.

INNER JOIN 5

SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id
Return only matched rows from both tables
SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id
Short form JOIN (defaults to INNER)
SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id AND o.status = 'paid'
Multiple ON conditions jointly decide the match
SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id WHERE o.total > 100
Filter the whole result with WHERE after JOIN
SELECT u.name, o.total, p.amount FROM users u JOIN orders o ON u.id = o.user_id JOIN payments p ON o.id = p.order_id
Chained multi-table INNER JOIN

LEFT / RIGHT JOIN 6

SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id
All left rows; right NULL if no match
SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id
All right rows; left NULL if no match
SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.id IS NULL
Anti-join: left-only rows (users with no orders)
SELECT * FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'
ON condition in LEFT JOIN does not drop left rows
SELECT * FROM orders o RIGHT JOIN users u ON u.id = o.user_id WHERE o.id IS NULL
RIGHT JOIN anti-join (users with no orders)
SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id
LEFT JOIN + aggregate, count users incl. zero orders

FULL / CROSS JOIN 5

SELECT * FROM users FULL OUTER JOIN orders ON users.id = orders.user_id
All rows from both; NULL where unmatched
SELECT * FROM users u FULL JOIN orders o ON u.id = o.user_id WHERE u.id IS NULL OR o.user_id IS NULL
Symmetric difference (rows unique to each side)
SELECT * FROM table1 CROSS JOIN table2
Cartesian product
SELECT * FROM table1, table2
Implicit CROSS JOIN (comma)
SELECT * FROM sizes CROSS JOIN colors
All combinations (size × color)

Self & Multi-table 5

SELECT e.name, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id
Self-join (employee-manager)
SELECT a.name, b.name AS parent FROM categories a JOIN categories b ON a.parent_id = b.id
Self-join (category-parent)
SELECT * FROM a JOIN b ON a.id = b.a_id JOIN c ON b.id = c.b_id
Three-table join
SELECT t1.name AS grandchild, t3.name AS grandparent FROM tree t1 JOIN tree t2 ON t1.parent_id = t2.id JOIN tree t3 ON t2.parent_id = t3.id
Multi-level self-join (3 generations)
SELECT u.name, o.total, p.amount FROM users u JOIN orders o ON u.id = o.user_id JOIN payments p ON o.id = p.order_id
Three-table (user-order-payment)

JOIN & Aggregation 5

SELECT u.name, COUNT(o.id) AS order_count FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id
Order count per user
SELECT u.name, SUM(o.total) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING SUM(o.total) > 1000
HAVING filters aggregates (spent > 1000)
SELECT c.name, AVG(o.total) AS avg_order FROM customers c JOIN orders o ON c.id = o.customer_id GROUP BY c.id
Group average
SELECT u.name, COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(o.id) = 0
LEFT JOIN + HAVING for zero-order users
SELECT DATE(o.created_at) AS d, COUNT(*) FROM orders o JOIN users u ON u.id = o.user_id GROUP BY DATE(o.created_at)
Group by date

JOIN Performance 6

EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id
Show execution plan
EXPLAIN ANALYZE SELECT * FROM users u JOIN orders o ON u.id = o.user_id
Run & show timings (PostgreSQL / MySQL 8.0+)
CREATE INDEX idx_orders_user_id ON orders(user_id)
Index foreign key to speed up JOIN
SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id
Select only needed columns (avoid SELECT *)
SELECT * FROM users u STRAIGHT_JOIN orders o ON u.id = o.user_id
MySQL: force left table as driver
SELECT * FROM users u JOIN orders o FORCE INDEX(idx_user_id) ON u.id = o.user_id
MySQL: force a specific index

USING & Natural Join 4

SELECT * FROM users JOIN orders USING (user_id)
USING shorthand (same-named join column)
SELECT * FROM users u JOIN orders o USING (user_id, tenant_id)
USING multiple columns (merges same-named col)
SELECT * FROM users NATURAL JOIN orders
Natural join (auto-match all same-named cols; use with care)
SELECT * FROM users u JOIN orders o ON u.id = o.user_id AND u.tenant_id = o.tenant_id
Multi-column join (can use USING)

Subquery & EXISTS 5

SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)
Users who have orders
SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)
Users with no orders
SELECT * FROM orders o WHERE o.user_id IN (SELECT id FROM users WHERE status = 'active')
IN subquery filter
SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS cnt FROM users u
Correlated subquery (scalar)
SELECT * FROM orders WHERE user_id = (SELECT MAX(user_id) FROM orders)
Subquery returning a single value

Parameter matrix

ParameterEffectExample
INNER JOIN ... ONKeeps only rows that match on both sides; unmatched rows disappear entirely.SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id
JOIN <alias> ON ...JOIN is shorthand for INNER JOIN, and aliases keep column references readable once several tables are in play.SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id
LEFT JOIN ... ONKeeps every row of the left table and fills NULL on the right when nothing matches, making it the default choice whenever the left table drives the query.SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id
RIGHT JOIN ... ONKeeps every row of the right table. In practice most teams rewrite it as a LEFT JOIN for readability.SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id
... WHERE o.id IS NULLThe anti-join idiom: it finds left-table rows with no counterpart on the right, such as users without orders or products without a category.SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.id IS NULL
LEFT JOIN ... ON u.id = o.user_id AND o.status = 'paid'Moving the extra condition into ON is what stops the LEFT JOIN from silently degrading into an inner join.SELECT u.name, o.total FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'
FULL OUTER JOINKeeps rows from both sides. MySQL has no support for it, so the equivalent is a LEFT JOIN and RIGHT JOIN combined with UNION.SELECT * FROM users FULL OUTER JOIN orders ON users.id = orders.user_id
CROSS JOIN / comma joinA cartesian product whose row count is the product of both tables. Reserve it for small tables where every combination is genuinely valid.SELECT * FROM sizes CROSS JOIN colors
Self join e LEFT JOIN employees mJoins a table to itself, the standard way to express a hierarchy such as employee-to-manager or category-to-parent.SELECT e.name, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id
Chained multi-table JOINWith three or more tables each step needs its own ON clause, and a missing one turns the middle of the query into a cartesian product.SELECT u.name, o.total, p.amount FROM users u JOIN orders o ON u.id = o.user_id JOIN payments p ON o.id = p.order_id
COUNT(o.id) AS order_countCounting a specific column rather than the star is what makes a LEFT JOIN row with no match count as zero rather than one.SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id
GROUP BY ... HAVING SUM(o.total) > 1000HAVING filters aggregated results while WHERE filters rows before aggregation, and the two cannot be swapped.SELECT u.name, SUM(o.total) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING SUM(o.total) > 1000
SELECT DATE(o.created_at) AS d, COUNT(*)Grouping by the result of a function gives you time-series rollups; note that some engines require the function expression to appear in the GROUP BY as well.SELECT DATE(o.created_at) AS d, COUNT(*) FROM orders o JOIN users u ON u.id = o.user_id GROUP BY DATE(o.created_at)
EXPLAIN SELECT ...Reveals the driving table, the join type and which index was hit in the plan, and it is the first move on a slow joining query.EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id
EXPLAIN ANALYZE SELECT ...Actually runs the query and reports real row counts and timings per step, exposing stale statistics whenever estimated and actual rows differ by orders of magnitude.EXPLAIN ANALYZE SELECT * FROM users u JOIN orders o ON u.id = o.user_id
CREATE INDEX idx_orders_user_id ON orders(user_id)Indexes the join key. A join whose key carries no index is guaranteed to fall back to a full scan on a large table.CREATE INDEX idx_orders_user_id ON orders(user_id)
STRAIGHT_JOINForces the join to follow the written FROM order, which is a stopgap for when the optimiser picks the wrong driving table.SELECT * FROM users u STRAIGHT_JOIN orders o ON u.id = o.user_id
FORCE INDEX (idx_user_id)Pins a specific index for one table. More precise than STRAIGHT_JOIN, but it bakes the index choice into the query.SELECT * FROM users u JOIN orders o FORCE INDEX(idx_user_id) ON u.id = o.user_id
JOIN ... USING (user_id)Joins on identically named columns, which then appear only once in the result. A good fit for wide tables with consistent naming.SELECT * FROM users JOIN orders USING (user_id)
NATURAL JOINJoins implicitly on every shared column name, so renaming a column silently changes the join semantics. Strongly discouraged.SELECT * FROM users NATURAL JOIN orders
ON u.id = o.user_id AND u.tenant_id = o.tenant_idIn a multi-tenant schema the tenant column belongs in ON, otherwise the join leaks rows across tenants.SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id AND u.tenant_id = o.tenant_id
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)A correlated existence check that stops at the first hit, and unlike IN it behaves correctly with nullable columns.SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)
WHERE NOT EXISTS (...)Existence-based anti-join, which usually produces a cleaner plan than LEFT JOIN combined with IS NULL.SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)
user_id IN (SELECT id FROM users WHERE status = 'active')A semi-join formulation. Beware that NOT IN returns an empty set entirely once the subquery yields a single NULL.SELECT * FROM orders o WHERE o.user_id IN (SELECT id FROM users WHERE status = 'active')
SELECT (SELECT COUNT(*) ...) AS cntA scalar subquery in the projection runs once per row, which slows down noticeably on large inputs; replace it with a join plus GROUP BY.SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS cnt FROM users u
WHERE user_id = (SELECT MAX(user_id) FROM orders)The classic way to fetch a row holding an extreme value, more direct than sorting and taking the first row.SELECT * FROM orders WHERE user_id = (SELECT MAX(user_id) FROM orders)

Common pitfalls

SymptomUsers who should appear as having no orders vanish from the result entirely.

CauseA LEFT JOIN was written but the right-table condition landed in WHERE. After the NULL padding, WHERE orders.status = "paid" is false, so the join silently degrades into an inner join.

FixMove the right-table condition into the ON clause, or state INNER JOIN explicitly if only paid orders were wanted.

SymptomUsers with no orders are counted as having one.

CauseThe aggregate used COUNT(*) or COUNT(o.*), so the padded row of NULLs was counted as well.

FixCount a specific nullable column such as COUNT(o.id), which ignores NULL and returns zero when there is no match.

SymptomThe row count explodes and the multi-table result is orders of magnitude larger than expected.

CauseIn a chained three-table join one ON clause is missing or wrong, so a cartesian product appears in the middle.

FixVerify step by step: join two tables and check the row count, then add the third, and confirm the driving table and row estimates for each step in the EXPLAIN output.

SymptomA join that was fast becomes slow even though the data volume has not grown.

CauseThe join key lost its index, or stale statistics led the optimiser to the wrong driving table.

FixUse EXPLAIN to check for full scans and index the join key, and compare estimates against reality with EXPLAIN ANALYZE when in doubt.

SymptomThe same SQL behaves differently on MySQL and on PostgreSQL.

CauseSupport for FULL OUTER JOIN and USING differs, and so do string concatenation and GROUP BY rules, in particular whether every non-aggregated column must appear in the group.

FixWrite an equivalent form for the target engine, and keep cross-database reporting to joins, explicit column names and strict GROUP BY.

SymptomA NATURAL JOIN drops a column from the result and the semantics shift unexpectedly.

CauseNATURAL JOIN matches every shared column name implicitly, so adding a same-named column on either side changes the join condition without any warning.

FixSwitch to an explicit JOIN ... ON with the join columns written out, so schema changes cannot introduce a silent bug.

SymptomA query in a multi-tenant database returns rows belonging to another tenant.

CauseThe join condition only asserted equality of the primary key and forgot tenant_id, or the tenant filter was applied to one side alone.

FixPut tenant_id into the ON clause, filter on both sides, and consider a composite index that leads with the tenant column.

SymptomA query using NOT IN against a subquery comes back with no rows at all.

CauseThe subquery yields a NULL, and NOT IN evaluates to unknown for every row once a NULL is present, so nothing can be returned.

FixUse NOT EXISTS instead, or filter the NULLs out inside the subquery.

Troubleshooting

  1. 1Read the plan and the join types

    EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id

    Focus on the type, key and rows columns for each table.

  2. 2Compare estimated rows against actual rows

    EXPLAIN ANALYZE SELECT * FROM users u JOIN orders o ON u.id = o.user_id

    A gap of orders of magnitude means the statistics need refreshing.

  3. 3Confirm the join keys are indexed

    grep -n "CREATE INDEX" schema.sql

    A missing index is the most common root cause of slow joins.

  4. 4Verify tenant filters appear on both sides

    grep -n "tenant_id" queries.sql

    Every query touching a multi-tenant table should filter twice.

  5. 5Check for accidental NATURAL JOIN usage

    grep -n "NATURAL JOIN" queries.sql

    Its presence in a production query basically always means rewriting with an explicit ON.

  6. 6Look for right-table conditions that landed in WHERE

    grep -n "LEFT JOIN" -A 3 queries.sql

    A WHERE right after the join that references a right-table column is the signal of a degraded inner join.

Command Examples

Anti-join: find users with no orders at all

SELECT u.name, o.total FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid' WHERE o.id IS NULL

Putting the condition in ON rather than WHERE is what makes this work: an ON clause only decides whether the right row matches, it never filters rows out of the left table. Users with zero orders therefore survive the join, and WHERE o.id IS NULL picks them out. Move o.status into WHERE and every user without a paid order gets mislabelled as having no orders.

Zero-order users: LEFT JOIN plus aggregation plus HAVING

SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(o.id) = 0

COUNT(o.id), never COUNT(*): with no matching order every o.id is NULL so COUNT(o.id) is 0, while COUNT(*) still sees the surviving left row and returns 1, so the condition never fires. Whenever you need "how many" rather than "does it exist", count a nullable column from the outer-joined side.

Self-join to rebuild a hierarchy from a flat table

SELECT t1.name AS grandchild, t3.name AS grandparent FROM tree t1 JOIN tree t2 ON t1.parent_id = t2.id JOIN tree t3 ON t2.parent_id = t3.id

Three aliases of the same tree table joined in sequence walk parent_id up two generations. A NULL grandparent does not mean the query is wrong: the root simply has no parent. If you need to tell roots from broken links, switch to a LEFT JOIN and test t3.name IS NULL.

Compare the execution plan before and after indexing the foreign key

EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id CREATE INDEX idx_orders_user_id ON orders(user_id) EXPLAIN ANALYZE SELECT * FROM users u JOIN orders o ON u.id = o.user_id

A Nested Loop plan with a large row estimate means orders.user_id is not indexed, so the database rescans the whole orders table once per user row. On a large orders table that is a disaster. Re-run EXPLAIN ANALYZE after creating the index and confirm the actual scanned row count dropped before calling it done.

Output

Nested Loop  (cost=0.00..45231.44 rows=1500)

Use EXISTS instead of IN for a large subquery

SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id) SELECT * FROM orders o WHERE o.user_id IN (SELECT id FROM users WHERE status = 'active')

The semantics differ in an important way: EXISTS and NOT EXISTS short-circuit at the first match, while IN generally has to materialize the whole subquery result. With a users table of hundreds of thousands of rows, the intermediate IN result can exceed the main query; switching to EXISTS usually turns a Materialize into a Semi Join.

Common Pitfalls

  • Omitting ON (or writing ON 1=1, or mistyping a.id=b.id as a.id=a.id) yields a cartesian product: two 50k-row tables return 2.5 billion rows, filling the disk and exhausting the connection pool. In production this usually shows up as "the API got slow", not as a SQL error, which makes it very hard to trace. Before running, check that the number of ON predicates equals the number of join keys.
  • NULL never compares equal in an inner join: ON a.user_id = o.user_id treats NULL as non-matching, not as matching. Any nullable join column silently drops rows. When a join column can be NULL, wrap it in NULLIF or use IS NOT DISTINCT FROM semantics, and add o.user_id IS NOT NULL to the predicate.
  • SELECT DISTINCT removes duplicate rows but does nothing about their source. A LEFT JOIN against a detail table (orders, items) legitimately multiplies rows, and DISTINCT then throws away the amount and quantity along with the duplicates: a user with three 100-total orders collapses to a single row of 100. Aggregate with GROUP BY on the primary key instead of masking it with DISTINCT.
  • Moving a right-table filter into the WHERE clause of a LEFT JOIN silently degrades it to an INNER JOIN: the very rows the LEFT JOIN was meant to preserve (users with no orders) are removed by WHERE o.total > 100. To keep every left row, right-table conditions belong in ON.
  • SELECT * in a JOIN yields duplicate column names for identically named columns (id, created_at from both sides), and positional or name-based access can silently read the wrong table. Worse, count(o.id) stays correct so the bug survives for years. Select only the columns you need and alias them explicitly.
  • Forgetting an index on the join key forces a full table scan, and the rows estimate in EXPLAIN reveals it. Before adding an index for users JOIN orders ON u.id = o.user_id, check whether orders.user_id is already indexed; a redundant index only slows writes.

Tips

  • A LEFT JOIN WHERE condition in ON only affects right-table matching; in WHERE it filters the whole result set.
  • CROSS JOIN produces a Cartesian product — use cautiously on large data.
  • Table aliases (u, o) make multi-table queries more concise and readable.
  • EXISTS subqueries can outperform JOIN in some cases, especially when only existence matters.
  • Index the JOIN columns, otherwise a full table scan hurts performance.
  • USING(a, b) is cleaner than ON x.a = y.a AND x.b = y.b, and merges same-named columns.

FAQ

How do I choose between INNER JOIN and LEFT JOIN?

Decide with one question: do you need to keep left-table rows that have no match on the right? If a record only counts when both sides exist, use INNER — SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id, where JOIN defaults to INNER. If every left row must survive even without orders, use LEFT JOIN and expect NULLs on the right. The classic trap is filter placement: LEFT JOIN ... WHERE o.status = 'paid' drops the orderless users because NULL never compares true, while putting the condition in ON — ON u.id = o.user_id AND o.status = 'paid' — only affects matching and preserves the left row count.

My JOIN returns far more rows than expected — why?

Almost always one-to-many fan-out. Joining users to orders emits one row per order, so a user with three orders becomes three rows; adding a payments join multiplies it again. Remedies: select only the columns you actually need instead of SELECT *; collapse to one row per group with an alias plus aggregation such as SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id; or skip the JOIN entirely when you only test existence and use WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id), which can even be faster in some cases.

How do I find rows with no match, such as users who never ordered?

Use LEFT JOIN with an IS NULL test: SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.id IS NULL. Matched rows have a non-null id on the right and get filtered away, leaving exactly the orderless users. HAVING gives the same answer: SELECT u.name, COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(o.id) = 0. Or skip the JOIN entirely with SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id). When you need rows exclusive to either side, reach for FULL OUTER JOIN with WHERE u.id IS NULL OR o.user_id IS NULL to get the symmetric difference.

Between USING, NATURAL JOIN and ON, which should I write for a multi-table query?

ON is the general and clearest form, and it is mandatory when the columns have different names: ON u.id = o.user_id AND u.tenant_id = o.tenant_id. When the join columns share a name, USING is shorter: SELECT * FROM users JOIN orders USING (user_id), and multiple columns work too as USING (user_id, tenant_id) — the result set merges identically named columns into one field, removing the u. versus o. ambiguity. NATURAL JOIN matches on every shared column name automatically, so a schema change breaks it silently; the sheet flags it as risky and recommends reserving it for throwaway queries.

A JOIN query suddenly got slow — what do I check first?

Start with the plan: EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id shows the join method, and EXPLAIN ANALYZE (PostgreSQL / MySQL 8.0+) actually runs it and reports timings. The usual culprit is a missing index on the join column — add one with CREATE INDEX idx_orders_user_id ON orders(user_id) to avoid full scans. Next, replace SELECT * with just the columns you need to reduce table lookups. When MySQL picks the wrong driving table, force the order with STRAIGHT_JOIN or pin the index with FORCE INDEX(idx_user_id). Remember that filtering after aggregation (COUNT, SUM, AVG) belongs in HAVING, not WHERE.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us