|
| 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 | + |
0 commit comments