SQL Join Visualizer
Venn diagrams are a poor model for SQL joins — they cannot show duplicate matches or NULL-filled columns, which is where most join bugs actually live. This visualizer runs the join on real rows instead. Edit the two sample tables as CSV, set the join key on each side, and switch between INNER, LEFT, RIGHT, FULL OUTER, CROSS and the three anti-join patterns. You get the exact output rows with NULLs printed explicitly, the row count, and the SQL statement that produces them. Everything runs in your browser; no data is uploaded.
Do more than sql join visualizer — meet Chat2DB
Chat2DB is an AI-powered SQL client for Windows, macOS and Linux. Write SQL in natural language, format and optimize queries automatically, and manage MySQL, PostgreSQL, Oracle and 20+ other databases in one workspace.
How to use
- Edit the left and right tables as CSV — the first line is the header row, one record per line after that.
- Set the table names and the join key column on each side (the key is ignored for CROSS JOIN).
- Pick a join type to see the resulting rows, the row count and the generated SQL, and read the explanation of why rows appeared or vanished.
Frequently asked questions
What is the difference between INNER JOIN and LEFT JOIN?
INNER JOIN returns only rows whose key matches on both sides; anything unmatched is dropped from the result. LEFT JOIN returns every row of the left table regardless, filling the right table's columns with NULL when there is no match. Switch between the two in the visualizer with the sample data: the INNER result loses Carol and Dan (users with no orders), while LEFT keeps them with NULL order columns. This is the single most common cause of 'my report is missing rows'.
Why does my join return more rows than the original table?
Because the join key is not unique on the other side. A join matches every qualifying pair, so if one user has three orders, that user's row is repeated three times — one per matching order. This is called row multiplication or fan-out, and it silently inflates SUM() and COUNT() results. Fix it by aggregating the many-side first (a subquery or CTE with GROUP BY) and joining to that, or by adding DISTINCT only after you understand which side fans out.
How do I find rows that exist in one table but not the other?
Use an anti join: LEFT JOIN the other table and keep only rows where its key came back NULL — SELECT u.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.user_id IS NULL. Pick 'LEFT ANTI' in the visualizer to see the generated statement. NOT EXISTS is an equivalent and often faster alternative; avoid NOT IN when the subquery can return NULL, because that makes the whole predicate return no rows. Chat2DB can build and run these joins against your live schema — download it at https://chat2db.ai/download or use the web version at https://app.chat2db.ai.
