Skip to content

Commit f12b15b

Browse files
Add joins documentation (#3775)
Adds documentation for joins using comma-separated FROM clause with WHERE conditions. Includes examples of cross joins, equijoins, three-way joins, joins with subqueries/CTEs/functions, and semi-joins with EXISTS.
1 parent beb4176 commit f12b15b

File tree

4 files changed

+408
-0
lines changed

4 files changed

+408
-0
lines changed

docs/sphinx/source/SQL_Reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ to change frequently. This reference is currently also a work in progress to be
1111
reference/sql_types
1212
reference/sql_commands
1313
reference/Functions
14+
reference/Subqueries
15+
reference/Joins
1416
```
1517

1618
```{toctree}
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
=====
2+
Joins
3+
=====
4+
5+
.. _joins:
6+
7+
Joins combine rows from two or more tables based on a related column. FDB Record Layer supports joins using comma-separated table references in the FROM clause with join conditions specified in the WHERE clause.
8+
9+
.. important::
10+
11+
FDB Record Layer **does not support** standard SQL JOIN keywords (``INNER JOIN``, ``LEFT JOIN``, ``RIGHT JOIN``, ``OUTER JOIN``, etc.). Use comma-separated FROM clause instead.
12+
13+
Basic Join Syntax
14+
=================
15+
16+
Cross Join (Cartesian Product)
17+
-------------------------------
18+
19+
List multiple tables separated by commas:
20+
21+
.. code-block:: sql
22+
23+
SELECT columns FROM table1, table2
24+
25+
This produces a Cartesian product of all rows from both tables.
26+
27+
Equijoin with WHERE Clause
28+
---------------------------
29+
30+
Use WHERE clause to specify join conditions:
31+
32+
.. code-block:: sql
33+
34+
SELECT columns
35+
FROM table1, table2
36+
WHERE table1.column = table2.column
37+
38+
This is equivalent to an INNER JOIN in standard SQL.
39+
40+
Examples
41+
========
42+
43+
Setup
44+
-----
45+
46+
For these examples, assume we have the following tables:
47+
48+
.. code-block:: sql
49+
50+
CREATE TABLE emp(
51+
id BIGINT,
52+
fname STRING,
53+
lname STRING,
54+
dept_id BIGINT,
55+
PRIMARY KEY(id)
56+
)
57+
58+
CREATE TABLE dept(
59+
id BIGINT,
60+
name STRING,
61+
PRIMARY KEY(id)
62+
)
63+
64+
CREATE TABLE project(
65+
id BIGINT,
66+
name STRING,
67+
dsc STRING,
68+
emp_id BIGINT,
69+
PRIMARY KEY(id)
70+
)
71+
72+
INSERT INTO emp VALUES
73+
(1, 'Jack', 'Williams', 1),
74+
(2, 'Thomas', 'Johnson', 1),
75+
(3, 'Emily', 'Martinez', 1),
76+
(5, 'Daniel', 'Miller', 2),
77+
(8, 'Megan', 'Miller', 3)
78+
79+
INSERT INTO dept VALUES
80+
(1, 'Engineering'),
81+
(2, 'Sales'),
82+
(3, 'Marketing')
83+
84+
INSERT INTO project VALUES
85+
(1, 'OLAP', 'Support OLAP queries', 3),
86+
(2, 'SEO', 'Increase visibility on search engines', 8),
87+
(3, 'Feedback', 'Turn customer feedback into items', 5)
88+
89+
Simple Two-Table Join
90+
----------------------
91+
92+
Join employees with their departments:
93+
94+
.. code-block:: sql
95+
96+
SELECT fname, lname
97+
FROM emp, dept
98+
WHERE emp.dept_id = dept.id
99+
AND dept.name = 'Engineering'
100+
101+
.. list-table::
102+
:header-rows: 1
103+
104+
* - :sql:`fname`
105+
- :sql:`lname`
106+
* - :json:`"Jack"`
107+
- :json:`"Williams"`
108+
* - :json:`"Thomas"`
109+
- :json:`"Johnson"`
110+
* - :json:`"Emily"`
111+
- :json:`"Martinez"`
112+
113+
Three-Way Join
114+
--------------
115+
116+
Join across three tables to find departments and their projects:
117+
118+
.. code-block:: sql
119+
120+
SELECT dept.name, project.name
121+
FROM emp, dept, project
122+
WHERE emp.dept_id = dept.id
123+
AND project.emp_id = emp.id
124+
125+
.. list-table::
126+
:header-rows: 1
127+
128+
* - :sql:`dept.name`
129+
- :sql:`project.name`
130+
* - :json:`"Engineering"`
131+
- :json:`"OLAP"`
132+
* - :json:`"Sales"`
133+
- :json:`"Feedback"`
134+
* - :json:`"Marketing"`
135+
- :json:`"SEO"`
136+
137+
The join conditions create relationships: employees are linked to departments via ``dept_id``, and projects are linked to employees via ``emp_id``.
138+
139+
Join with Subquery
140+
------------------
141+
142+
Use a derived table (subquery) in a join:
143+
144+
.. code-block:: sql
145+
146+
SELECT fname, lname
147+
FROM (
148+
SELECT fname, lname, dept_id
149+
FROM emp
150+
WHERE EXISTS (SELECT * FROM project WHERE emp_id = emp.id)
151+
) AS sq, dept
152+
WHERE sq.dept_id = dept.id
153+
AND dept.name = 'Sales'
154+
155+
.. list-table::
156+
:header-rows: 1
157+
158+
* - :sql:`fname`
159+
- :sql:`lname`
160+
* - :json:`"Daniel"`
161+
- :json:`"Miller"`
162+
163+
This finds employees who are assigned to projects and work in the Sales department.
164+
165+
Nested Joins
166+
------------
167+
168+
Join subqueries that themselves contain joins:
169+
170+
.. code-block:: sql
171+
172+
SELECT sq.name, project.name
173+
FROM (
174+
SELECT dept.name, emp.id
175+
FROM emp, dept
176+
WHERE emp.dept_id = dept.id
177+
) AS sq, project
178+
WHERE project.emp_id = sq.id
179+
180+
.. list-table::
181+
:header-rows: 1
182+
183+
* - :sql:`sq.name`
184+
- :sql:`project.name`
185+
* - :json:`"Engineering"`
186+
- :json:`"OLAP"`
187+
* - :json:`"Sales"`
188+
- :json:`"Feedback"`
189+
* - :json:`"Marketing"`
190+
- :json:`"SEO"`
191+
192+
The subquery first joins employees with departments, then the result is joined with projects.
193+
194+
Join with CTEs
195+
--------------
196+
197+
Use Common Table Expressions in joins:
198+
199+
.. code-block:: sql
200+
201+
WITH c1(w, z) AS (SELECT id, col1 FROM t1),
202+
c2(a, b) AS (SELECT id, col1 FROM t1 WHERE id IN (1, 2))
203+
SELECT * FROM c1, c2
204+
205+
This creates two CTEs and joins them using a cross join.
206+
207+
Self-Join
208+
---------
209+
210+
Join a table to itself:
211+
212+
.. code-block:: sql
213+
214+
SELECT * FROM Table1, Table1 WHERE col1 = 10
215+
216+
This self-join can be used to find relationships within the same table. Use aliases to distinguish between the two references:
217+
218+
.. code-block:: sql
219+
220+
SELECT t1.fname, t2.fname
221+
FROM emp t1, emp t2
222+
WHERE t1.dept_id = t2.dept_id
223+
AND t1.id < t2.id
224+
225+
Semi-Join with EXISTS
226+
----------------------
227+
228+
Use EXISTS to implement a semi-join (find rows that have matching rows in another table):
229+
230+
.. code-block:: sql
231+
232+
SELECT fname, lname
233+
FROM emp
234+
WHERE EXISTS (
235+
SELECT * FROM project WHERE emp_id = emp.id
236+
)
237+
238+
.. list-table::
239+
:header-rows: 1
240+
241+
* - :sql:`fname`
242+
- :sql:`lname`
243+
* - :json:`"Emily"`
244+
- :json:`"Martinez"`
245+
* - :json:`"Daniel"`
246+
- :json:`"Miller"`
247+
* - :json:`"Megan"`
248+
- :json:`"Miller"`
249+
250+
This finds all employees who have at least one project assigned, without returning duplicate employee rows.
251+
252+
Join with User-Defined Functions
253+
---------------------------------
254+
255+
Join results from user-defined functions:
256+
257+
.. code-block:: sql
258+
259+
SELECT A.col1, A.col2, B.col1, B.col2
260+
FROM f1(103, 'b') A, f1(103, 'b') B
261+
WHERE A.col1 = B.col1
262+
263+
User-defined functions can be used like tables in the FROM clause and joined with join conditions in the WHERE clause.
264+
265+
Important Notes
266+
===============
267+
268+
Table Aliases
269+
-------------
270+
271+
Use aliases to:
272+
273+
- Distinguish between multiple references to the same table
274+
- Shorten long table names
275+
- Reference columns from specific tables in multi-table joins
276+
277+
.. code-block:: sql
278+
279+
SELECT e.fname, d.name
280+
FROM emp e, dept d
281+
WHERE e.dept_id = d.id
282+
283+
Join Conditions
284+
---------------
285+
286+
- Join conditions should be specified in the WHERE clause
287+
- Use ``AND`` to combine multiple join conditions and filters
288+
- Missing join conditions result in a Cartesian product (all combinations)
289+
290+
See Also
291+
========
292+
293+
* :doc:`sql_commands/DQL/SELECT` - SELECT statement syntax
294+
* :doc:`sql_commands/DQL/WHERE` - WHERE clause filtering
295+
* :doc:`Subqueries` - Subqueries and correlated subqueries
296+
* :doc:`sql_commands/DQL/WITH` - Common Table Expressions
297+

yaml-tests/src/test/java/DocumentationQueriesTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ void isDistinctFromOperatorQueriesTests(YamlTest.Runner runner) throws Exception
5454
runner.runYamsql(PREFIX + "/is-distinct-from-operator-queries.yamsql");
5555
}
5656

57+
@TestTemplate
58+
void joinsDocumentationQueriesTests(YamlTest.Runner runner) throws Exception {
59+
runner.runYamsql(PREFIX + "/joins-documentation-queries.yamsql");
60+
}
61+
5762
@TestTemplate
5863
void likeOperatorQueriesTests(YamlTest.Runner runner) throws Exception {
5964
runner.runYamsql(PREFIX + "/like-operator-queries.yamsql");

0 commit comments

Comments
 (0)