← Application StudioAvailable · v0.1.0

SQL join row tracer

Follow each output row back to its source. See why two matching rows on each side produce four joined rows.

Left table · L rows

Right table · R rows

Up to 50 rows and 12 scalar fields per table; 20,000 characters per input and 2,000 output rows. Keys compare exact types and case. JSON null never matches. No SQL is executed.

Page-memory inputs only. Nothing is uploaded or saved. Clear or reload removes your data; use non-sensitive sample rows.

Why a join can produce more rows

A join returns matching row pairs. If a non-null key appears twice in the left input and twice in the right input, those rows form four pairs. This is not a request to remove repeated values automatically: each pair may describe a different relationship. The tracer keeps original input order labels such as L1 and R2 so you can see the rows behind a result.

Try the example

Load the example and choose INNER. The two left rows with key 1 match two right rows with key 1, producing four output rows. The left-only key 2 and right-only key 3 have no partner. The null key on each side does not match the other null key. LEFT retains the four pairs and adds two unmatched left rows, giving six. RIGHT also gives six; FULL OUTER gives eight.

The key breakdown shows left and right occurrence counts and the number of matching pairs for each non-null typed key. The result table names the source row on each side and displays its complete JSON object. “Unmatched side” means an outer join retained a row without a partner. In a SQL result, the absent side's columns would be filled with NULL; that differs from a real input row whose key itself is null.

Prepare a small input

Enter each table as a JSON array of flat objects and name its key field. Every row must contain that field. Text, finite numbers, booleans and null are accepted; arrays and nested objects inside a row are not. Numbers must stay within JavaScript’s safe integer magnitude; arbitrary-precision decimal behavior is not implemented. Use JSON null for an unknown value, an empty quoted string for empty text, and a quoted numeric identifier when you want it treated as text. An empty array is a valid table with zero rows.

Input row labels are positions in the supplied array, not permanent database identifiers. Result order follows the app's left-to-right trace and is a presentation choice. SQL does not promise a row order without an ORDER BY clause. Editing either input or changing the join type clears the old trace; press Trace join again to calculate it.

Equality and database differences

This tool demonstrates equality joins on one key per table. It compares non-null values with exactly the same JSON type and value: number 1 does not match string “1”, and “Ada” does not match “ada”. Null keys never match, including another null key. The app does not implement null-safe equality.

A real database may apply data types, conversions, collations or other rules that differ from this simplified model. Composite keys, conditions other than equality, CROSS JOIN, filters, aggregation, DISTINCT and arbitrary SQL are outside this tool. It does not connect to a database or validate a production query. Use a small representative example to understand row pairing, then check the actual query against your database documentation.

Limits and privacy

The row, field, text and output limits keep the trace readable and bounded in a browser. Results that exceed the output cap are rejected rather than silently truncated. No input or result history is retained. Reloading clears the form. Keep any examples you need separately and avoid pasting private production records.

Technical reference: PostgreSQL documentation on joined tables.