mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-07 13:52:59 +00:00
test: migrate join tests from duckdb, part3 (#6881)
* test: migrate join tests Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * chore: update test results after rebasing main branch Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: unstable query sort results and natural_join test Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: count(*) with joining Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix: unstable query sort results and style Signed-off-by: Dennis Zhuang <killme2008@gmail.com> --------- Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
This commit is contained in:
162
tests/cases/standalone/common/join/cross_join_advanced.result
Normal file
162
tests/cases/standalone/common/join/cross_join_advanced.result
Normal file
@@ -0,0 +1,162 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/cross_product/ advanced tests
|
||||
-- Tests advanced cross join scenarios
|
||||
CREATE TABLE products(prod_id INTEGER, prod_name VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE stores(store_id INTEGER, store_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE categories(cat_id INTEGER, cat_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO products VALUES
|
||||
(1, 'Laptop', 999.99, 1000), (2, 'Mouse', 29.99, 2000), (3, 'Monitor', 299.99, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO stores VALUES
|
||||
(1, 'TechStore', 'NYC', 1000), (2, 'GadgetShop', 'LA', 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO categories VALUES
|
||||
(1, 'Electronics', 1000), (2, 'Accessories', 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
-- Basic cross join
|
||||
SELECT
|
||||
p.prod_name, s.store_name, s.city
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
ORDER BY p.prod_id, s.store_id;
|
||||
|
||||
+-----------+------------+------+
|
||||
| prod_name | store_name | city |
|
||||
+-----------+------------+------+
|
||||
| Laptop | TechStore | NYC |
|
||||
| Laptop | GadgetShop | LA |
|
||||
| Mouse | TechStore | NYC |
|
||||
| Mouse | GadgetShop | LA |
|
||||
| Monitor | TechStore | NYC |
|
||||
| Monitor | GadgetShop | LA |
|
||||
+-----------+------------+------+
|
||||
|
||||
-- Cross join with filtering
|
||||
SELECT
|
||||
p.prod_name, s.store_name, p.price
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
WHERE p.price > 100.00
|
||||
ORDER BY p.price DESC, s.store_name;
|
||||
|
||||
+-----------+------------+--------+
|
||||
| prod_name | store_name | price |
|
||||
+-----------+------------+--------+
|
||||
| Laptop | GadgetShop | 999.99 |
|
||||
| Laptop | TechStore | 999.99 |
|
||||
| Monitor | GadgetShop | 299.99 |
|
||||
| Monitor | TechStore | 299.99 |
|
||||
+-----------+------------+--------+
|
||||
|
||||
-- Triple cross join
|
||||
SELECT
|
||||
p.prod_name, s.store_name, c.cat_name,
|
||||
CASE WHEN p.price > 500 THEN 'Premium' ELSE 'Standard' END as tier
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
CROSS JOIN categories c
|
||||
ORDER BY p.prod_id, s.store_id, c.cat_id;
|
||||
|
||||
+-----------+------------+-------------+----------+
|
||||
| prod_name | store_name | cat_name | tier |
|
||||
+-----------+------------+-------------+----------+
|
||||
| Laptop | TechStore | Electronics | Premium |
|
||||
| Laptop | TechStore | Accessories | Premium |
|
||||
| Laptop | GadgetShop | Electronics | Premium |
|
||||
| Laptop | GadgetShop | Accessories | Premium |
|
||||
| Mouse | TechStore | Electronics | Standard |
|
||||
| Mouse | TechStore | Accessories | Standard |
|
||||
| Mouse | GadgetShop | Electronics | Standard |
|
||||
| Mouse | GadgetShop | Accessories | Standard |
|
||||
| Monitor | TechStore | Electronics | Standard |
|
||||
| Monitor | TechStore | Accessories | Standard |
|
||||
| Monitor | GadgetShop | Electronics | Standard |
|
||||
| Monitor | GadgetShop | Accessories | Standard |
|
||||
+-----------+------------+-------------+----------+
|
||||
|
||||
-- Cross join with aggregation
|
||||
SELECT
|
||||
s.city,
|
||||
COUNT(*) as product_store_combinations,
|
||||
AVG(p.price) as avg_price,
|
||||
SUM(p.price) as total_inventory_value
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
GROUP BY s.city
|
||||
ORDER BY s.city;
|
||||
|
||||
+------+----------------------------+-------------------+-----------------------+
|
||||
| city | product_store_combinations | avg_price | total_inventory_value |
|
||||
+------+----------------------------+-------------------+-----------------------+
|
||||
| LA | 3 | 443.3233333333333 | 1329.97 |
|
||||
| NYC | 3 | 443.3233333333333 | 1329.97 |
|
||||
+------+----------------------------+-------------------+-----------------------+
|
||||
|
||||
-- Cross join for inventory matrix
|
||||
SELECT
|
||||
p.prod_name,
|
||||
SUM(CASE WHEN s.city = 'NYC' THEN 1 ELSE 0 END) as nyc_availability,
|
||||
SUM(CASE WHEN s.city = 'LA' THEN 1 ELSE 0 END) as la_availability,
|
||||
COUNT(s.store_id) as total_store_availability
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
GROUP BY p.prod_name, p.prod_id
|
||||
ORDER BY p.prod_id;
|
||||
|
||||
+-----------+------------------+-----------------+--------------------------+
|
||||
| prod_name | nyc_availability | la_availability | total_store_availability |
|
||||
+-----------+------------------+-----------------+--------------------------+
|
||||
| Laptop | 1 | 1 | 2 |
|
||||
| Mouse | 1 | 1 | 2 |
|
||||
| Monitor | 1 | 1 | 2 |
|
||||
+-----------+------------------+-----------------+--------------------------+
|
||||
|
||||
-- Cross join with conditions and calculations
|
||||
SELECT
|
||||
p.prod_name,
|
||||
s.store_name,
|
||||
p.price,
|
||||
p.price * 0.1 as store_commission,
|
||||
p.price * 1.08 as price_with_tax
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
WHERE p.price BETWEEN 25.00 AND 1000.00
|
||||
ORDER BY p.price DESC, s.store_name;
|
||||
|
||||
+-----------+------------+--------+--------------------+--------------------+
|
||||
| prod_name | store_name | price | store_commission | price_with_tax |
|
||||
+-----------+------------+--------+--------------------+--------------------+
|
||||
| Laptop | GadgetShop | 999.99 | 99.99900000000001 | 1079.9892 |
|
||||
| Laptop | TechStore | 999.99 | 99.99900000000001 | 1079.9892 |
|
||||
| Monitor | GadgetShop | 299.99 | 29.999000000000002 | 323.98920000000004 |
|
||||
| Monitor | TechStore | 299.99 | 29.999000000000002 | 323.98920000000004 |
|
||||
| Mouse | GadgetShop | 29.99 | 2.999 | 32.3892 |
|
||||
| Mouse | TechStore | 29.99 | 2.999 | 32.3892 |
|
||||
+-----------+------------+--------+--------------------+--------------------+
|
||||
|
||||
DROP TABLE products;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE stores;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE categories;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
81
tests/cases/standalone/common/join/cross_join_advanced.sql
Normal file
81
tests/cases/standalone/common/join/cross_join_advanced.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/cross_product/ advanced tests
|
||||
-- Tests advanced cross join scenarios
|
||||
|
||||
CREATE TABLE products(prod_id INTEGER, prod_name VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE stores(store_id INTEGER, store_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE categories(cat_id INTEGER, cat_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO products VALUES
|
||||
(1, 'Laptop', 999.99, 1000), (2, 'Mouse', 29.99, 2000), (3, 'Monitor', 299.99, 3000);
|
||||
|
||||
INSERT INTO stores VALUES
|
||||
(1, 'TechStore', 'NYC', 1000), (2, 'GadgetShop', 'LA', 2000);
|
||||
|
||||
INSERT INTO categories VALUES
|
||||
(1, 'Electronics', 1000), (2, 'Accessories', 2000);
|
||||
|
||||
-- Basic cross join
|
||||
SELECT
|
||||
p.prod_name, s.store_name, s.city
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
ORDER BY p.prod_id, s.store_id;
|
||||
|
||||
-- Cross join with filtering
|
||||
SELECT
|
||||
p.prod_name, s.store_name, p.price
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
WHERE p.price > 100.00
|
||||
ORDER BY p.price DESC, s.store_name;
|
||||
|
||||
-- Triple cross join
|
||||
SELECT
|
||||
p.prod_name, s.store_name, c.cat_name,
|
||||
CASE WHEN p.price > 500 THEN 'Premium' ELSE 'Standard' END as tier
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
CROSS JOIN categories c
|
||||
ORDER BY p.prod_id, s.store_id, c.cat_id;
|
||||
|
||||
-- Cross join with aggregation
|
||||
SELECT
|
||||
s.city,
|
||||
COUNT(*) as product_store_combinations,
|
||||
AVG(p.price) as avg_price,
|
||||
SUM(p.price) as total_inventory_value
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
GROUP BY s.city
|
||||
ORDER BY s.city;
|
||||
|
||||
-- Cross join for inventory matrix
|
||||
SELECT
|
||||
p.prod_name,
|
||||
SUM(CASE WHEN s.city = 'NYC' THEN 1 ELSE 0 END) as nyc_availability,
|
||||
SUM(CASE WHEN s.city = 'LA' THEN 1 ELSE 0 END) as la_availability,
|
||||
COUNT(s.store_id) as total_store_availability
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
GROUP BY p.prod_name, p.prod_id
|
||||
ORDER BY p.prod_id;
|
||||
|
||||
-- Cross join with conditions and calculations
|
||||
SELECT
|
||||
p.prod_name,
|
||||
s.store_name,
|
||||
p.price,
|
||||
p.price * 0.1 as store_commission,
|
||||
p.price * 1.08 as price_with_tax
|
||||
FROM products p
|
||||
CROSS JOIN stores s
|
||||
WHERE p.price BETWEEN 25.00 AND 1000.00
|
||||
ORDER BY p.price DESC, s.store_name;
|
||||
|
||||
DROP TABLE products;
|
||||
|
||||
DROP TABLE stores;
|
||||
|
||||
DROP TABLE categories;
|
||||
63
tests/cases/standalone/common/join/cross_product.result
Normal file
63
tests/cases/standalone/common/join/cross_product.result
Normal file
@@ -0,0 +1,63 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/cross_product/test_cross_product.test
|
||||
-- Tests CROSS JOIN functionality
|
||||
CREATE TABLE small_table (a INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE another_table (b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO small_table VALUES (1, 1000), (2, 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO another_table VALUES (10, 3000), (20, 4000), (30, 5000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Basic CROSS JOIN
|
||||
SELECT * FROM small_table CROSS JOIN another_table ORDER BY a, b;
|
||||
|
||||
+---+---------------------+----+---------------------+
|
||||
| a | ts | b | ts |
|
||||
+---+---------------------+----+---------------------+
|
||||
| 1 | 1970-01-01T00:00:01 | 10 | 1970-01-01T00:00:03 |
|
||||
| 1 | 1970-01-01T00:00:01 | 20 | 1970-01-01T00:00:04 |
|
||||
| 1 | 1970-01-01T00:00:01 | 30 | 1970-01-01T00:00:05 |
|
||||
| 2 | 1970-01-01T00:00:02 | 10 | 1970-01-01T00:00:03 |
|
||||
| 2 | 1970-01-01T00:00:02 | 20 | 1970-01-01T00:00:04 |
|
||||
| 2 | 1970-01-01T00:00:02 | 30 | 1970-01-01T00:00:05 |
|
||||
+---+---------------------+----+---------------------+
|
||||
|
||||
-- CROSS JOIN with WHERE filter
|
||||
SELECT * FROM small_table CROSS JOIN another_table WHERE a + b < 25 ORDER BY a, b;
|
||||
|
||||
+---+---------------------+----+---------------------+
|
||||
| a | ts | b | ts |
|
||||
+---+---------------------+----+---------------------+
|
||||
| 1 | 1970-01-01T00:00:01 | 10 | 1970-01-01T00:00:03 |
|
||||
| 1 | 1970-01-01T00:00:01 | 20 | 1970-01-01T00:00:04 |
|
||||
| 2 | 1970-01-01T00:00:02 | 10 | 1970-01-01T00:00:03 |
|
||||
| 2 | 1970-01-01T00:00:02 | 20 | 1970-01-01T00:00:04 |
|
||||
+---+---------------------+----+---------------------+
|
||||
|
||||
-- CROSS JOIN with aliases
|
||||
SELECT s.a, t.b FROM small_table s CROSS JOIN another_table t WHERE s.a = 1 ORDER BY b;
|
||||
|
||||
+---+----+
|
||||
| a | b |
|
||||
+---+----+
|
||||
| 1 | 10 |
|
||||
| 1 | 20 |
|
||||
| 1 | 30 |
|
||||
+---+----+
|
||||
|
||||
DROP TABLE another_table;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE small_table;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
23
tests/cases/standalone/common/join/cross_product.sql
Normal file
23
tests/cases/standalone/common/join/cross_product.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/cross_product/test_cross_product.test
|
||||
-- Tests CROSS JOIN functionality
|
||||
|
||||
CREATE TABLE small_table (a INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE another_table (b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO small_table VALUES (1, 1000), (2, 2000);
|
||||
|
||||
INSERT INTO another_table VALUES (10, 3000), (20, 4000), (30, 5000);
|
||||
|
||||
-- Basic CROSS JOIN
|
||||
SELECT * FROM small_table CROSS JOIN another_table ORDER BY a, b;
|
||||
|
||||
-- CROSS JOIN with WHERE filter
|
||||
SELECT * FROM small_table CROSS JOIN another_table WHERE a + b < 25 ORDER BY a, b;
|
||||
|
||||
-- CROSS JOIN with aliases
|
||||
SELECT s.a, t.b FROM small_table s CROSS JOIN another_table t WHERE s.a = 1 ORDER BY b;
|
||||
|
||||
DROP TABLE another_table;
|
||||
|
||||
DROP TABLE small_table;
|
||||
61
tests/cases/standalone/common/join/full_outer_join.result
Normal file
61
tests/cases/standalone/common/join/full_outer_join.result
Normal file
@@ -0,0 +1,61 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/full_outer/test_full_outer_join.test
|
||||
-- Tests FULL OUTER JOIN scenarios
|
||||
CREATE TABLE left_full("id" INTEGER, "name" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE right_full("id" INTEGER, "value" INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO left_full VALUES (1, 'Alice', 1000), (2, 'Bob', 2000), (3, 'Carol', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO right_full VALUES (2, 200, 4000), (3, 300, 5000), (4, 400, 6000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Basic FULL OUTER JOIN
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| id | name | ts | id | value | ts |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | | | |
|
||||
| 2 | Bob | 1970-01-01T00:00:02 | 2 | 200 | 1970-01-01T00:00:04 |
|
||||
| 3 | Carol | 1970-01-01T00:00:03 | 3 | 300 | 1970-01-01T00:00:05 |
|
||||
| | | | 4 | 400 | 1970-01-01T00:00:06 |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
|
||||
-- FULL OUTER JOIN with WHERE on result
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" WHERE l."name" IS NULL OR r."value" IS NULL ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| id | name | ts | id | value | ts |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | | | |
|
||||
| | | | 4 | 400 | 1970-01-01T00:00:06 |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
|
||||
-- FULL OUTER JOIN with complex conditions
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" AND r."value" > 250 ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| id | name | ts | id | value | ts |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | | | |
|
||||
| 2 | Bob | 1970-01-01T00:00:02 | | | |
|
||||
| 3 | Carol | 1970-01-01T00:00:03 | 3 | 300 | 1970-01-01T00:00:05 |
|
||||
| | | | 2 | 200 | 1970-01-01T00:00:04 |
|
||||
| | | | 4 | 400 | 1970-01-01T00:00:06 |
|
||||
+----+-------+---------------------+----+-------+---------------------+
|
||||
|
||||
DROP TABLE right_full;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE left_full;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
23
tests/cases/standalone/common/join/full_outer_join.sql
Normal file
23
tests/cases/standalone/common/join/full_outer_join.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/full_outer/test_full_outer_join.test
|
||||
-- Tests FULL OUTER JOIN scenarios
|
||||
|
||||
CREATE TABLE left_full("id" INTEGER, "name" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE right_full("id" INTEGER, "value" INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO left_full VALUES (1, 'Alice', 1000), (2, 'Bob', 2000), (3, 'Carol', 3000);
|
||||
|
||||
INSERT INTO right_full VALUES (2, 200, 4000), (3, 300, 5000), (4, 400, 6000);
|
||||
|
||||
-- Basic FULL OUTER JOIN
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
-- FULL OUTER JOIN with WHERE on result
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" WHERE l."name" IS NULL OR r."value" IS NULL ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
-- FULL OUTER JOIN with complex conditions
|
||||
SELECT * FROM left_full l FULL OUTER JOIN right_full r ON l."id" = r."id" AND r."value" > 250 ORDER BY l."id" NULLS LAST, r."id" NULLS LAST;
|
||||
|
||||
DROP TABLE right_full;
|
||||
|
||||
DROP TABLE left_full;
|
||||
140
tests/cases/standalone/common/join/hash_join_complex.result
Normal file
140
tests/cases/standalone/common/join/hash_join_complex.result
Normal file
@@ -0,0 +1,140 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ hash join tests
|
||||
-- Tests complex hash join scenarios
|
||||
CREATE TABLE large_table_a("id" INTEGER, value_a VARCHAR, num_a INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE large_table_b("id" INTEGER, value_b VARCHAR, num_b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO large_table_a VALUES
|
||||
(1, 'alpha', 100, 1000), (2, 'beta', 200, 2000), (3, 'gamma', 300, 3000),
|
||||
(4, 'delta', 400, 4000), (5, 'epsilon', 500, 5000), (6, 'zeta', 600, 6000),
|
||||
(7, 'eta', 700, 7000), (8, 'theta', 800, 8000), (9, 'iota', 900, 9000),
|
||||
(10, 'kappa', 1000, 10000);
|
||||
|
||||
Affected Rows: 10
|
||||
|
||||
INSERT INTO large_table_b VALUES
|
||||
(2, 'second', 20, 1000), (4, 'fourth', 40, 2000), (6, 'sixth', 60, 3000),
|
||||
(8, 'eighth', 80, 4000), (10, 'tenth', 100, 5000), (12, 'twelfth', 120, 6000),
|
||||
(14, 'fourteenth', 140, 7000), (16, 'sixteenth', 160, 8000);
|
||||
|
||||
Affected Rows: 8
|
||||
|
||||
-- Hash join with exact match
|
||||
SELECT
|
||||
a."id", a.value_a, a.num_a, b.value_b, b.num_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
ORDER BY a."id";
|
||||
|
||||
+----+---------+-------+---------+-------+
|
||||
| id | value_a | num_a | value_b | num_b |
|
||||
+----+---------+-------+---------+-------+
|
||||
| 2 | beta | 200 | second | 20 |
|
||||
| 4 | delta | 400 | fourth | 40 |
|
||||
| 6 | zeta | 600 | sixth | 60 |
|
||||
| 8 | theta | 800 | eighth | 80 |
|
||||
| 10 | kappa | 1000 | tenth | 100 |
|
||||
+----+---------+-------+---------+-------+
|
||||
|
||||
-- Hash join with multiple key conditions
|
||||
SELECT
|
||||
a."id", a.value_a, b.value_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id" AND a.num_a > b.num_b * 5
|
||||
ORDER BY a."id";
|
||||
|
||||
+----+---------+---------+
|
||||
| id | value_a | value_b |
|
||||
+----+---------+---------+
|
||||
| 2 | beta | second |
|
||||
| 4 | delta | fourth |
|
||||
| 6 | zeta | sixth |
|
||||
| 8 | theta | eighth |
|
||||
| 10 | kappa | tenth |
|
||||
+----+---------+---------+
|
||||
|
||||
-- Hash join with aggregation on both sides
|
||||
SELECT
|
||||
joined_data."id",
|
||||
joined_data.combined_num,
|
||||
joined_data.value_concat
|
||||
FROM (
|
||||
SELECT
|
||||
a."id",
|
||||
a.num_a + b.num_b as combined_num,
|
||||
a.value_a || '-' || b.value_b as value_concat
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
) joined_data
|
||||
WHERE joined_data.combined_num > 500
|
||||
ORDER BY joined_data.combined_num DESC;
|
||||
|
||||
+----+--------------+--------------+
|
||||
| id | combined_num | value_concat |
|
||||
+----+--------------+--------------+
|
||||
| 10 | 1100 | kappa-tenth |
|
||||
| 8 | 880 | theta-eighth |
|
||||
| 6 | 660 | zeta-sixth |
|
||||
+----+--------------+--------------+
|
||||
|
||||
-- Hash join with filtering on both tables
|
||||
SELECT
|
||||
a.value_a, b.value_b, a.num_a, b.num_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
WHERE a.num_a > 500 AND b.num_b < 100
|
||||
ORDER BY a.num_a DESC;
|
||||
|
||||
+---------+---------+-------+-------+
|
||||
| value_a | value_b | num_a | num_b |
|
||||
+---------+---------+-------+-------+
|
||||
| theta | eighth | 800 | 80 |
|
||||
| zeta | sixth | 600 | 60 |
|
||||
+---------+---------+-------+-------+
|
||||
|
||||
-- Hash join for set operations
|
||||
SELECT
|
||||
a."id",
|
||||
'Both Tables' as source,
|
||||
a.value_a as value_from_a,
|
||||
b.value_b as value_from_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
UNION ALL
|
||||
SELECT
|
||||
a."id",
|
||||
'Only Table A' as source,
|
||||
a.value_a,
|
||||
NULL as value_from_b
|
||||
FROM large_table_a a
|
||||
LEFT JOIN large_table_b b ON a."id" = b."id"
|
||||
WHERE b."id" IS NULL
|
||||
ORDER BY "id", source;
|
||||
|
||||
+----+--------------+--------------+--------------+
|
||||
| id | source | value_from_a | value_from_b |
|
||||
+----+--------------+--------------+--------------+
|
||||
| 1 | Only Table A | alpha | |
|
||||
| 2 | Both Tables | beta | second |
|
||||
| 3 | Only Table A | gamma | |
|
||||
| 4 | Both Tables | delta | fourth |
|
||||
| 5 | Only Table A | epsilon | |
|
||||
| 6 | Both Tables | zeta | sixth |
|
||||
| 7 | Only Table A | eta | |
|
||||
| 8 | Both Tables | theta | eighth |
|
||||
| 9 | Only Table A | iota | |
|
||||
| 10 | Both Tables | kappa | tenth |
|
||||
+----+--------------+--------------+--------------+
|
||||
|
||||
DROP TABLE large_table_a;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE large_table_b;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
78
tests/cases/standalone/common/join/hash_join_complex.sql
Normal file
78
tests/cases/standalone/common/join/hash_join_complex.sql
Normal file
@@ -0,0 +1,78 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ hash join tests
|
||||
-- Tests complex hash join scenarios
|
||||
|
||||
CREATE TABLE large_table_a("id" INTEGER, value_a VARCHAR, num_a INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE large_table_b("id" INTEGER, value_b VARCHAR, num_b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO large_table_a VALUES
|
||||
(1, 'alpha', 100, 1000), (2, 'beta', 200, 2000), (3, 'gamma', 300, 3000),
|
||||
(4, 'delta', 400, 4000), (5, 'epsilon', 500, 5000), (6, 'zeta', 600, 6000),
|
||||
(7, 'eta', 700, 7000), (8, 'theta', 800, 8000), (9, 'iota', 900, 9000),
|
||||
(10, 'kappa', 1000, 10000);
|
||||
|
||||
INSERT INTO large_table_b VALUES
|
||||
(2, 'second', 20, 1000), (4, 'fourth', 40, 2000), (6, 'sixth', 60, 3000),
|
||||
(8, 'eighth', 80, 4000), (10, 'tenth', 100, 5000), (12, 'twelfth', 120, 6000),
|
||||
(14, 'fourteenth', 140, 7000), (16, 'sixteenth', 160, 8000);
|
||||
|
||||
-- Hash join with exact match
|
||||
SELECT
|
||||
a."id", a.value_a, a.num_a, b.value_b, b.num_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
ORDER BY a."id";
|
||||
|
||||
-- Hash join with multiple key conditions
|
||||
SELECT
|
||||
a."id", a.value_a, b.value_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id" AND a.num_a > b.num_b * 5
|
||||
ORDER BY a."id";
|
||||
|
||||
-- Hash join with aggregation on both sides
|
||||
SELECT
|
||||
joined_data."id",
|
||||
joined_data.combined_num,
|
||||
joined_data.value_concat
|
||||
FROM (
|
||||
SELECT
|
||||
a."id",
|
||||
a.num_a + b.num_b as combined_num,
|
||||
a.value_a || '-' || b.value_b as value_concat
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
) joined_data
|
||||
WHERE joined_data.combined_num > 500
|
||||
ORDER BY joined_data.combined_num DESC;
|
||||
|
||||
-- Hash join with filtering on both tables
|
||||
SELECT
|
||||
a.value_a, b.value_b, a.num_a, b.num_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
WHERE a.num_a > 500 AND b.num_b < 100
|
||||
ORDER BY a.num_a DESC;
|
||||
|
||||
-- Hash join for set operations
|
||||
SELECT
|
||||
a."id",
|
||||
'Both Tables' as source,
|
||||
a.value_a as value_from_a,
|
||||
b.value_b as value_from_b
|
||||
FROM large_table_a a
|
||||
INNER JOIN large_table_b b ON a."id" = b."id"
|
||||
UNION ALL
|
||||
SELECT
|
||||
a."id",
|
||||
'Only Table A' as source,
|
||||
a.value_a,
|
||||
NULL as value_from_b
|
||||
FROM large_table_a a
|
||||
LEFT JOIN large_table_b b ON a."id" = b."id"
|
||||
WHERE b."id" IS NULL
|
||||
ORDER BY "id", source;
|
||||
|
||||
DROP TABLE large_table_a;
|
||||
|
||||
DROP TABLE large_table_b;
|
||||
79
tests/cases/standalone/common/join/inequality_join.result
Normal file
79
tests/cases/standalone/common/join/inequality_join.result
Normal file
@@ -0,0 +1,79 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_range_join.test
|
||||
-- Tests inequality JOIN conditions
|
||||
CREATE TABLE events("id" INTEGER, event_time INTEGER, duration INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE time_ranges(start_time INTEGER, end_time INTEGER, range_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO events VALUES (1, 10, 5, 1000), (2, 25, 3, 2000), (3, 45, 8, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO time_ranges VALUES (0, 20, 'Early', 4000), (20, 40, 'Mid', 5000), (40, 60, 'Late', 6000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Range join using BETWEEN
|
||||
SELECT e."id", e.event_time, t.range_name
|
||||
FROM events e JOIN time_ranges t ON e.event_time BETWEEN t.start_time AND t.end_time
|
||||
ORDER BY e."id";
|
||||
|
||||
+----+------------+------------+
|
||||
| id | event_time | range_name |
|
||||
+----+------------+------------+
|
||||
| 1 | 10 | Early |
|
||||
| 2 | 25 | Mid |
|
||||
| 3 | 45 | Late |
|
||||
+----+------------+------------+
|
||||
|
||||
-- Inequality join conditions
|
||||
SELECT e."id", e.event_time, e.duration, t.range_name
|
||||
FROM events e JOIN time_ranges t ON e.event_time >= t.start_time AND e.event_time < t.end_time
|
||||
ORDER BY e."id";
|
||||
|
||||
+----+------------+----------+------------+
|
||||
| id | event_time | duration | range_name |
|
||||
+----+------------+----------+------------+
|
||||
| 1 | 10 | 5 | Early |
|
||||
| 2 | 25 | 3 | Mid |
|
||||
| 3 | 45 | 8 | Late |
|
||||
+----+------------+----------+------------+
|
||||
|
||||
-- Join with overlap condition
|
||||
SELECT e."id", t.range_name
|
||||
FROM events e JOIN time_ranges t ON
|
||||
e.event_time < t.end_time AND (e.event_time + e.duration) > t.start_time
|
||||
ORDER BY e."id", t.start_time;
|
||||
|
||||
+----+------------+
|
||||
| id | range_name |
|
||||
+----+------------+
|
||||
| 1 | Early |
|
||||
| 2 | Mid |
|
||||
| 3 | Late |
|
||||
+----+------------+
|
||||
|
||||
-- Self join with inequality
|
||||
SELECT e1."id" as id1, e2."id" as id2, e1.event_time, e2.event_time
|
||||
FROM events e1 JOIN events e2 ON e1.event_time < e2.event_time
|
||||
ORDER BY e1."id", e2."id";
|
||||
|
||||
+-----+-----+------------+------------+
|
||||
| id1 | id2 | event_time | event_time |
|
||||
+-----+-----+------------+------------+
|
||||
| 1 | 2 | 10 | 25 |
|
||||
| 1 | 3 | 10 | 45 |
|
||||
| 2 | 3 | 25 | 45 |
|
||||
+-----+-----+------------+------------+
|
||||
|
||||
DROP TABLE time_ranges;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE events;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
35
tests/cases/standalone/common/join/inequality_join.sql
Normal file
35
tests/cases/standalone/common/join/inequality_join.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_range_join.test
|
||||
-- Tests inequality JOIN conditions
|
||||
|
||||
CREATE TABLE events("id" INTEGER, event_time INTEGER, duration INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE time_ranges(start_time INTEGER, end_time INTEGER, range_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO events VALUES (1, 10, 5, 1000), (2, 25, 3, 2000), (3, 45, 8, 3000);
|
||||
|
||||
INSERT INTO time_ranges VALUES (0, 20, 'Early', 4000), (20, 40, 'Mid', 5000), (40, 60, 'Late', 6000);
|
||||
|
||||
-- Range join using BETWEEN
|
||||
SELECT e."id", e.event_time, t.range_name
|
||||
FROM events e JOIN time_ranges t ON e.event_time BETWEEN t.start_time AND t.end_time
|
||||
ORDER BY e."id";
|
||||
|
||||
-- Inequality join conditions
|
||||
SELECT e."id", e.event_time, e.duration, t.range_name
|
||||
FROM events e JOIN time_ranges t ON e.event_time >= t.start_time AND e.event_time < t.end_time
|
||||
ORDER BY e."id";
|
||||
|
||||
-- Join with overlap condition
|
||||
SELECT e."id", t.range_name
|
||||
FROM events e JOIN time_ranges t ON
|
||||
e.event_time < t.end_time AND (e.event_time + e.duration) > t.start_time
|
||||
ORDER BY e."id", t.start_time;
|
||||
|
||||
-- Self join with inequality
|
||||
SELECT e1."id" as id1, e2."id" as id2, e1.event_time, e2.event_time
|
||||
FROM events e1 JOIN events e2 ON e1.event_time < e2.event_time
|
||||
ORDER BY e1."id", e2."id";
|
||||
|
||||
DROP TABLE time_ranges;
|
||||
|
||||
DROP TABLE events;
|
||||
167
tests/cases/standalone/common/join/inequality_joins.result
Normal file
167
tests/cases/standalone/common/join/inequality_joins.result
Normal file
@@ -0,0 +1,167 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/iejoin/ inequality join tests
|
||||
-- Tests inequality join conditions
|
||||
CREATE TABLE time_events(event_id INTEGER, event_time TIMESTAMP, event_type VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE time_windows(window_id INTEGER, start_time TIMESTAMP, end_time TIMESTAMP, window_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO time_events VALUES
|
||||
(1, '2023-01-01 10:15:00', 'login', 1000),
|
||||
(2, '2023-01-01 10:30:00', 'purchase', 2000),
|
||||
(3, '2023-01-01 10:45:00', 'logout', 3000),
|
||||
(4, '2023-01-01 11:05:00', 'login', 4000),
|
||||
(5, '2023-01-01 11:20:00', 'view', 5000),
|
||||
(6, '2023-01-01 11:35:00', 'purchase', 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
INSERT INTO time_windows VALUES
|
||||
(1, '2023-01-01 10:00:00', '2023-01-01 10:30:00', 'Morning Early', 1000),
|
||||
(2, '2023-01-01 10:30:00', '2023-01-01 11:00:00', 'Morning Late', 2000),
|
||||
(3, '2023-01-01 11:00:00', '2023-01-01 11:30:00', 'Noon Early', 3000),
|
||||
(4, '2023-01-01 11:30:00', '2023-01-01 12:00:00', 'Noon Late', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Range join: events within time windows
|
||||
SELECT
|
||||
e.event_id, e.event_time, e.event_type, w.window_name
|
||||
FROM time_events e
|
||||
INNER JOIN time_windows w
|
||||
ON e.event_time >= w.start_time AND e.event_time < w.end_time
|
||||
ORDER BY e.event_time;
|
||||
|
||||
+----------+---------------------+------------+---------------+
|
||||
| event_id | event_time | event_type | window_name |
|
||||
+----------+---------------------+------------+---------------+
|
||||
| 1 | 2023-01-01T10:15:00 | login | Morning Early |
|
||||
| 2 | 2023-01-01T10:30:00 | purchase | Morning Late |
|
||||
| 3 | 2023-01-01T10:45:00 | logout | Morning Late |
|
||||
| 4 | 2023-01-01T11:05:00 | login | Noon Early |
|
||||
| 5 | 2023-01-01T11:20:00 | view | Noon Early |
|
||||
| 6 | 2023-01-01T11:35:00 | purchase | Noon Late |
|
||||
+----------+---------------------+------------+---------------+
|
||||
|
||||
-- Inequality join with additional conditions
|
||||
SELECT
|
||||
e.event_id, e.event_type, w.window_name
|
||||
FROM time_events e
|
||||
INNER JOIN time_windows w
|
||||
ON e.event_time >= w.start_time
|
||||
AND e.event_time < w.end_time
|
||||
AND e.event_type = 'purchase'
|
||||
ORDER BY e.event_time;
|
||||
|
||||
+----------+------------+--------------+
|
||||
| event_id | event_type | window_name |
|
||||
+----------+------------+--------------+
|
||||
| 2 | purchase | Morning Late |
|
||||
| 6 | purchase | Noon Late |
|
||||
+----------+------------+--------------+
|
||||
|
||||
-- Cross-time analysis with inequality joins
|
||||
SELECT
|
||||
e1.event_id as first_event, e2.event_id as second_event,
|
||||
e1.event_type as first_type, e2.event_type as second_type,
|
||||
e2.event_time - e1.event_time as time_diff
|
||||
FROM time_events e1
|
||||
INNER JOIN time_events e2
|
||||
ON e1.event_time < e2.event_time
|
||||
AND e2.event_time - e1.event_time <= INTERVAL '30 minutes'
|
||||
ORDER BY e1.event_time, e2.event_time;
|
||||
|
||||
+-------------+--------------+------------+-------------+-----------+
|
||||
| first_event | second_event | first_type | second_type | time_diff |
|
||||
+-------------+--------------+------------+-------------+-----------+
|
||||
| 1 | 2 | login | purchase | PT900S |
|
||||
| 1 | 3 | login | logout | PT1800S |
|
||||
| 2 | 3 | purchase | logout | PT900S |
|
||||
| 3 | 4 | logout | login | PT1200S |
|
||||
| 4 | 5 | login | view | PT900S |
|
||||
| 4 | 6 | login | purchase | PT1800S |
|
||||
| 5 | 6 | view | purchase | PT900S |
|
||||
+-------------+--------------+------------+-------------+-----------+
|
||||
|
||||
CREATE TABLE price_history(item_id INTEGER, price DOUBLE, effective_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE orders_ineq(order_id INTEGER, item_id INTEGER, order_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO price_history VALUES
|
||||
(1, 100.00, '2023-01-01', 1000), (1, 110.00, '2023-01-15', 2000),
|
||||
(2, 50.00, '2023-01-01', 3000), (2, 55.00, '2023-01-20', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO orders_ineq VALUES
|
||||
(1, 1, '2023-01-10', 1000), (2, 1, '2023-01-20', 2000),
|
||||
(3, 2, '2023-01-05', 3000), (4, 2, '2023-01-25', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Historical price lookup with inequality join
|
||||
SELECT
|
||||
o.order_id, o.order_date, p.price, p.effective_date
|
||||
FROM orders_ineq o
|
||||
INNER JOIN price_history p
|
||||
ON o.item_id = p.item_id
|
||||
AND o.order_date >= p.effective_date
|
||||
ORDER BY o.order_id;
|
||||
|
||||
+----------+------------+-------+----------------+
|
||||
| order_id | order_date | price | effective_date |
|
||||
+----------+------------+-------+----------------+
|
||||
| 1 | 2023-01-10 | 100.0 | 2023-01-01 |
|
||||
| 2 | 2023-01-20 | 100.0 | 2023-01-01 |
|
||||
| 2 | 2023-01-20 | 110.0 | 2023-01-15 |
|
||||
| 3 | 2023-01-05 | 50.0 | 2023-01-01 |
|
||||
| 4 | 2023-01-25 | 50.0 | 2023-01-01 |
|
||||
| 4 | 2023-01-25 | 55.0 | 2023-01-20 |
|
||||
+----------+------------+-------+----------------+
|
||||
|
||||
-- Latest price before order date
|
||||
SELECT
|
||||
o.order_id, o.order_date, latest_price.price
|
||||
FROM orders_ineq o
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
item_id,
|
||||
price,
|
||||
effective_date,
|
||||
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY effective_date DESC) as rn
|
||||
FROM price_history
|
||||
) latest_price
|
||||
ON o.item_id = latest_price.item_id
|
||||
AND o.order_date >= latest_price.effective_date
|
||||
AND latest_price.rn = 1
|
||||
ORDER BY o.order_id;
|
||||
|
||||
+----------+------------+-------+
|
||||
| order_id | order_date | price |
|
||||
+----------+------------+-------+
|
||||
| 2 | 2023-01-20 | 110.0 |
|
||||
| 4 | 2023-01-25 | 55.0 |
|
||||
+----------+------------+-------+
|
||||
|
||||
DROP TABLE time_events;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE time_windows;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE price_history;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE orders_ineq;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
94
tests/cases/standalone/common/join/inequality_joins.sql
Normal file
94
tests/cases/standalone/common/join/inequality_joins.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/iejoin/ inequality join tests
|
||||
-- Tests inequality join conditions
|
||||
|
||||
CREATE TABLE time_events(event_id INTEGER, event_time TIMESTAMP, event_type VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE time_windows(window_id INTEGER, start_time TIMESTAMP, end_time TIMESTAMP, window_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO time_events VALUES
|
||||
(1, '2023-01-01 10:15:00', 'login', 1000),
|
||||
(2, '2023-01-01 10:30:00', 'purchase', 2000),
|
||||
(3, '2023-01-01 10:45:00', 'logout', 3000),
|
||||
(4, '2023-01-01 11:05:00', 'login', 4000),
|
||||
(5, '2023-01-01 11:20:00', 'view', 5000),
|
||||
(6, '2023-01-01 11:35:00', 'purchase', 6000);
|
||||
|
||||
INSERT INTO time_windows VALUES
|
||||
(1, '2023-01-01 10:00:00', '2023-01-01 10:30:00', 'Morning Early', 1000),
|
||||
(2, '2023-01-01 10:30:00', '2023-01-01 11:00:00', 'Morning Late', 2000),
|
||||
(3, '2023-01-01 11:00:00', '2023-01-01 11:30:00', 'Noon Early', 3000),
|
||||
(4, '2023-01-01 11:30:00', '2023-01-01 12:00:00', 'Noon Late', 4000);
|
||||
|
||||
-- Range join: events within time windows
|
||||
SELECT
|
||||
e.event_id, e.event_time, e.event_type, w.window_name
|
||||
FROM time_events e
|
||||
INNER JOIN time_windows w
|
||||
ON e.event_time >= w.start_time AND e.event_time < w.end_time
|
||||
ORDER BY e.event_time;
|
||||
|
||||
-- Inequality join with additional conditions
|
||||
SELECT
|
||||
e.event_id, e.event_type, w.window_name
|
||||
FROM time_events e
|
||||
INNER JOIN time_windows w
|
||||
ON e.event_time >= w.start_time
|
||||
AND e.event_time < w.end_time
|
||||
AND e.event_type = 'purchase'
|
||||
ORDER BY e.event_time;
|
||||
|
||||
-- Cross-time analysis with inequality joins
|
||||
SELECT
|
||||
e1.event_id as first_event, e2.event_id as second_event,
|
||||
e1.event_type as first_type, e2.event_type as second_type,
|
||||
e2.event_time - e1.event_time as time_diff
|
||||
FROM time_events e1
|
||||
INNER JOIN time_events e2
|
||||
ON e1.event_time < e2.event_time
|
||||
AND e2.event_time - e1.event_time <= INTERVAL '30 minutes'
|
||||
ORDER BY e1.event_time, e2.event_time;
|
||||
|
||||
CREATE TABLE price_history(item_id INTEGER, price DOUBLE, effective_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
CREATE TABLE orders_ineq(order_id INTEGER, item_id INTEGER, order_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO price_history VALUES
|
||||
(1, 100.00, '2023-01-01', 1000), (1, 110.00, '2023-01-15', 2000),
|
||||
(2, 50.00, '2023-01-01', 3000), (2, 55.00, '2023-01-20', 4000);
|
||||
|
||||
INSERT INTO orders_ineq VALUES
|
||||
(1, 1, '2023-01-10', 1000), (2, 1, '2023-01-20', 2000),
|
||||
(3, 2, '2023-01-05', 3000), (4, 2, '2023-01-25', 4000);
|
||||
|
||||
-- Historical price lookup with inequality join
|
||||
SELECT
|
||||
o.order_id, o.order_date, p.price, p.effective_date
|
||||
FROM orders_ineq o
|
||||
INNER JOIN price_history p
|
||||
ON o.item_id = p.item_id
|
||||
AND o.order_date >= p.effective_date
|
||||
ORDER BY o.order_id;
|
||||
|
||||
-- Latest price before order date
|
||||
SELECT
|
||||
o.order_id, o.order_date, latest_price.price
|
||||
FROM orders_ineq o
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
item_id,
|
||||
price,
|
||||
effective_date,
|
||||
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY effective_date DESC) as rn
|
||||
FROM price_history
|
||||
) latest_price
|
||||
ON o.item_id = latest_price.item_id
|
||||
AND o.order_date >= latest_price.effective_date
|
||||
AND latest_price.rn = 1
|
||||
ORDER BY o.order_id;
|
||||
|
||||
DROP TABLE time_events;
|
||||
|
||||
DROP TABLE time_windows;
|
||||
|
||||
DROP TABLE price_history;
|
||||
|
||||
DROP TABLE orders_ineq;
|
||||
167
tests/cases/standalone/common/join/inner_join_advanced.result
Normal file
167
tests/cases/standalone/common/join/inner_join_advanced.result
Normal file
@@ -0,0 +1,167 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/ advanced tests
|
||||
-- Tests advanced inner join patterns
|
||||
CREATE TABLE customers(cust_id INTEGER, cust_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE orders(order_id INTEGER, cust_id INTEGER, order_date DATE, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE order_items(item_id INTEGER, order_id INTEGER, product VARCHAR, quantity INTEGER, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO customers VALUES
|
||||
(1, 'John', 'NYC', 1000), (2, 'Jane', 'LA', 2000), (3, 'Bob', 'Chicago', 3000), (4, 'Alice', 'NYC', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO orders VALUES
|
||||
(101, 1, '2023-01-01', 250.00, 1000), (102, 2, '2023-01-02', 180.00, 2000),
|
||||
(103, 1, '2023-01-03', 420.00, 3000), (104, 3, '2023-01-04', 95.00, 4000),
|
||||
(105, 4, '2023-01-05', 310.00, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO order_items VALUES
|
||||
(1, 101, 'Widget', 2, 125.00, 1000), (2, 101, 'Gadget', 1, 0.00, 2000),
|
||||
(3, 102, 'Tool', 3, 60.00, 3000), (4, 103, 'Device', 1, 420.00, 4000),
|
||||
(5, 104, 'Part', 5, 19.00, 5000), (6, 105, 'Component', 2, 155.00, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
-- Multi-table inner join
|
||||
SELECT
|
||||
c.cust_name, c.city, o.order_id, o.order_date, o.amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
ORDER BY o.order_date, c.cust_name;
|
||||
|
||||
+-----------+---------+----------+------------+--------+
|
||||
| cust_name | city | order_id | order_date | amount |
|
||||
+-----------+---------+----------+------------+--------+
|
||||
| John | NYC | 101 | 2023-01-01 | 250.0 |
|
||||
| Jane | LA | 102 | 2023-01-02 | 180.0 |
|
||||
| John | NYC | 103 | 2023-01-03 | 420.0 |
|
||||
| Bob | Chicago | 104 | 2023-01-04 | 95.0 |
|
||||
| Alice | NYC | 105 | 2023-01-05 | 310.0 |
|
||||
+-----------+---------+----------+------------+--------+
|
||||
|
||||
-- Three-way inner join
|
||||
SELECT
|
||||
c.cust_name, o.order_id, oi.product, oi.quantity, oi.price
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
INNER JOIN order_items oi ON o.order_id = oi.order_id
|
||||
ORDER BY c.cust_name, o.order_id, oi.product;
|
||||
|
||||
+-----------+----------+-----------+----------+-------+
|
||||
| cust_name | order_id | product | quantity | price |
|
||||
+-----------+----------+-----------+----------+-------+
|
||||
| Alice | 105 | Component | 2 | 155.0 |
|
||||
| Bob | 104 | Part | 5 | 19.0 |
|
||||
| Jane | 102 | Tool | 3 | 60.0 |
|
||||
| John | 101 | Gadget | 1 | 0.0 |
|
||||
| John | 101 | Widget | 2 | 125.0 |
|
||||
| John | 103 | Device | 1 | 420.0 |
|
||||
+-----------+----------+-----------+----------+-------+
|
||||
|
||||
-- Inner join with complex conditions
|
||||
SELECT
|
||||
c.cust_name, o.order_id, o.amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id AND o.amount > 200.00
|
||||
ORDER BY o.amount DESC;
|
||||
|
||||
+-----------+----------+--------+
|
||||
| cust_name | order_id | amount |
|
||||
+-----------+----------+--------+
|
||||
| John | 103 | 420.0 |
|
||||
| Alice | 105 | 310.0 |
|
||||
| John | 101 | 250.0 |
|
||||
+-----------+----------+--------+
|
||||
|
||||
-- Inner join with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
COUNT(o.order_id) as total_orders,
|
||||
SUM(o.amount) as total_amount,
|
||||
AVG(o.amount) as avg_order_amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
GROUP BY c.city
|
||||
ORDER BY total_amount DESC;
|
||||
|
||||
+---------+--------------+--------------+-------------------+
|
||||
| city | total_orders | total_amount | avg_order_amount |
|
||||
+---------+--------------+--------------+-------------------+
|
||||
| NYC | 3 | 980.0 | 326.6666666666667 |
|
||||
| LA | 1 | 180.0 | 180.0 |
|
||||
| Chicago | 1 | 95.0 | 95.0 |
|
||||
+---------+--------------+--------------+-------------------+
|
||||
|
||||
-- Self join
|
||||
SELECT
|
||||
o1.order_id as order1, o2.order_id as order2, o1.amount, o2.amount
|
||||
FROM orders o1
|
||||
INNER JOIN orders o2 ON o1.cust_id = o2.cust_id AND o1.order_id < o2.order_id
|
||||
ORDER BY o1.order_id, o2.order_id;
|
||||
|
||||
+--------+--------+--------+--------+
|
||||
| order1 | order2 | amount | amount |
|
||||
+--------+--------+--------+--------+
|
||||
| 101 | 103 | 250.0 | 420.0 |
|
||||
+--------+--------+--------+--------+
|
||||
|
||||
-- Join with subquery
|
||||
SELECT
|
||||
c.cust_name, high_orders.total_amount
|
||||
FROM customers c
|
||||
INNER JOIN (
|
||||
SELECT cust_id, SUM(amount) as total_amount
|
||||
FROM orders
|
||||
GROUP BY cust_id
|
||||
HAVING SUM(amount) > 300
|
||||
) high_orders ON c.cust_id = high_orders.cust_id
|
||||
ORDER BY high_orders.total_amount DESC;
|
||||
|
||||
+-----------+--------------+
|
||||
| cust_name | total_amount |
|
||||
+-----------+--------------+
|
||||
| John | 670.0 |
|
||||
| Alice | 310.0 |
|
||||
+-----------+--------------+
|
||||
|
||||
-- Join with window functions
|
||||
SELECT
|
||||
c.cust_name,
|
||||
o.order_id,
|
||||
o.amount,
|
||||
ROW_NUMBER() OVER (PARTITION BY c.cust_id ORDER BY o.order_date) as order_sequence
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
ORDER BY c.cust_name, order_sequence;
|
||||
|
||||
+-----------+----------+--------+----------------+
|
||||
| cust_name | order_id | amount | order_sequence |
|
||||
+-----------+----------+--------+----------------+
|
||||
| Alice | 105 | 310.0 | 1 |
|
||||
| Bob | 104 | 95.0 | 1 |
|
||||
| Jane | 102 | 180.0 | 1 |
|
||||
| John | 101 | 250.0 | 1 |
|
||||
| John | 103 | 420.0 | 2 |
|
||||
+-----------+----------+--------+----------------+
|
||||
|
||||
DROP TABLE customers;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE orders;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE order_items;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
87
tests/cases/standalone/common/join/inner_join_advanced.sql
Normal file
87
tests/cases/standalone/common/join/inner_join_advanced.sql
Normal file
@@ -0,0 +1,87 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/ advanced tests
|
||||
-- Tests advanced inner join patterns
|
||||
|
||||
CREATE TABLE customers(cust_id INTEGER, cust_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
CREATE TABLE orders(order_id INTEGER, cust_id INTEGER, order_date DATE, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
CREATE TABLE order_items(item_id INTEGER, order_id INTEGER, product VARCHAR, quantity INTEGER, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO customers VALUES
|
||||
(1, 'John', 'NYC', 1000), (2, 'Jane', 'LA', 2000), (3, 'Bob', 'Chicago', 3000), (4, 'Alice', 'NYC', 4000);
|
||||
|
||||
INSERT INTO orders VALUES
|
||||
(101, 1, '2023-01-01', 250.00, 1000), (102, 2, '2023-01-02', 180.00, 2000),
|
||||
(103, 1, '2023-01-03', 420.00, 3000), (104, 3, '2023-01-04', 95.00, 4000),
|
||||
(105, 4, '2023-01-05', 310.00, 5000);
|
||||
|
||||
INSERT INTO order_items VALUES
|
||||
(1, 101, 'Widget', 2, 125.00, 1000), (2, 101, 'Gadget', 1, 0.00, 2000),
|
||||
(3, 102, 'Tool', 3, 60.00, 3000), (4, 103, 'Device', 1, 420.00, 4000),
|
||||
(5, 104, 'Part', 5, 19.00, 5000), (6, 105, 'Component', 2, 155.00, 6000);
|
||||
|
||||
-- Multi-table inner join
|
||||
SELECT
|
||||
c.cust_name, c.city, o.order_id, o.order_date, o.amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
ORDER BY o.order_date, c.cust_name;
|
||||
|
||||
-- Three-way inner join
|
||||
SELECT
|
||||
c.cust_name, o.order_id, oi.product, oi.quantity, oi.price
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
INNER JOIN order_items oi ON o.order_id = oi.order_id
|
||||
ORDER BY c.cust_name, o.order_id, oi.product;
|
||||
|
||||
-- Inner join with complex conditions
|
||||
SELECT
|
||||
c.cust_name, o.order_id, o.amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id AND o.amount > 200.00
|
||||
ORDER BY o.amount DESC;
|
||||
|
||||
-- Inner join with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
COUNT(o.order_id) as total_orders,
|
||||
SUM(o.amount) as total_amount,
|
||||
AVG(o.amount) as avg_order_amount
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
GROUP BY c.city
|
||||
ORDER BY total_amount DESC;
|
||||
|
||||
-- Self join
|
||||
SELECT
|
||||
o1.order_id as order1, o2.order_id as order2, o1.amount, o2.amount
|
||||
FROM orders o1
|
||||
INNER JOIN orders o2 ON o1.cust_id = o2.cust_id AND o1.order_id < o2.order_id
|
||||
ORDER BY o1.order_id, o2.order_id;
|
||||
|
||||
-- Join with subquery
|
||||
SELECT
|
||||
c.cust_name, high_orders.total_amount
|
||||
FROM customers c
|
||||
INNER JOIN (
|
||||
SELECT cust_id, SUM(amount) as total_amount
|
||||
FROM orders
|
||||
GROUP BY cust_id
|
||||
HAVING SUM(amount) > 300
|
||||
) high_orders ON c.cust_id = high_orders.cust_id
|
||||
ORDER BY high_orders.total_amount DESC;
|
||||
|
||||
-- Join with window functions
|
||||
SELECT
|
||||
c.cust_name,
|
||||
o.order_id,
|
||||
o.amount,
|
||||
ROW_NUMBER() OVER (PARTITION BY c.cust_id ORDER BY o.order_date) as order_sequence
|
||||
FROM customers c
|
||||
INNER JOIN orders o ON c.cust_id = o.cust_id
|
||||
ORDER BY c.cust_name, order_sequence;
|
||||
|
||||
DROP TABLE customers;
|
||||
|
||||
DROP TABLE orders;
|
||||
|
||||
DROP TABLE order_items;
|
||||
@@ -0,0 +1,162 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ complex condition tests
|
||||
-- Tests complex join conditions and predicates
|
||||
CREATE TABLE sales_reps(rep_id INTEGER, "name" VARCHAR, region VARCHAR, quota INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE customer_accounts(account_id INTEGER, account_name VARCHAR, region VARCHAR, rep_id INTEGER, revenue INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO sales_reps VALUES
|
||||
(1, 'Tom', 'North', 100000, 1000), (2, 'Sarah', 'South', 150000, 2000),
|
||||
(3, 'Mike', 'East', 120000, 3000), (4, 'Lisa', 'West', 180000, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO customer_accounts VALUES
|
||||
(101, 'TechCorp', 'North', 1, 85000, 1000), (102, 'DataInc', 'South', 2, 195000, 2000),
|
||||
(103, 'CloudSys', 'North', 1, 110000, 3000), (104, 'NetSoft', 'East', 3, 75000, 4000),
|
||||
(105, 'WebCo', 'West', 4, 225000, 5000), (106, 'AppDev', 'South', 2, 140000, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
-- Join with multiple conditions
|
||||
SELECT
|
||||
sr."name" as rep_name, ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id AND sr.region = ca.region
|
||||
ORDER BY sr.rep_id, ca.revenue DESC;
|
||||
|
||||
+----------+--------------+---------+
|
||||
| rep_name | account_name | revenue |
|
||||
+----------+--------------+---------+
|
||||
| Tom | CloudSys | 110000 |
|
||||
| Tom | TechCorp | 85000 |
|
||||
| Sarah | DataInc | 195000 |
|
||||
| Sarah | AppDev | 140000 |
|
||||
| Mike | NetSoft | 75000 |
|
||||
| Lisa | WebCo | 225000 |
|
||||
+----------+--------------+---------+
|
||||
|
||||
-- Join with inequality conditions
|
||||
SELECT
|
||||
sr."name", sr.quota, ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id AND ca.revenue < sr.quota
|
||||
ORDER BY sr.rep_id, ca.revenue;
|
||||
|
||||
+-------+--------+--------------+---------+
|
||||
| name | quota | account_name | revenue |
|
||||
+-------+--------+--------------+---------+
|
||||
| Tom | 100000 | TechCorp | 85000 |
|
||||
| Sarah | 150000 | AppDev | 140000 |
|
||||
| Mike | 120000 | NetSoft | 75000 |
|
||||
+-------+--------+--------------+---------+
|
||||
|
||||
-- Join with range conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue, sr.quota
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND ca.revenue BETWEEN sr.quota * 0.5 AND sr.quota * 1.5
|
||||
ORDER BY sr.rep_id, ca.revenue;
|
||||
|
||||
+-------+--------------+---------+--------+
|
||||
| name | account_name | revenue | quota |
|
||||
+-------+--------------+---------+--------+
|
||||
| Tom | TechCorp | 85000 | 100000 |
|
||||
| Tom | CloudSys | 110000 | 100000 |
|
||||
| Sarah | AppDev | 140000 | 150000 |
|
||||
| Sarah | DataInc | 195000 | 150000 |
|
||||
| Mike | NetSoft | 75000 | 120000 |
|
||||
| Lisa | WebCo | 225000 | 180000 |
|
||||
+-------+--------------+---------+--------+
|
||||
|
||||
-- Join with CASE in conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue,
|
||||
CASE WHEN ca.revenue >= sr.quota THEN 'Met Quota' ELSE 'Below Quota' END as performance
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca ON sr.rep_id = ca.rep_id
|
||||
ORDER BY sr.rep_id, ca.revenue DESC;
|
||||
|
||||
+-------+--------------+---------+-------------+
|
||||
| name | account_name | revenue | performance |
|
||||
+-------+--------------+---------+-------------+
|
||||
| Tom | CloudSys | 110000 | Met Quota |
|
||||
| Tom | TechCorp | 85000 | Below Quota |
|
||||
| Sarah | DataInc | 195000 | Met Quota |
|
||||
| Sarah | AppDev | 140000 | Below Quota |
|
||||
| Mike | NetSoft | 75000 | Below Quota |
|
||||
| Lisa | WebCo | 225000 | Met Quota |
|
||||
+-------+--------------+---------+-------------+
|
||||
|
||||
-- Join with expression conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name,
|
||||
ca.revenue, sr.quota,
|
||||
ca.revenue - sr.quota as quota_diff
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND UPPER(sr.region) = UPPER(ca.region)
|
||||
ORDER BY quota_diff DESC, sr."name" ASC;
|
||||
|
||||
+-------+--------------+---------+--------+------------+
|
||||
| name | account_name | revenue | quota | quota_diff |
|
||||
+-------+--------------+---------+--------+------------+
|
||||
| Lisa | WebCo | 225000 | 180000 | 45000 |
|
||||
| Sarah | DataInc | 195000 | 150000 | 45000 |
|
||||
| Tom | CloudSys | 110000 | 100000 | 10000 |
|
||||
| Sarah | AppDev | 140000 | 150000 | -10000 |
|
||||
| Tom | TechCorp | 85000 | 100000 | -15000 |
|
||||
| Mike | NetSoft | 75000 | 120000 | -45000 |
|
||||
+-------+--------------+---------+--------+------------+
|
||||
|
||||
-- Join with string pattern conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND ca.account_name LIKE '%Corp%'
|
||||
ORDER BY sr."name";
|
||||
|
||||
+------+--------------+
|
||||
| name | account_name |
|
||||
+------+--------------+
|
||||
| Tom | TechCorp |
|
||||
+------+--------------+
|
||||
|
||||
-- Complex nested join conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca ON (
|
||||
sr.rep_id = ca.rep_id
|
||||
AND (ca.revenue > 100000 OR sr.quota < 130000)
|
||||
AND sr.region IN ('North', 'South')
|
||||
)
|
||||
ORDER BY ca.revenue DESC;
|
||||
|
||||
+-------+--------------+---------+
|
||||
| name | account_name | revenue |
|
||||
+-------+--------------+---------+
|
||||
| Sarah | DataInc | 195000 |
|
||||
| Sarah | AppDev | 140000 |
|
||||
| Tom | CloudSys | 110000 |
|
||||
| Tom | TechCorp | 85000 |
|
||||
+-------+--------------+---------+
|
||||
|
||||
DROP TABLE sales_reps;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE customer_accounts;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ complex condition tests
|
||||
-- Tests complex join conditions and predicates
|
||||
|
||||
CREATE TABLE sales_reps(rep_id INTEGER, "name" VARCHAR, region VARCHAR, quota INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE customer_accounts(account_id INTEGER, account_name VARCHAR, region VARCHAR, rep_id INTEGER, revenue INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO sales_reps VALUES
|
||||
(1, 'Tom', 'North', 100000, 1000), (2, 'Sarah', 'South', 150000, 2000),
|
||||
(3, 'Mike', 'East', 120000, 3000), (4, 'Lisa', 'West', 180000, 4000);
|
||||
|
||||
INSERT INTO customer_accounts VALUES
|
||||
(101, 'TechCorp', 'North', 1, 85000, 1000), (102, 'DataInc', 'South', 2, 195000, 2000),
|
||||
(103, 'CloudSys', 'North', 1, 110000, 3000), (104, 'NetSoft', 'East', 3, 75000, 4000),
|
||||
(105, 'WebCo', 'West', 4, 225000, 5000), (106, 'AppDev', 'South', 2, 140000, 6000);
|
||||
|
||||
-- Join with multiple conditions
|
||||
SELECT
|
||||
sr."name" as rep_name, ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id AND sr.region = ca.region
|
||||
ORDER BY sr.rep_id, ca.revenue DESC;
|
||||
|
||||
-- Join with inequality conditions
|
||||
SELECT
|
||||
sr."name", sr.quota, ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id AND ca.revenue < sr.quota
|
||||
ORDER BY sr.rep_id, ca.revenue;
|
||||
|
||||
-- Join with range conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue, sr.quota
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND ca.revenue BETWEEN sr.quota * 0.5 AND sr.quota * 1.5
|
||||
ORDER BY sr.rep_id, ca.revenue;
|
||||
|
||||
-- Join with CASE in conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue,
|
||||
CASE WHEN ca.revenue >= sr.quota THEN 'Met Quota' ELSE 'Below Quota' END as performance
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca ON sr.rep_id = ca.rep_id
|
||||
ORDER BY sr.rep_id, ca.revenue DESC;
|
||||
|
||||
-- Join with expression conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name,
|
||||
ca.revenue, sr.quota,
|
||||
ca.revenue - sr.quota as quota_diff
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND UPPER(sr.region) = UPPER(ca.region)
|
||||
ORDER BY quota_diff DESC, sr."name" ASC;
|
||||
|
||||
-- Join with string pattern conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca
|
||||
ON sr.rep_id = ca.rep_id
|
||||
AND ca.account_name LIKE '%Corp%'
|
||||
ORDER BY sr."name";
|
||||
|
||||
-- Complex nested join conditions
|
||||
SELECT
|
||||
sr."name", ca.account_name, ca.revenue
|
||||
FROM sales_reps sr
|
||||
INNER JOIN customer_accounts ca ON (
|
||||
sr.rep_id = ca.rep_id
|
||||
AND (ca.revenue > 100000 OR sr.quota < 130000)
|
||||
AND sr.region IN ('North', 'South')
|
||||
)
|
||||
ORDER BY ca.revenue DESC;
|
||||
|
||||
DROP TABLE sales_reps;
|
||||
|
||||
DROP TABLE customer_accounts;
|
||||
53
tests/cases/standalone/common/join/join_distinct.result
Normal file
53
tests/cases/standalone/common/join/join_distinct.result
Normal file
@@ -0,0 +1,53 @@
|
||||
-- Tests joins with DISTINCT operations
|
||||
CREATE TABLE products_dist(prod_id INTEGER, prod_name VARCHAR, category VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE sales_dist(sale_id INTEGER, prod_id INTEGER, customer VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO products_dist VALUES (1, 'Widget', 'Tools', 1000), (2, 'Gadget', 'Electronics', 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO sales_dist VALUES (1, 1, 'Alice', 1000), (2, 1, 'Bob', 2000), (3, 2, 'Alice', 3000), (4, 1, 'Alice', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
SELECT DISTINCT p.category FROM products_dist p INNER JOIN sales_dist s ON p.prod_id = s.prod_id ORDER BY p.category;
|
||||
|
||||
+-------------+
|
||||
| category |
|
||||
+-------------+
|
||||
| Electronics |
|
||||
| Tools |
|
||||
+-------------+
|
||||
|
||||
SELECT DISTINCT s.customer, p.category FROM sales_dist s INNER JOIN products_dist p ON s.prod_id = p.prod_id ORDER BY s.customer, p.category;
|
||||
|
||||
+----------+-------------+
|
||||
| customer | category |
|
||||
+----------+-------------+
|
||||
| Alice | Electronics |
|
||||
| Alice | Tools |
|
||||
| Bob | Tools |
|
||||
+----------+-------------+
|
||||
|
||||
SELECT p.prod_name, COUNT(DISTINCT s.customer) as unique_customers FROM products_dist p LEFT JOIN sales_dist s ON p.prod_id = s.prod_id GROUP BY p.prod_id, p.prod_name ORDER BY unique_customers DESC;
|
||||
|
||||
+-----------+------------------+
|
||||
| prod_name | unique_customers |
|
||||
+-----------+------------------+
|
||||
| Widget | 2 |
|
||||
| Gadget | 1 |
|
||||
+-----------+------------------+
|
||||
|
||||
DROP TABLE products_dist;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE sales_dist;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
18
tests/cases/standalone/common/join/join_distinct.sql
Normal file
18
tests/cases/standalone/common/join/join_distinct.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Tests joins with DISTINCT operations
|
||||
|
||||
CREATE TABLE products_dist(prod_id INTEGER, prod_name VARCHAR, category VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE sales_dist(sale_id INTEGER, prod_id INTEGER, customer VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO products_dist VALUES (1, 'Widget', 'Tools', 1000), (2, 'Gadget', 'Electronics', 2000);
|
||||
|
||||
INSERT INTO sales_dist VALUES (1, 1, 'Alice', 1000), (2, 1, 'Bob', 2000), (3, 2, 'Alice', 3000), (4, 1, 'Alice', 4000);
|
||||
|
||||
SELECT DISTINCT p.category FROM products_dist p INNER JOIN sales_dist s ON p.prod_id = s.prod_id ORDER BY p.category;
|
||||
|
||||
SELECT DISTINCT s.customer, p.category FROM sales_dist s INNER JOIN products_dist p ON s.prod_id = p.prod_id ORDER BY s.customer, p.category;
|
||||
SELECT p.prod_name, COUNT(DISTINCT s.customer) as unique_customers FROM products_dist p LEFT JOIN sales_dist s ON p.prod_id = s.prod_id GROUP BY p.prod_id, p.prod_name ORDER BY unique_customers DESC;
|
||||
|
||||
DROP TABLE products_dist;
|
||||
|
||||
DROP TABLE sales_dist;
|
||||
62
tests/cases/standalone/common/join/join_edge_cases.result
Normal file
62
tests/cases/standalone/common/join/join_edge_cases.result
Normal file
@@ -0,0 +1,62 @@
|
||||
-- Tests join edge cases and special scenarios
|
||||
CREATE TABLE empty_table("id" INTEGER, "value" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE single_row("id" INTEGER, "data" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE duplicate_keys("id" INTEGER, "description" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO single_row VALUES (1, 'only_row', 1000);
|
||||
|
||||
Affected Rows: 1
|
||||
|
||||
INSERT INTO duplicate_keys VALUES (1, 'first', 1000), (1, 'second', 2000), (2, 'third', 3000), (2, 'fourth', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Join with empty table
|
||||
SELECT s."id", s."data", e."value" FROM single_row s LEFT JOIN empty_table e ON s."id" = e."id" ORDER BY s."id";
|
||||
|
||||
+----+----------+-------+
|
||||
| id | data | value |
|
||||
+----+----------+-------+
|
||||
| 1 | only_row | |
|
||||
+----+----------+-------+
|
||||
|
||||
-- Join with duplicate keys
|
||||
SELECT s."id", s."data", d."description" FROM single_row s LEFT JOIN duplicate_keys d ON s."id" = d."id" ORDER BY d."description";
|
||||
|
||||
+----+----------+-------------+
|
||||
| id | data | description |
|
||||
+----+----------+-------------+
|
||||
| 1 | only_row | first |
|
||||
| 1 | only_row | second |
|
||||
+----+----------+-------------+
|
||||
|
||||
-- Self-join with duplicates
|
||||
SELECT d1."description" as desc1, d2."description" as desc2 FROM duplicate_keys d1 INNER JOIN duplicate_keys d2 ON d1."id" = d2."id" AND d1.ts < d2.ts ORDER BY d1.ts, d2.ts;
|
||||
|
||||
+-------+--------+
|
||||
| desc1 | desc2 |
|
||||
+-------+--------+
|
||||
| first | second |
|
||||
| third | fourth |
|
||||
+-------+--------+
|
||||
|
||||
DROP TABLE empty_table;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE single_row;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE duplicate_keys;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
26
tests/cases/standalone/common/join/join_edge_cases.sql
Normal file
26
tests/cases/standalone/common/join/join_edge_cases.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Tests join edge cases and special scenarios
|
||||
|
||||
CREATE TABLE empty_table("id" INTEGER, "value" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE single_row("id" INTEGER, "data" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE duplicate_keys("id" INTEGER, "description" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO single_row VALUES (1, 'only_row', 1000);
|
||||
|
||||
INSERT INTO duplicate_keys VALUES (1, 'first', 1000), (1, 'second', 2000), (2, 'third', 3000), (2, 'fourth', 4000);
|
||||
|
||||
-- Join with empty table
|
||||
SELECT s."id", s."data", e."value" FROM single_row s LEFT JOIN empty_table e ON s."id" = e."id" ORDER BY s."id";
|
||||
|
||||
-- Join with duplicate keys
|
||||
SELECT s."id", s."data", d."description" FROM single_row s LEFT JOIN duplicate_keys d ON s."id" = d."id" ORDER BY d."description";
|
||||
|
||||
-- Self-join with duplicates
|
||||
SELECT d1."description" as desc1, d2."description" as desc2 FROM duplicate_keys d1 INNER JOIN duplicate_keys d2 ON d1."id" = d2."id" AND d1.ts < d2.ts ORDER BY d1.ts, d2.ts;
|
||||
|
||||
DROP TABLE empty_table;
|
||||
|
||||
DROP TABLE single_row;
|
||||
|
||||
DROP TABLE duplicate_keys;
|
||||
51
tests/cases/standalone/common/join/join_large_tables.result
Normal file
51
tests/cases/standalone/common/join/join_large_tables.result
Normal file
@@ -0,0 +1,51 @@
|
||||
-- Tests joins with larger data sets
|
||||
CREATE TABLE log_entries(log_id INTEGER, user_id INTEGER, "action" VARCHAR, timestamp_val BIGINT, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE user_profiles(user_id INTEGER, username VARCHAR, signup_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO log_entries VALUES
|
||||
(1, 1, 'login', 1700000000, 1000), (2, 1, 'view_page', 1700000060, 2000), (3, 2, 'login', 1700000120, 3000),
|
||||
(4, 2, 'purchase', 1700000180, 4000), (5, 3, 'login', 1700000240, 5000), (6, 1, 'logout', 1700000300, 6000),
|
||||
(7, 3, 'view_page', 1700000360, 7000), (8, 2, 'logout', 1700000420, 8000), (9, 3, 'purchase', 1700000480, 9000),
|
||||
(10, 1, 'view_page', 1700000540, 10000), (11, 2, 'view_page', 1700000600, 11000), (12, 3, 'logout', 1700000660, 12000);
|
||||
|
||||
Affected Rows: 12
|
||||
|
||||
INSERT INTO user_profiles VALUES
|
||||
(1, 'alice_user', '2022-01-15', 1000), (2, 'bob_user', '2022-03-20', 2000), (3, 'charlie_user', '2022-06-10', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
SELECT u.username, COUNT(l.log_id) as activity_count, COUNT(DISTINCT l.action) as unique_actions FROM user_profiles u LEFT JOIN log_entries l ON u.user_id = l.user_id GROUP BY u.user_id, u.username ORDER BY activity_count DESC, u.username DESC;
|
||||
|
||||
+--------------+----------------+----------------+
|
||||
| username | activity_count | unique_actions |
|
||||
+--------------+----------------+----------------+
|
||||
| charlie_user | 4 | 4 |
|
||||
| bob_user | 4 | 4 |
|
||||
| alice_user | 4 | 3 |
|
||||
+--------------+----------------+----------------+
|
||||
|
||||
SELECT l."action", COUNT(DISTINCT l.user_id) as unique_users, COUNT(*) as total_actions FROM log_entries l INNER JOIN user_profiles u ON l.user_id = u.user_id GROUP BY l."action" ORDER BY total_actions DESC, l."action" ASC;
|
||||
|
||||
+-----------+--------------+---------------+
|
||||
| action | unique_users | total_actions |
|
||||
+-----------+--------------+---------------+
|
||||
| view_page | 3 | 4 |
|
||||
| login | 3 | 3 |
|
||||
| logout | 3 | 3 |
|
||||
| purchase | 2 | 2 |
|
||||
+-----------+--------------+---------------+
|
||||
|
||||
DROP TABLE log_entries;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE user_profiles;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
22
tests/cases/standalone/common/join/join_large_tables.sql
Normal file
22
tests/cases/standalone/common/join/join_large_tables.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
-- Tests joins with larger data sets
|
||||
|
||||
CREATE TABLE log_entries(log_id INTEGER, user_id INTEGER, "action" VARCHAR, timestamp_val BIGINT, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE user_profiles(user_id INTEGER, username VARCHAR, signup_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO log_entries VALUES
|
||||
(1, 1, 'login', 1700000000, 1000), (2, 1, 'view_page', 1700000060, 2000), (3, 2, 'login', 1700000120, 3000),
|
||||
(4, 2, 'purchase', 1700000180, 4000), (5, 3, 'login', 1700000240, 5000), (6, 1, 'logout', 1700000300, 6000),
|
||||
(7, 3, 'view_page', 1700000360, 7000), (8, 2, 'logout', 1700000420, 8000), (9, 3, 'purchase', 1700000480, 9000),
|
||||
(10, 1, 'view_page', 1700000540, 10000), (11, 2, 'view_page', 1700000600, 11000), (12, 3, 'logout', 1700000660, 12000);
|
||||
|
||||
INSERT INTO user_profiles VALUES
|
||||
(1, 'alice_user', '2022-01-15', 1000), (2, 'bob_user', '2022-03-20', 2000), (3, 'charlie_user', '2022-06-10', 3000);
|
||||
|
||||
SELECT u.username, COUNT(l.log_id) as activity_count, COUNT(DISTINCT l.action) as unique_actions FROM user_profiles u LEFT JOIN log_entries l ON u.user_id = l.user_id GROUP BY u.user_id, u.username ORDER BY activity_count DESC, u.username DESC;
|
||||
|
||||
SELECT l."action", COUNT(DISTINCT l.user_id) as unique_users, COUNT(*) as total_actions FROM log_entries l INNER JOIN user_profiles u ON l.user_id = u.user_id GROUP BY l."action" ORDER BY total_actions DESC, l."action" ASC;
|
||||
|
||||
DROP TABLE log_entries;
|
||||
|
||||
DROP TABLE user_profiles;
|
||||
44
tests/cases/standalone/common/join/join_lateral.result
Normal file
44
tests/cases/standalone/common/join/join_lateral.result
Normal file
@@ -0,0 +1,44 @@
|
||||
-- Tests lateral join patterns and correlated subqueries
|
||||
CREATE TABLE departments_lat(dept_id INTEGER, dept_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE employees_lat(emp_id INTEGER, dept_id INTEGER, salary INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO departments_lat VALUES (1, 'Engineering', 1000), (2, 'Sales', 2000), (3, 'Marketing', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO employees_lat VALUES (1, 1, 75000, 1000), (2, 1, 80000, 2000), (3, 2, 65000, 3000), (4, 2, 70000, 4000), (5, 3, 60000, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
-- Correlated subquery simulating lateral join behavior
|
||||
SELECT d.dept_name, top_earners.emp_id, top_earners.salary
|
||||
FROM departments_lat d
|
||||
INNER JOIN (
|
||||
SELECT emp_id, dept_id, salary, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) as rn
|
||||
FROM employees_lat
|
||||
) top_earners ON d.dept_id = top_earners.dept_id AND top_earners.rn <= 2
|
||||
ORDER BY d.dept_id, top_earners.salary DESC;
|
||||
|
||||
+-------------+--------+--------+
|
||||
| dept_name | emp_id | salary |
|
||||
+-------------+--------+--------+
|
||||
| Engineering | 2 | 80000 |
|
||||
| Engineering | 1 | 75000 |
|
||||
| Sales | 4 | 70000 |
|
||||
| Sales | 3 | 65000 |
|
||||
| Marketing | 5 | 60000 |
|
||||
+-------------+--------+--------+
|
||||
|
||||
DROP TABLE departments_lat;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE employees_lat;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
22
tests/cases/standalone/common/join/join_lateral.sql
Normal file
22
tests/cases/standalone/common/join/join_lateral.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
-- Tests lateral join patterns and correlated subqueries
|
||||
|
||||
CREATE TABLE departments_lat(dept_id INTEGER, dept_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE employees_lat(emp_id INTEGER, dept_id INTEGER, salary INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO departments_lat VALUES (1, 'Engineering', 1000), (2, 'Sales', 2000), (3, 'Marketing', 3000);
|
||||
|
||||
INSERT INTO employees_lat VALUES (1, 1, 75000, 1000), (2, 1, 80000, 2000), (3, 2, 65000, 3000), (4, 2, 70000, 4000), (5, 3, 60000, 5000);
|
||||
|
||||
-- Correlated subquery simulating lateral join behavior
|
||||
SELECT d.dept_name, top_earners.emp_id, top_earners.salary
|
||||
FROM departments_lat d
|
||||
INNER JOIN (
|
||||
SELECT emp_id, dept_id, salary, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) as rn
|
||||
FROM employees_lat
|
||||
) top_earners ON d.dept_id = top_earners.dept_id AND top_earners.rn <= 2
|
||||
ORDER BY d.dept_id, top_earners.salary DESC;
|
||||
|
||||
DROP TABLE departments_lat;
|
||||
|
||||
DROP TABLE employees_lat;
|
||||
44
tests/cases/standalone/common/join/join_mixed_types.result
Normal file
44
tests/cases/standalone/common/join/join_mixed_types.result
Normal file
@@ -0,0 +1,44 @@
|
||||
-- Tests joins with mixed data types and conversions
|
||||
CREATE TABLE numeric_keys(int_key INTEGER, float_key DOUBLE, str_val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE string_keys(str_key VARCHAR, int_val INTEGER, desc_val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO numeric_keys VALUES (1, 1.0, 'first', 1000), (2, 2.0, 'second', 2000), (3, 3.0, 'third', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO string_keys VALUES ('1', 100, 'hundred', 1000), ('2', 200, 'two_hundred', 2000), ('4', 400, 'four_hundred', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
SELECT n.int_key, n.str_val, s.int_val, s.desc_val FROM numeric_keys n INNER JOIN string_keys s ON CAST(n.int_key AS VARCHAR) = s.str_key ORDER BY n.int_key;
|
||||
|
||||
+---------+---------+---------+-------------+
|
||||
| int_key | str_val | int_val | desc_val |
|
||||
+---------+---------+---------+-------------+
|
||||
| 1 | first | 100 | hundred |
|
||||
| 2 | second | 200 | two_hundred |
|
||||
+---------+---------+---------+-------------+
|
||||
|
||||
SELECT n.float_key, s.str_key, s.int_val FROM numeric_keys n LEFT JOIN string_keys s ON n.float_key = CAST(s.str_key AS DOUBLE) ORDER BY n.float_key;
|
||||
|
||||
+-----------+---------+---------+
|
||||
| float_key | str_key | int_val |
|
||||
+-----------+---------+---------+
|
||||
| 1.0 | 1 | 100 |
|
||||
| 2.0 | 2 | 200 |
|
||||
| 3.0 | | |
|
||||
+-----------+---------+---------+
|
||||
|
||||
DROP TABLE numeric_keys;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE string_keys;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
17
tests/cases/standalone/common/join/join_mixed_types.sql
Normal file
17
tests/cases/standalone/common/join/join_mixed_types.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- Tests joins with mixed data types and conversions
|
||||
|
||||
CREATE TABLE numeric_keys(int_key INTEGER, float_key DOUBLE, str_val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE string_keys(str_key VARCHAR, int_val INTEGER, desc_val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO numeric_keys VALUES (1, 1.0, 'first', 1000), (2, 2.0, 'second', 2000), (3, 3.0, 'third', 3000);
|
||||
|
||||
INSERT INTO string_keys VALUES ('1', 100, 'hundred', 1000), ('2', 200, 'two_hundred', 2000), ('4', 400, 'four_hundred', 3000);
|
||||
|
||||
SELECT n.int_key, n.str_val, s.int_val, s.desc_val FROM numeric_keys n INNER JOIN string_keys s ON CAST(n.int_key AS VARCHAR) = s.str_key ORDER BY n.int_key;
|
||||
|
||||
SELECT n.float_key, s.str_key, s.int_val FROM numeric_keys n LEFT JOIN string_keys s ON n.float_key = CAST(s.str_key AS DOUBLE) ORDER BY n.float_key;
|
||||
|
||||
DROP TABLE numeric_keys;
|
||||
|
||||
DROP TABLE string_keys;
|
||||
121
tests/cases/standalone/common/join/join_null_handling.result
Normal file
121
tests/cases/standalone/common/join/join_null_handling.result
Normal file
@@ -0,0 +1,121 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ NULL handling tests
|
||||
-- Tests join behavior with NULL values
|
||||
CREATE TABLE table_with_nulls("id" INTEGER, "value" VARCHAR, category INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE lookup_table(category INTEGER, cat_name VARCHAR, priority INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO table_with_nulls VALUES
|
||||
(1, 'item1', 1, 1000), (2, 'item2', NULL, 2000), (3, 'item3', 2, 3000),
|
||||
(4, 'item4', NULL, 4000), (5, 'item5', 3, 5000), (6, 'item6', 1, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
INSERT INTO lookup_table VALUES
|
||||
(1, 'Category A', 1, 1000), (2, 'Category B', 2, 2000), (3, 'Category C', 3, 3000), (NULL, 'Unknown', 0, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Inner join with NULLs (NULLs won't match)
|
||||
SELECT t."id", t."value", l.cat_name
|
||||
FROM table_with_nulls t
|
||||
INNER JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
+----+-------+------------+
|
||||
| id | value | cat_name |
|
||||
+----+-------+------------+
|
||||
| 1 | item1 | Category A |
|
||||
| 3 | item3 | Category B |
|
||||
| 5 | item5 | Category C |
|
||||
| 6 | item6 | Category A |
|
||||
+----+-------+------------+
|
||||
|
||||
-- Left join with NULLs
|
||||
SELECT t."id", t."value", t.category, COALESCE(l.cat_name, 'No Category') as category_name
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
+----+-------+----------+---------------+
|
||||
| id | value | category | category_name |
|
||||
+----+-------+----------+---------------+
|
||||
| 1 | item1 | 1 | Category A |
|
||||
| 2 | item2 | | No Category |
|
||||
| 3 | item3 | 2 | Category B |
|
||||
| 4 | item4 | | No Category |
|
||||
| 5 | item5 | 3 | Category C |
|
||||
| 6 | item6 | 1 | Category A |
|
||||
+----+-------+----------+---------------+
|
||||
|
||||
-- Join with explicit NULL handling
|
||||
SELECT
|
||||
t."id", t."value",
|
||||
CASE
|
||||
WHEN t.category IS NULL THEN 'NULL Category'
|
||||
WHEN l.cat_name IS NULL THEN 'Missing Lookup'
|
||||
ELSE l.cat_name
|
||||
END as resolved_category
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
+----+-------+-------------------+
|
||||
| id | value | resolved_category |
|
||||
+----+-------+-------------------+
|
||||
| 1 | item1 | Category A |
|
||||
| 2 | item2 | NULL Category |
|
||||
| 3 | item3 | Category B |
|
||||
| 4 | item4 | NULL Category |
|
||||
| 5 | item5 | Category C |
|
||||
| 6 | item6 | Category A |
|
||||
+----+-------+-------------------+
|
||||
|
||||
-- NULL-safe join using COALESCE
|
||||
SELECT t."id", t."value", l.cat_name
|
||||
FROM table_with_nulls t
|
||||
INNER JOIN lookup_table l ON COALESCE(t.category, -1) = COALESCE(l.category, -1)
|
||||
ORDER BY t."id";
|
||||
|
||||
+----+-------+------------+
|
||||
| id | value | cat_name |
|
||||
+----+-------+------------+
|
||||
| 1 | item1 | Category A |
|
||||
| 2 | item2 | Unknown |
|
||||
| 3 | item3 | Category B |
|
||||
| 4 | item4 | Unknown |
|
||||
| 5 | item5 | Category C |
|
||||
| 6 | item6 | Category A |
|
||||
+----+-------+------------+
|
||||
|
||||
-- Aggregation with NULL join results
|
||||
SELECT
|
||||
COALESCE(l.cat_name, 'Uncategorized') as category,
|
||||
COUNT(*) as item_count,
|
||||
COUNT(l.category) as matched_count,
|
||||
COUNT(*) - COUNT(l.category) as unmatched_count
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
GROUP BY l.cat_name
|
||||
ORDER BY item_count DESC, category DESC;
|
||||
|
||||
+---------------+------------+---------------+-----------------+
|
||||
| category | item_count | matched_count | unmatched_count |
|
||||
+---------------+------------+---------------+-----------------+
|
||||
| Uncategorized | 2 | 0 | 2 |
|
||||
| Category A | 2 | 2 | 0 |
|
||||
| Category C | 1 | 1 | 0 |
|
||||
| Category B | 1 | 1 | 0 |
|
||||
+---------------+------------+---------------+-----------------+
|
||||
|
||||
DROP TABLE table_with_nulls;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE lookup_table;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
58
tests/cases/standalone/common/join/join_null_handling.sql
Normal file
58
tests/cases/standalone/common/join/join_null_handling.sql
Normal file
@@ -0,0 +1,58 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ NULL handling tests
|
||||
-- Tests join behavior with NULL values
|
||||
|
||||
CREATE TABLE table_with_nulls("id" INTEGER, "value" VARCHAR, category INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE lookup_table(category INTEGER, cat_name VARCHAR, priority INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO table_with_nulls VALUES
|
||||
(1, 'item1', 1, 1000), (2, 'item2', NULL, 2000), (3, 'item3', 2, 3000),
|
||||
(4, 'item4', NULL, 4000), (5, 'item5', 3, 5000), (6, 'item6', 1, 6000);
|
||||
|
||||
INSERT INTO lookup_table VALUES
|
||||
(1, 'Category A', 1, 1000), (2, 'Category B', 2, 2000), (3, 'Category C', 3, 3000), (NULL, 'Unknown', 0, 4000);
|
||||
|
||||
-- Inner join with NULLs (NULLs won't match)
|
||||
SELECT t."id", t."value", l.cat_name
|
||||
FROM table_with_nulls t
|
||||
INNER JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
-- Left join with NULLs
|
||||
SELECT t."id", t."value", t.category, COALESCE(l.cat_name, 'No Category') as category_name
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
-- Join with explicit NULL handling
|
||||
SELECT
|
||||
t."id", t."value",
|
||||
CASE
|
||||
WHEN t.category IS NULL THEN 'NULL Category'
|
||||
WHEN l.cat_name IS NULL THEN 'Missing Lookup'
|
||||
ELSE l.cat_name
|
||||
END as resolved_category
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
ORDER BY t."id";
|
||||
|
||||
-- NULL-safe join using COALESCE
|
||||
SELECT t."id", t."value", l.cat_name
|
||||
FROM table_with_nulls t
|
||||
INNER JOIN lookup_table l ON COALESCE(t.category, -1) = COALESCE(l.category, -1)
|
||||
ORDER BY t."id";
|
||||
|
||||
-- Aggregation with NULL join results
|
||||
SELECT
|
||||
COALESCE(l.cat_name, 'Uncategorized') as category,
|
||||
COUNT(*) as item_count,
|
||||
COUNT(l.category) as matched_count,
|
||||
COUNT(*) - COUNT(l.category) as unmatched_count
|
||||
FROM table_with_nulls t
|
||||
LEFT JOIN lookup_table l ON t.category = l.category
|
||||
GROUP BY l.cat_name
|
||||
ORDER BY item_count DESC, category DESC;
|
||||
|
||||
DROP TABLE table_with_nulls;
|
||||
|
||||
DROP TABLE lookup_table;
|
||||
46
tests/cases/standalone/common/join/join_ordering.result
Normal file
46
tests/cases/standalone/common/join/join_ordering.result
Normal file
@@ -0,0 +1,46 @@
|
||||
-- Tests join result ordering and deterministic behavior
|
||||
CREATE TABLE left_data("id" INTEGER, left_val VARCHAR, sort_key INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE right_data("id" INTEGER, right_val VARCHAR, sort_key INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO left_data VALUES (1, 'A1', 10, 1000), (2, 'A2', 5, 2000), (3, 'A3', 15, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO right_data VALUES (1, 'B1', 20, 1000), (2, 'B2', 8, 2000), (4, 'B4', 12, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
SELECT l."id", l.left_val, r.right_val, l.sort_key + r.sort_key as combined_sort
|
||||
FROM left_data l INNER JOIN right_data r ON l."id" = r."id" ORDER BY l."id";
|
||||
|
||||
+----+----------+-----------+---------------+
|
||||
| id | left_val | right_val | combined_sort |
|
||||
+----+----------+-----------+---------------+
|
||||
| 1 | A1 | B1 | 30 |
|
||||
| 2 | A2 | B2 | 13 |
|
||||
+----+----------+-----------+---------------+
|
||||
|
||||
SELECT l.left_val, r.right_val FROM left_data l FULL OUTER JOIN right_data r ON l."id" = r."id" ORDER BY l."id", r."id";
|
||||
|
||||
+----------+-----------+
|
||||
| left_val | right_val |
|
||||
+----------+-----------+
|
||||
| A1 | B1 |
|
||||
| A2 | B2 |
|
||||
| A3 | |
|
||||
| | B4 |
|
||||
+----------+-----------+
|
||||
|
||||
DROP TABLE left_data;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE right_data;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
18
tests/cases/standalone/common/join/join_ordering.sql
Normal file
18
tests/cases/standalone/common/join/join_ordering.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Tests join result ordering and deterministic behavior
|
||||
|
||||
CREATE TABLE left_data("id" INTEGER, left_val VARCHAR, sort_key INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE right_data("id" INTEGER, right_val VARCHAR, sort_key INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO left_data VALUES (1, 'A1', 10, 1000), (2, 'A2', 5, 2000), (3, 'A3', 15, 3000);
|
||||
|
||||
INSERT INTO right_data VALUES (1, 'B1', 20, 1000), (2, 'B2', 8, 2000), (4, 'B4', 12, 3000);
|
||||
|
||||
SELECT l."id", l.left_val, r.right_val, l.sort_key + r.sort_key as combined_sort
|
||||
FROM left_data l INNER JOIN right_data r ON l."id" = r."id" ORDER BY l."id";
|
||||
|
||||
SELECT l.left_val, r.right_val FROM left_data l FULL OUTER JOIN right_data r ON l."id" = r."id" ORDER BY l."id", r."id";
|
||||
|
||||
DROP TABLE left_data;
|
||||
|
||||
DROP TABLE right_data;
|
||||
@@ -0,0 +1,121 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ performance pattern tests
|
||||
-- Tests join patterns common in time-series queries
|
||||
CREATE TABLE metrics_perf(metric_id INTEGER, metric_name VARCHAR, "value" DOUBLE, timestamp_val BIGINT, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE metadata_perf(metric_id INTEGER, unit VARCHAR, description VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE thresholds_perf(metric_id INTEGER, warning_level DOUBLE, critical_level DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO metrics_perf VALUES
|
||||
(1, 'cpu_usage', 65.2, 1700000000, 1000), (1, 'cpu_usage', 72.1, 1700000060, 2000),
|
||||
(2, 'memory_usage', 85.5, 1700000000, 3000), (2, 'memory_usage', 78.3, 1700000060, 4000),
|
||||
(3, 'disk_io', 120.7, 1700000000, 5000), (3, 'disk_io', 95.2, 1700000060, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
INSERT INTO metadata_perf VALUES
|
||||
(1, 'percent', 'CPU utilization percentage', 1000),
|
||||
(2, 'percent', 'Memory utilization percentage', 2000),
|
||||
(3, 'MB/s', 'Disk I/O throughput', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO thresholds_perf VALUES
|
||||
(1, 70.0, 90.0, 1000), (2, 80.0, 95.0, 2000), (3, 100.0, 150.0, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Join for monitoring dashboard
|
||||
SELECT
|
||||
m.metric_name,
|
||||
md.unit,
|
||||
m."value",
|
||||
t.warning_level,
|
||||
t.critical_level,
|
||||
CASE
|
||||
WHEN m."value" >= t.critical_level THEN 'CRITICAL'
|
||||
WHEN m."value" >= t.warning_level THEN 'WARNING'
|
||||
ELSE 'OK'
|
||||
END as status
|
||||
FROM metrics_perf m
|
||||
INNER JOIN metadata_perf md ON m.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON m.metric_id = t.metric_id
|
||||
ORDER BY m.timestamp_val, m.metric_id;
|
||||
|
||||
+--------------+---------+-------+---------------+----------------+---------+
|
||||
| metric_name | unit | value | warning_level | critical_level | status |
|
||||
+--------------+---------+-------+---------------+----------------+---------+
|
||||
| cpu_usage | percent | 65.2 | 70.0 | 90.0 | OK |
|
||||
| memory_usage | percent | 85.5 | 80.0 | 95.0 | WARNING |
|
||||
| disk_io | MB/s | 120.7 | 100.0 | 150.0 | WARNING |
|
||||
| cpu_usage | percent | 72.1 | 70.0 | 90.0 | WARNING |
|
||||
| memory_usage | percent | 78.3 | 80.0 | 95.0 | OK |
|
||||
| disk_io | MB/s | 95.2 | 100.0 | 150.0 | OK |
|
||||
+--------------+---------+-------+---------------+----------------+---------+
|
||||
|
||||
-- Time-series join with latest values
|
||||
SELECT
|
||||
latest_metrics.metric_name,
|
||||
latest_metrics.latest_value,
|
||||
md.unit,
|
||||
t.warning_level
|
||||
FROM (
|
||||
SELECT
|
||||
metric_id,
|
||||
metric_name,
|
||||
"value" as latest_value,
|
||||
ROW_NUMBER() OVER (PARTITION BY metric_id ORDER BY timestamp_val DESC) as rn
|
||||
FROM metrics_perf
|
||||
) latest_metrics
|
||||
INNER JOIN metadata_perf md ON latest_metrics.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON latest_metrics.metric_id = t.metric_id
|
||||
WHERE latest_metrics.rn = 1
|
||||
ORDER BY latest_metrics.metric_id;
|
||||
|
||||
+--------------+--------------+---------+---------------+
|
||||
| metric_name | latest_value | unit | warning_level |
|
||||
+--------------+--------------+---------+---------------+
|
||||
| cpu_usage | 72.1 | percent | 70.0 |
|
||||
| memory_usage | 78.3 | percent | 80.0 |
|
||||
| disk_io | 95.2 | MB/s | 100.0 |
|
||||
+--------------+--------------+---------+---------------+
|
||||
|
||||
-- Historical analysis join
|
||||
SELECT
|
||||
md.description,
|
||||
COUNT(*) as total_readings,
|
||||
AVG(m."value") as avg_value,
|
||||
COUNT(CASE WHEN m."value" > t.warning_level THEN 1 END) as warning_count,
|
||||
COUNT(CASE WHEN m."value" > t.critical_level THEN 1 END) as critical_count
|
||||
FROM metrics_perf m
|
||||
INNER JOIN metadata_perf md ON m.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON m.metric_id = t.metric_id
|
||||
GROUP BY md.description, m.metric_id
|
||||
ORDER BY critical_count DESC, warning_count DESC, avg_value DESC;
|
||||
|
||||
+-------------------------------+----------------+-----------+---------------+----------------+
|
||||
| description | total_readings | avg_value | warning_count | critical_count |
|
||||
+-------------------------------+----------------+-----------+---------------+----------------+
|
||||
| Disk I/O throughput | 2 | 107.95 | 1 | 0 |
|
||||
| Memory utilization percentage | 2 | 81.9 | 1 | 0 |
|
||||
| CPU utilization percentage | 2 | 68.65 | 1 | 0 |
|
||||
+-------------------------------+----------------+-----------+---------------+----------------+
|
||||
|
||||
DROP TABLE metrics_perf;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE metadata_perf;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE thresholds_perf;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ performance pattern tests
|
||||
-- Tests join patterns common in time-series queries
|
||||
|
||||
CREATE TABLE metrics_perf(metric_id INTEGER, metric_name VARCHAR, "value" DOUBLE, timestamp_val BIGINT, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE metadata_perf(metric_id INTEGER, unit VARCHAR, description VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE thresholds_perf(metric_id INTEGER, warning_level DOUBLE, critical_level DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO metrics_perf VALUES
|
||||
(1, 'cpu_usage', 65.2, 1700000000, 1000), (1, 'cpu_usage', 72.1, 1700000060, 2000),
|
||||
(2, 'memory_usage', 85.5, 1700000000, 3000), (2, 'memory_usage', 78.3, 1700000060, 4000),
|
||||
(3, 'disk_io', 120.7, 1700000000, 5000), (3, 'disk_io', 95.2, 1700000060, 6000);
|
||||
|
||||
INSERT INTO metadata_perf VALUES
|
||||
(1, 'percent', 'CPU utilization percentage', 1000),
|
||||
(2, 'percent', 'Memory utilization percentage', 2000),
|
||||
(3, 'MB/s', 'Disk I/O throughput', 3000);
|
||||
|
||||
INSERT INTO thresholds_perf VALUES
|
||||
(1, 70.0, 90.0, 1000), (2, 80.0, 95.0, 2000), (3, 100.0, 150.0, 3000);
|
||||
|
||||
-- Join for monitoring dashboard
|
||||
SELECT
|
||||
m.metric_name,
|
||||
md.unit,
|
||||
m."value",
|
||||
t.warning_level,
|
||||
t.critical_level,
|
||||
CASE
|
||||
WHEN m."value" >= t.critical_level THEN 'CRITICAL'
|
||||
WHEN m."value" >= t.warning_level THEN 'WARNING'
|
||||
ELSE 'OK'
|
||||
END as status
|
||||
FROM metrics_perf m
|
||||
INNER JOIN metadata_perf md ON m.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON m.metric_id = t.metric_id
|
||||
ORDER BY m.timestamp_val, m.metric_id;
|
||||
|
||||
-- Time-series join with latest values
|
||||
SELECT
|
||||
latest_metrics.metric_name,
|
||||
latest_metrics.latest_value,
|
||||
md.unit,
|
||||
t.warning_level
|
||||
FROM (
|
||||
SELECT
|
||||
metric_id,
|
||||
metric_name,
|
||||
"value" as latest_value,
|
||||
ROW_NUMBER() OVER (PARTITION BY metric_id ORDER BY timestamp_val DESC) as rn
|
||||
FROM metrics_perf
|
||||
) latest_metrics
|
||||
INNER JOIN metadata_perf md ON latest_metrics.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON latest_metrics.metric_id = t.metric_id
|
||||
WHERE latest_metrics.rn = 1
|
||||
ORDER BY latest_metrics.metric_id;
|
||||
|
||||
-- Historical analysis join
|
||||
SELECT
|
||||
md.description,
|
||||
COUNT(*) as total_readings,
|
||||
AVG(m."value") as avg_value,
|
||||
COUNT(CASE WHEN m."value" > t.warning_level THEN 1 END) as warning_count,
|
||||
COUNT(CASE WHEN m."value" > t.critical_level THEN 1 END) as critical_count
|
||||
FROM metrics_perf m
|
||||
INNER JOIN metadata_perf md ON m.metric_id = md.metric_id
|
||||
INNER JOIN thresholds_perf t ON m.metric_id = t.metric_id
|
||||
GROUP BY md.description, m.metric_id
|
||||
ORDER BY critical_count DESC, warning_count DESC, avg_value DESC;
|
||||
|
||||
DROP TABLE metrics_perf;
|
||||
|
||||
DROP TABLE metadata_perf;
|
||||
|
||||
DROP TABLE thresholds_perf;
|
||||
44
tests/cases/standalone/common/join/join_pushdown.result
Normal file
44
tests/cases/standalone/common/join/join_pushdown.result
Normal file
@@ -0,0 +1,44 @@
|
||||
-- Tests join predicate pushdown optimization scenarios
|
||||
CREATE TABLE events_push(event_id INTEGER, user_id INTEGER, event_type VARCHAR, "value" INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE users_push(user_id INTEGER, user_name VARCHAR, region VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO events_push VALUES (1, 100, 'click', 1, 1000), (2, 100, 'view', 2, 2000), (3, 200, 'click', 1, 3000), (4, 300, 'purchase', 5, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO users_push VALUES (100, 'Alice', 'US', 1000), (200, 'Bob', 'EU', 2000), (300, 'Charlie', 'US', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
SELECT e.event_type, u.region, COUNT(*) as event_count FROM events_push e INNER JOIN users_push u ON e.user_id = u.user_id WHERE u.region = 'US' GROUP BY e.event_type, u.region ORDER BY event_count DESC, e.event_type ASC;
|
||||
|
||||
+------------+--------+-------------+
|
||||
| event_type | region | event_count |
|
||||
+------------+--------+-------------+
|
||||
| click | US | 1 |
|
||||
| purchase | US | 1 |
|
||||
| view | US | 1 |
|
||||
+------------+--------+-------------+
|
||||
|
||||
SELECT u.user_name, SUM(e."value") as total_value FROM users_push u INNER JOIN events_push e ON u.user_id = e.user_id WHERE e.event_type = 'click' GROUP BY u.user_id, u.user_name ORDER BY total_value DESC, u.user_name ASC;
|
||||
|
||||
+-----------+-------------+
|
||||
| user_name | total_value |
|
||||
+-----------+-------------+
|
||||
| Alice | 1 |
|
||||
| Bob | 1 |
|
||||
+-----------+-------------+
|
||||
|
||||
DROP TABLE events_push;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE users_push;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
17
tests/cases/standalone/common/join/join_pushdown.sql
Normal file
17
tests/cases/standalone/common/join/join_pushdown.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- Tests join predicate pushdown optimization scenarios
|
||||
|
||||
CREATE TABLE events_push(event_id INTEGER, user_id INTEGER, event_type VARCHAR, "value" INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE users_push(user_id INTEGER, user_name VARCHAR, region VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO events_push VALUES (1, 100, 'click', 1, 1000), (2, 100, 'view', 2, 2000), (3, 200, 'click', 1, 3000), (4, 300, 'purchase', 5, 4000);
|
||||
|
||||
INSERT INTO users_push VALUES (100, 'Alice', 'US', 1000), (200, 'Bob', 'EU', 2000), (300, 'Charlie', 'US', 3000);
|
||||
|
||||
SELECT e.event_type, u.region, COUNT(*) as event_count FROM events_push e INNER JOIN users_push u ON e.user_id = u.user_id WHERE u.region = 'US' GROUP BY e.event_type, u.region ORDER BY event_count DESC, e.event_type ASC;
|
||||
|
||||
SELECT u.user_name, SUM(e."value") as total_value FROM users_push u INNER JOIN events_push e ON u.user_id = e.user_id WHERE e.event_type = 'click' GROUP BY u.user_id, u.user_name ORDER BY total_value DESC, u.user_name ASC;
|
||||
|
||||
DROP TABLE events_push;
|
||||
|
||||
DROP TABLE users_push;
|
||||
38
tests/cases/standalone/common/join/join_self_patterns.result
Normal file
38
tests/cases/standalone/common/join/join_self_patterns.result
Normal file
@@ -0,0 +1,38 @@
|
||||
-- Tests self-join patterns
|
||||
CREATE TABLE employee_hierarchy(emp_id INTEGER, emp_name VARCHAR, manager_id INTEGER, level_num INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO employee_hierarchy VALUES
|
||||
(1, 'CEO', NULL, 1, 1000), (2, 'CTO', 1, 2, 2000), (3, 'Dev1', 2, 3, 3000), (4, 'Dev2', 2, 3, 4000), (5, 'QA1', 2, 3, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
SELECT e.emp_name as employee, m.emp_name as manager FROM employee_hierarchy e LEFT JOIN employee_hierarchy m ON e.manager_id = m.emp_id ORDER BY e.level_num, e.emp_id;
|
||||
|
||||
+----------+---------+
|
||||
| employee | manager |
|
||||
+----------+---------+
|
||||
| CEO | |
|
||||
| CTO | CEO |
|
||||
| Dev1 | CTO |
|
||||
| Dev2 | CTO |
|
||||
| QA1 | CTO |
|
||||
+----------+---------+
|
||||
|
||||
SELECT m.emp_name as manager, COUNT(e.emp_id) as direct_reports FROM employee_hierarchy m LEFT JOIN employee_hierarchy e ON m.emp_id = e.manager_id GROUP BY m.emp_id, m.emp_name ORDER BY direct_reports DESC, manager DESC;
|
||||
|
||||
+---------+----------------+
|
||||
| manager | direct_reports |
|
||||
+---------+----------------+
|
||||
| CTO | 3 |
|
||||
| CEO | 1 |
|
||||
| QA1 | 0 |
|
||||
| Dev2 | 0 |
|
||||
| Dev1 | 0 |
|
||||
+---------+----------------+
|
||||
|
||||
DROP TABLE employee_hierarchy;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
11
tests/cases/standalone/common/join/join_self_patterns.sql
Normal file
11
tests/cases/standalone/common/join/join_self_patterns.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Tests self-join patterns
|
||||
|
||||
CREATE TABLE employee_hierarchy(emp_id INTEGER, emp_name VARCHAR, manager_id INTEGER, level_num INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO employee_hierarchy VALUES
|
||||
(1, 'CEO', NULL, 1, 1000), (2, 'CTO', 1, 2, 2000), (3, 'Dev1', 2, 3, 3000), (4, 'Dev2', 2, 3, 4000), (5, 'QA1', 2, 3, 5000);
|
||||
|
||||
SELECT e.emp_name as employee, m.emp_name as manager FROM employee_hierarchy e LEFT JOIN employee_hierarchy m ON e.manager_id = m.emp_id ORDER BY e.level_num, e.emp_id;
|
||||
SELECT m.emp_name as manager, COUNT(e.emp_id) as direct_reports FROM employee_hierarchy m LEFT JOIN employee_hierarchy e ON m.emp_id = e.manager_id GROUP BY m.emp_id, m.emp_name ORDER BY direct_reports DESC, manager DESC;
|
||||
|
||||
DROP TABLE employee_hierarchy;
|
||||
83
tests/cases/standalone/common/join/join_types.result
Normal file
83
tests/cases/standalone/common/join/join_types.result
Normal file
@@ -0,0 +1,83 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_join_types.test
|
||||
-- Tests different join types and conditions
|
||||
CREATE TABLE customers("id" INTEGER, "name" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE orders(order_id INTEGER, customer_id INTEGER, amount INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO customers VALUES (1, 'Alice', 1000), (2, 'Bob', 2000), (3, 'Carol', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO orders VALUES (101, 1, 100, 4000), (102, 1, 200, 5000), (103, 2, 150, 6000), (104, 4, 300, 7000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- INNER JOIN
|
||||
SELECT c."name", o.amount FROM customers c INNER JOIN orders o ON c."id" = o.customer_id ORDER BY c."name", o.amount;
|
||||
|
||||
+-------+--------+
|
||||
| name | amount |
|
||||
+-------+--------+
|
||||
| Alice | 100 |
|
||||
| Alice | 200 |
|
||||
| Bob | 150 |
|
||||
+-------+--------+
|
||||
|
||||
-- LEFT JOIN showing unmatched customers
|
||||
SELECT c."name", o.amount FROM customers c LEFT JOIN orders o ON c."id" = o.customer_id ORDER BY c."name", o.amount NULLS LAST;
|
||||
|
||||
+-------+--------+
|
||||
| name | amount |
|
||||
+-------+--------+
|
||||
| Alice | 100 |
|
||||
| Alice | 200 |
|
||||
| Bob | 150 |
|
||||
| Carol | |
|
||||
+-------+--------+
|
||||
|
||||
-- RIGHT JOIN showing unmatched orders
|
||||
SELECT c."name", o.amount FROM customers c RIGHT JOIN orders o ON c."id" = o.customer_id ORDER BY c."name" NULLS LAST, o.amount;
|
||||
|
||||
+-------+--------+
|
||||
| name | amount |
|
||||
+-------+--------+
|
||||
| Alice | 100 |
|
||||
| Alice | 200 |
|
||||
| Bob | 150 |
|
||||
| | 300 |
|
||||
+-------+--------+
|
||||
|
||||
-- FULL OUTER JOIN showing all unmatched
|
||||
SELECT c."name", o.amount FROM customers c FULL OUTER JOIN orders o ON c."id" = o.customer_id ORDER BY c."name" NULLS LAST, o.amount NULLS LAST;
|
||||
|
||||
+-------+--------+
|
||||
| name | amount |
|
||||
+-------+--------+
|
||||
| Alice | 100 |
|
||||
| Alice | 200 |
|
||||
| Bob | 150 |
|
||||
| Carol | |
|
||||
| | 300 |
|
||||
+-------+--------+
|
||||
|
||||
-- JOIN with additional conditions
|
||||
SELECT c."name", o.amount FROM customers c JOIN orders o ON c."id" = o.customer_id AND o.amount > 150 ORDER BY c."name";
|
||||
|
||||
+-------+--------+
|
||||
| name | amount |
|
||||
+-------+--------+
|
||||
| Alice | 200 |
|
||||
+-------+--------+
|
||||
|
||||
DROP TABLE orders;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE customers;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
29
tests/cases/standalone/common/join/join_types.sql
Normal file
29
tests/cases/standalone/common/join/join_types.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_join_types.test
|
||||
-- Tests different join types and conditions
|
||||
|
||||
CREATE TABLE customers("id" INTEGER, "name" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE orders(order_id INTEGER, customer_id INTEGER, amount INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO customers VALUES (1, 'Alice', 1000), (2, 'Bob', 2000), (3, 'Carol', 3000);
|
||||
|
||||
INSERT INTO orders VALUES (101, 1, 100, 4000), (102, 1, 200, 5000), (103, 2, 150, 6000), (104, 4, 300, 7000);
|
||||
|
||||
-- INNER JOIN
|
||||
SELECT c."name", o.amount FROM customers c INNER JOIN orders o ON c."id" = o.customer_id ORDER BY c."name", o.amount;
|
||||
|
||||
-- LEFT JOIN showing unmatched customers
|
||||
SELECT c."name", o.amount FROM customers c LEFT JOIN orders o ON c."id" = o.customer_id ORDER BY c."name", o.amount NULLS LAST;
|
||||
|
||||
-- RIGHT JOIN showing unmatched orders
|
||||
SELECT c."name", o.amount FROM customers c RIGHT JOIN orders o ON c."id" = o.customer_id ORDER BY c."name" NULLS LAST, o.amount;
|
||||
|
||||
-- FULL OUTER JOIN showing all unmatched
|
||||
SELECT c."name", o.amount FROM customers c FULL OUTER JOIN orders o ON c."id" = o.customer_id ORDER BY c."name" NULLS LAST, o.amount NULLS LAST;
|
||||
|
||||
-- JOIN with additional conditions
|
||||
SELECT c."name", o.amount FROM customers c JOIN orders o ON c."id" = o.customer_id AND o.amount > 150 ORDER BY c."name";
|
||||
|
||||
DROP TABLE orders;
|
||||
|
||||
DROP TABLE customers;
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Tests joins combined with window functions
|
||||
CREATE TABLE sales_data_win(rep_id INTEGER, sale_amount DOUBLE, sale_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE rep_targets(rep_id INTEGER, quarterly_target DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO sales_data_win VALUES (1, 1000, '2023-01-01', 1000), (1, 1500, '2023-01-15', 2000), (2, 800, '2023-01-02', 3000), (2, 1200, '2023-01-16', 4000), (3, 2000, '2023-01-03', 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO rep_targets VALUES (1, 5000, 1000), (2, 4000, 2000), (3, 6000, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
SELECT s.rep_id, s.sale_amount, rt.quarterly_target, SUM(s.sale_amount) OVER (PARTITION BY s.rep_id ORDER BY s.sale_date) as running_total FROM sales_data_win s INNER JOIN rep_targets rt ON s.rep_id = rt.rep_id ORDER BY s.rep_id, s.sale_date;
|
||||
|
||||
+--------+-------------+------------------+---------------+
|
||||
| rep_id | sale_amount | quarterly_target | running_total |
|
||||
+--------+-------------+------------------+---------------+
|
||||
| 1 | 1000.0 | 5000.0 | 1000.0 |
|
||||
| 1 | 1500.0 | 5000.0 | 2500.0 |
|
||||
| 2 | 800.0 | 4000.0 | 800.0 |
|
||||
| 2 | 1200.0 | 4000.0 | 2000.0 |
|
||||
| 3 | 2000.0 | 6000.0 | 2000.0 |
|
||||
+--------+-------------+------------------+---------------+
|
||||
|
||||
DROP TABLE sales_data_win;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE rep_targets;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
15
tests/cases/standalone/common/join/join_window_functions.sql
Normal file
15
tests/cases/standalone/common/join/join_window_functions.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- Tests joins combined with window functions
|
||||
|
||||
CREATE TABLE sales_data_win(rep_id INTEGER, sale_amount DOUBLE, sale_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE rep_targets(rep_id INTEGER, quarterly_target DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO sales_data_win VALUES (1, 1000, '2023-01-01', 1000), (1, 1500, '2023-01-15', 2000), (2, 800, '2023-01-02', 3000), (2, 1200, '2023-01-16', 4000), (3, 2000, '2023-01-03', 5000);
|
||||
|
||||
INSERT INTO rep_targets VALUES (1, 5000, 1000), (2, 4000, 2000), (3, 6000, 3000);
|
||||
|
||||
SELECT s.rep_id, s.sale_amount, rt.quarterly_target, SUM(s.sale_amount) OVER (PARTITION BY s.rep_id ORDER BY s.sale_date) as running_total FROM sales_data_win s INNER JOIN rep_targets rt ON s.rep_id = rt.rep_id ORDER BY s.rep_id, s.sale_date;
|
||||
|
||||
DROP TABLE sales_data_win;
|
||||
|
||||
DROP TABLE rep_targets;
|
||||
146
tests/cases/standalone/common/join/join_with_aggregates.result
Normal file
146
tests/cases/standalone/common/join/join_with_aggregates.result
Normal file
@@ -0,0 +1,146 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ join with aggregate tests
|
||||
-- Tests joins combined with aggregate functions
|
||||
CREATE TABLE sensors(sensor_id INTEGER, sensor_name VARCHAR, "location" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE readings(reading_id INTEGER, sensor_id INTEGER, "value" DOUBLE, reading_time TIMESTAMP, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO sensors VALUES
|
||||
(1, 'TempSensor1', 'Room A', 1000), (2, 'TempSensor2', 'Room B', 2000),
|
||||
(3, 'HumiditySensor1', 'Room A', 3000), (4, 'HumiditySensor2', 'Room B', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO readings VALUES
|
||||
(1, 1, 22.5, '2023-01-01 10:00:00', 1000), (2, 1, 23.1, '2023-01-01 11:00:00', 2000),
|
||||
(3, 1, 21.8, '2023-01-01 12:00:00', 3000), (4, 2, 25.3, '2023-01-01 10:00:00', 4000),
|
||||
(5, 2, 26.0, '2023-01-01 11:00:00', 5000), (6, 2, 24.7, '2023-01-01 12:00:00', 6000),
|
||||
(7, 3, 45.2, '2023-01-01 10:00:00', 7000), (8, 3, 46.8, '2023-01-01 11:00:00', 8000),
|
||||
(9, 4, 52.1, '2023-01-01 10:00:00', 9000), (10, 4, 51.3, '2023-01-01 11:00:00', 10000);
|
||||
|
||||
Affected Rows: 10
|
||||
|
||||
-- Join with basic aggregation
|
||||
SELECT
|
||||
s.sensor_name, s."location",
|
||||
COUNT(r.reading_id) as reading_count,
|
||||
AVG(r."value") as avg_value,
|
||||
MIN(r."value") as min_value,
|
||||
MAX(r."value") as max_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s.sensor_id, s.sensor_name, s."location"
|
||||
ORDER BY s.sensor_name;
|
||||
|
||||
+-----------------+----------+---------------+--------------------+-----------+-----------+
|
||||
| sensor_name | location | reading_count | avg_value | min_value | max_value |
|
||||
+-----------------+----------+---------------+--------------------+-----------+-----------+
|
||||
| HumiditySensor1 | Room A | 2 | 46.0 | 45.2 | 46.8 |
|
||||
| HumiditySensor2 | Room B | 2 | 51.7 | 51.3 | 52.1 |
|
||||
| TempSensor1 | Room A | 3 | 22.46666666666667 | 21.8 | 23.1 |
|
||||
| TempSensor2 | Room B | 3 | 25.333333333333332 | 24.7 | 26.0 |
|
||||
+-----------------+----------+---------------+--------------------+-----------+-----------+
|
||||
|
||||
-- Join with time-based aggregation
|
||||
SELECT
|
||||
s."location",
|
||||
DATE_TRUNC('hour', r.reading_time) as hour_bucket,
|
||||
COUNT(*) as readings_per_hour,
|
||||
AVG(r."value") as avg_hourly_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s."location", DATE_TRUNC('hour', r.reading_time)
|
||||
ORDER BY s."location", hour_bucket;
|
||||
|
||||
+----------+---------------------+-------------------+------------------+
|
||||
| location | hour_bucket | readings_per_hour | avg_hourly_value |
|
||||
+----------+---------------------+-------------------+------------------+
|
||||
| Room A | 2023-01-01T10:00:00 | 2 | 33.85 |
|
||||
| Room A | 2023-01-01T11:00:00 | 2 | 34.95 |
|
||||
| Room A | 2023-01-01T12:00:00 | 1 | 21.8 |
|
||||
| Room B | 2023-01-01T10:00:00 | 2 | 38.7 |
|
||||
| Room B | 2023-01-01T11:00:00 | 2 | 38.65 |
|
||||
| Room B | 2023-01-01T12:00:00 | 1 | 24.7 |
|
||||
+----------+---------------------+-------------------+------------------+
|
||||
|
||||
-- Aggregation before join
|
||||
SELECT
|
||||
s.sensor_name, s."location", agg_readings.avg_value, agg_readings.reading_count
|
||||
FROM sensors s
|
||||
INNER JOIN (
|
||||
SELECT sensor_id, AVG("value") as avg_value, COUNT(*) as reading_count
|
||||
FROM readings
|
||||
GROUP BY sensor_id
|
||||
) agg_readings ON s.sensor_id = agg_readings.sensor_id
|
||||
WHERE agg_readings.avg_value > 30.0
|
||||
ORDER BY agg_readings.avg_value DESC;
|
||||
|
||||
+-----------------+----------+-----------+---------------+
|
||||
| sensor_name | location | avg_value | reading_count |
|
||||
+-----------------+----------+-----------+---------------+
|
||||
| HumiditySensor2 | Room B | 51.7 | 2 |
|
||||
| HumiditySensor1 | Room A | 46.0 | 2 |
|
||||
+-----------------+----------+-----------+---------------+
|
||||
|
||||
-- Multiple aggregation levels with joins
|
||||
SELECT
|
||||
location_summary.location,
|
||||
location_summary.sensor_count,
|
||||
location_summary.avg_readings_per_sensor,
|
||||
location_summary.location_avg_value
|
||||
FROM (
|
||||
SELECT
|
||||
s."location",
|
||||
COUNT(DISTINCT s.sensor_id) as sensor_count,
|
||||
COUNT(r.reading_id) / COUNT(DISTINCT s.sensor_id) as avg_readings_per_sensor,
|
||||
AVG(r."value") as location_avg_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s."location"
|
||||
) location_summary
|
||||
ORDER BY location_summary.location_avg_value DESC;
|
||||
|
||||
+----------+--------------+-------------------------+--------------------+
|
||||
| location | sensor_count | avg_readings_per_sensor | location_avg_value |
|
||||
+----------+--------------+-------------------------+--------------------+
|
||||
| Room B | 2 | 2 | 35.88 |
|
||||
| Room A | 2 | 2 | 31.880000000000003 |
|
||||
+----------+--------------+-------------------------+--------------------+
|
||||
|
||||
-- Join with aggregated conditions
|
||||
SELECT
|
||||
s.sensor_name,
|
||||
high_readings.high_count,
|
||||
high_readings.avg_high_value
|
||||
FROM sensors s
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
sensor_id,
|
||||
COUNT(*) as high_count,
|
||||
AVG("value") as avg_high_value
|
||||
FROM readings
|
||||
WHERE "value" > 25.0
|
||||
GROUP BY sensor_id
|
||||
HAVING COUNT(*) >= 2
|
||||
) high_readings ON s.sensor_id = high_readings.sensor_id
|
||||
ORDER BY high_readings.avg_high_value DESC;
|
||||
|
||||
+-----------------+------------+----------------+
|
||||
| sensor_name | high_count | avg_high_value |
|
||||
+-----------------+------------+----------------+
|
||||
| HumiditySensor2 | 2 | 51.7 |
|
||||
| HumiditySensor1 | 2 | 46.0 |
|
||||
| TempSensor2 | 2 | 25.65 |
|
||||
+-----------------+------------+----------------+
|
||||
|
||||
DROP TABLE sensors;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE readings;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
92
tests/cases/standalone/common/join/join_with_aggregates.sql
Normal file
92
tests/cases/standalone/common/join/join_with_aggregates.sql
Normal file
@@ -0,0 +1,92 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ join with aggregate tests
|
||||
-- Tests joins combined with aggregate functions
|
||||
|
||||
CREATE TABLE sensors(sensor_id INTEGER, sensor_name VARCHAR, "location" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE readings(reading_id INTEGER, sensor_id INTEGER, "value" DOUBLE, reading_time TIMESTAMP, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO sensors VALUES
|
||||
(1, 'TempSensor1', 'Room A', 1000), (2, 'TempSensor2', 'Room B', 2000),
|
||||
(3, 'HumiditySensor1', 'Room A', 3000), (4, 'HumiditySensor2', 'Room B', 4000);
|
||||
|
||||
INSERT INTO readings VALUES
|
||||
(1, 1, 22.5, '2023-01-01 10:00:00', 1000), (2, 1, 23.1, '2023-01-01 11:00:00', 2000),
|
||||
(3, 1, 21.8, '2023-01-01 12:00:00', 3000), (4, 2, 25.3, '2023-01-01 10:00:00', 4000),
|
||||
(5, 2, 26.0, '2023-01-01 11:00:00', 5000), (6, 2, 24.7, '2023-01-01 12:00:00', 6000),
|
||||
(7, 3, 45.2, '2023-01-01 10:00:00', 7000), (8, 3, 46.8, '2023-01-01 11:00:00', 8000),
|
||||
(9, 4, 52.1, '2023-01-01 10:00:00', 9000), (10, 4, 51.3, '2023-01-01 11:00:00', 10000);
|
||||
|
||||
-- Join with basic aggregation
|
||||
SELECT
|
||||
s.sensor_name, s."location",
|
||||
COUNT(r.reading_id) as reading_count,
|
||||
AVG(r."value") as avg_value,
|
||||
MIN(r."value") as min_value,
|
||||
MAX(r."value") as max_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s.sensor_id, s.sensor_name, s."location"
|
||||
ORDER BY s.sensor_name;
|
||||
|
||||
-- Join with time-based aggregation
|
||||
SELECT
|
||||
s."location",
|
||||
DATE_TRUNC('hour', r.reading_time) as hour_bucket,
|
||||
COUNT(*) as readings_per_hour,
|
||||
AVG(r."value") as avg_hourly_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s."location", DATE_TRUNC('hour', r.reading_time)
|
||||
ORDER BY s."location", hour_bucket;
|
||||
|
||||
-- Aggregation before join
|
||||
SELECT
|
||||
s.sensor_name, s."location", agg_readings.avg_value, agg_readings.reading_count
|
||||
FROM sensors s
|
||||
INNER JOIN (
|
||||
SELECT sensor_id, AVG("value") as avg_value, COUNT(*) as reading_count
|
||||
FROM readings
|
||||
GROUP BY sensor_id
|
||||
) agg_readings ON s.sensor_id = agg_readings.sensor_id
|
||||
WHERE agg_readings.avg_value > 30.0
|
||||
ORDER BY agg_readings.avg_value DESC;
|
||||
|
||||
-- Multiple aggregation levels with joins
|
||||
SELECT
|
||||
location_summary.location,
|
||||
location_summary.sensor_count,
|
||||
location_summary.avg_readings_per_sensor,
|
||||
location_summary.location_avg_value
|
||||
FROM (
|
||||
SELECT
|
||||
s."location",
|
||||
COUNT(DISTINCT s.sensor_id) as sensor_count,
|
||||
COUNT(r.reading_id) / COUNT(DISTINCT s.sensor_id) as avg_readings_per_sensor,
|
||||
AVG(r."value") as location_avg_value
|
||||
FROM sensors s
|
||||
INNER JOIN readings r ON s.sensor_id = r.sensor_id
|
||||
GROUP BY s."location"
|
||||
) location_summary
|
||||
ORDER BY location_summary.location_avg_value DESC;
|
||||
|
||||
-- Join with aggregated conditions
|
||||
SELECT
|
||||
s.sensor_name,
|
||||
high_readings.high_count,
|
||||
high_readings.avg_high_value
|
||||
FROM sensors s
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
sensor_id,
|
||||
COUNT(*) as high_count,
|
||||
AVG("value") as avg_high_value
|
||||
FROM readings
|
||||
WHERE "value" > 25.0
|
||||
GROUP BY sensor_id
|
||||
HAVING COUNT(*) >= 2
|
||||
) high_readings ON s.sensor_id = high_readings.sensor_id
|
||||
ORDER BY high_readings.avg_high_value DESC;
|
||||
|
||||
DROP TABLE sensors;
|
||||
|
||||
DROP TABLE readings;
|
||||
147
tests/cases/standalone/common/join/join_with_expressions.result
Normal file
147
tests/cases/standalone/common/join/join_with_expressions.result
Normal file
@@ -0,0 +1,147 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ expression join tests
|
||||
-- Tests joins with complex expressions
|
||||
CREATE TABLE measurements(measure_id INTEGER, sensor_id INTEGER, reading DOUBLE, measure_time TIMESTAMP, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE sensor_config(sensor_id INTEGER, min_threshold DOUBLE, max_threshold DOUBLE, calibration_factor DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO measurements VALUES
|
||||
(1, 1, 22.5, '2023-01-01 10:00:00', 1000), (2, 1, 45.8, '2023-01-01 10:05:00', 2000),
|
||||
(3, 2, 18.2, '2023-01-01 10:00:00', 3000), (4, 2, 89.7, '2023-01-01 10:05:00', 4000),
|
||||
(5, 3, 35.1, '2023-01-01 10:00:00', 5000), (6, 3, 42.3, '2023-01-01 10:05:00', 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
INSERT INTO sensor_config VALUES
|
||||
(1, 20.0, 50.0, 1.05, 1000), (2, 15.0, 85.0, 0.98, 2000), (3, 30.0, 70.0, 1.02, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Join with mathematical expressions
|
||||
SELECT
|
||||
m.measure_id,
|
||||
m.reading,
|
||||
m.reading * sc.calibration_factor as calibrated_reading,
|
||||
sc.min_threshold,
|
||||
sc.max_threshold
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc ON m.sensor_id = sc.sensor_id
|
||||
ORDER BY m.measure_id;
|
||||
|
||||
+------------+---------+--------------------+---------------+---------------+
|
||||
| measure_id | reading | calibrated_reading | min_threshold | max_threshold |
|
||||
+------------+---------+--------------------+---------------+---------------+
|
||||
| 1 | 22.5 | 23.625 | 20.0 | 50.0 |
|
||||
| 2 | 45.8 | 48.089999999999996 | 20.0 | 50.0 |
|
||||
| 3 | 18.2 | 17.836 | 15.0 | 85.0 |
|
||||
| 4 | 89.7 | 87.906 | 15.0 | 85.0 |
|
||||
| 5 | 35.1 | 35.802 | 30.0 | 70.0 |
|
||||
| 6 | 42.3 | 43.146 | 30.0 | 70.0 |
|
||||
+------------+---------+--------------------+---------------+---------------+
|
||||
|
||||
-- Join with conditional expressions
|
||||
SELECT
|
||||
m.measure_id,
|
||||
m.reading,
|
||||
sc.calibration_factor,
|
||||
CASE
|
||||
WHEN m.reading * sc.calibration_factor < sc.min_threshold THEN 'Below Range'
|
||||
WHEN m.reading * sc.calibration_factor > sc.max_threshold THEN 'Above Range'
|
||||
ELSE 'In Range'
|
||||
END as reading_status
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc
|
||||
ON m.sensor_id = sc.sensor_id
|
||||
AND m.reading BETWEEN sc.min_threshold * 0.5 AND sc.max_threshold * 1.5
|
||||
ORDER BY m.measure_id;
|
||||
|
||||
+------------+---------+--------------------+----------------+
|
||||
| measure_id | reading | calibration_factor | reading_status |
|
||||
+------------+---------+--------------------+----------------+
|
||||
| 1 | 22.5 | 1.05 | In Range |
|
||||
| 2 | 45.8 | 1.05 | In Range |
|
||||
| 3 | 18.2 | 0.98 | In Range |
|
||||
| 4 | 89.7 | 0.98 | Above Range |
|
||||
| 5 | 35.1 | 1.02 | In Range |
|
||||
| 6 | 42.3 | 1.02 | In Range |
|
||||
+------------+---------+--------------------+----------------+
|
||||
|
||||
-- Join with aggregated expressions
|
||||
SELECT
|
||||
sc.sensor_id,
|
||||
COUNT(*) as total_readings,
|
||||
AVG(m.reading * sc.calibration_factor) as avg_calibrated,
|
||||
COUNT(CASE WHEN m.reading * sc.calibration_factor > sc.max_threshold THEN 1 END) as over_threshold_count
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc ON m.sensor_id = sc.sensor_id
|
||||
GROUP BY sc.sensor_id, sc.calibration_factor, sc.max_threshold
|
||||
HAVING AVG(m.reading * sc.calibration_factor) > 30.0
|
||||
ORDER BY avg_calibrated DESC;
|
||||
|
||||
+-----------+----------------+--------------------+----------------------+
|
||||
| sensor_id | total_readings | avg_calibrated | over_threshold_count |
|
||||
+-----------+----------------+--------------------+----------------------+
|
||||
| 2 | 2 | 52.871 | 1 |
|
||||
| 3 | 2 | 39.474000000000004 | 0 |
|
||||
| 1 | 2 | 35.8575 | 0 |
|
||||
+-----------+----------------+--------------------+----------------------+
|
||||
|
||||
-- Join with string expression conditions
|
||||
CREATE TABLE devices(device_id INTEGER, device_code VARCHAR, "status" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE device_logs(log_id INTEGER, device_code VARCHAR, log_message VARCHAR, severity INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO devices VALUES
|
||||
(1, 'DEV001', 'active', 1000), (2, 'DEV002', 'inactive', 2000), (3, 'DEV003', 'active', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO device_logs VALUES
|
||||
(1, 'DEV001', 'System started', 1, 1000), (2, 'DEV001', 'Warning detected', 2, 2000),
|
||||
(3, 'DEV002', 'Error occurred', 3, 3000), (4, 'DEV003', 'Normal operation', 1, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Join with string expression matching
|
||||
SELECT
|
||||
d.device_id,
|
||||
d."status",
|
||||
dl.log_message,
|
||||
dl.severity
|
||||
FROM devices d
|
||||
INNER JOIN device_logs dl
|
||||
ON UPPER(d.device_code) = UPPER(dl.device_code)
|
||||
AND d."status" = 'active'
|
||||
ORDER BY d.device_id, dl.severity DESC;
|
||||
|
||||
+-----------+--------+------------------+----------+
|
||||
| device_id | status | log_message | severity |
|
||||
+-----------+--------+------------------+----------+
|
||||
| 1 | active | Warning detected | 2 |
|
||||
| 1 | active | System started | 1 |
|
||||
| 3 | active | Normal operation | 1 |
|
||||
+-----------+--------+------------------+----------+
|
||||
|
||||
DROP TABLE measurements;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE sensor_config;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE devices;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE device_logs;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
84
tests/cases/standalone/common/join/join_with_expressions.sql
Normal file
84
tests/cases/standalone/common/join/join_with_expressions.sql
Normal file
@@ -0,0 +1,84 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ expression join tests
|
||||
-- Tests joins with complex expressions
|
||||
|
||||
CREATE TABLE measurements(measure_id INTEGER, sensor_id INTEGER, reading DOUBLE, measure_time TIMESTAMP, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE sensor_config(sensor_id INTEGER, min_threshold DOUBLE, max_threshold DOUBLE, calibration_factor DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO measurements VALUES
|
||||
(1, 1, 22.5, '2023-01-01 10:00:00', 1000), (2, 1, 45.8, '2023-01-01 10:05:00', 2000),
|
||||
(3, 2, 18.2, '2023-01-01 10:00:00', 3000), (4, 2, 89.7, '2023-01-01 10:05:00', 4000),
|
||||
(5, 3, 35.1, '2023-01-01 10:00:00', 5000), (6, 3, 42.3, '2023-01-01 10:05:00', 6000);
|
||||
|
||||
INSERT INTO sensor_config VALUES
|
||||
(1, 20.0, 50.0, 1.05, 1000), (2, 15.0, 85.0, 0.98, 2000), (3, 30.0, 70.0, 1.02, 3000);
|
||||
|
||||
-- Join with mathematical expressions
|
||||
SELECT
|
||||
m.measure_id,
|
||||
m.reading,
|
||||
m.reading * sc.calibration_factor as calibrated_reading,
|
||||
sc.min_threshold,
|
||||
sc.max_threshold
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc ON m.sensor_id = sc.sensor_id
|
||||
ORDER BY m.measure_id;
|
||||
|
||||
-- Join with conditional expressions
|
||||
SELECT
|
||||
m.measure_id,
|
||||
m.reading,
|
||||
sc.calibration_factor,
|
||||
CASE
|
||||
WHEN m.reading * sc.calibration_factor < sc.min_threshold THEN 'Below Range'
|
||||
WHEN m.reading * sc.calibration_factor > sc.max_threshold THEN 'Above Range'
|
||||
ELSE 'In Range'
|
||||
END as reading_status
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc
|
||||
ON m.sensor_id = sc.sensor_id
|
||||
AND m.reading BETWEEN sc.min_threshold * 0.5 AND sc.max_threshold * 1.5
|
||||
ORDER BY m.measure_id;
|
||||
|
||||
-- Join with aggregated expressions
|
||||
SELECT
|
||||
sc.sensor_id,
|
||||
COUNT(*) as total_readings,
|
||||
AVG(m.reading * sc.calibration_factor) as avg_calibrated,
|
||||
COUNT(CASE WHEN m.reading * sc.calibration_factor > sc.max_threshold THEN 1 END) as over_threshold_count
|
||||
FROM measurements m
|
||||
INNER JOIN sensor_config sc ON m.sensor_id = sc.sensor_id
|
||||
GROUP BY sc.sensor_id, sc.calibration_factor, sc.max_threshold
|
||||
HAVING AVG(m.reading * sc.calibration_factor) > 30.0
|
||||
ORDER BY avg_calibrated DESC;
|
||||
|
||||
-- Join with string expression conditions
|
||||
CREATE TABLE devices(device_id INTEGER, device_code VARCHAR, "status" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
CREATE TABLE device_logs(log_id INTEGER, device_code VARCHAR, log_message VARCHAR, severity INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO devices VALUES
|
||||
(1, 'DEV001', 'active', 1000), (2, 'DEV002', 'inactive', 2000), (3, 'DEV003', 'active', 3000);
|
||||
|
||||
INSERT INTO device_logs VALUES
|
||||
(1, 'DEV001', 'System started', 1, 1000), (2, 'DEV001', 'Warning detected', 2, 2000),
|
||||
(3, 'DEV002', 'Error occurred', 3, 3000), (4, 'DEV003', 'Normal operation', 1, 4000);
|
||||
|
||||
-- Join with string expression matching
|
||||
SELECT
|
||||
d.device_id,
|
||||
d."status",
|
||||
dl.log_message,
|
||||
dl.severity
|
||||
FROM devices d
|
||||
INNER JOIN device_logs dl
|
||||
ON UPPER(d.device_code) = UPPER(dl.device_code)
|
||||
AND d."status" = 'active'
|
||||
ORDER BY d.device_id, dl.severity DESC;
|
||||
|
||||
DROP TABLE measurements;
|
||||
|
||||
DROP TABLE sensor_config;
|
||||
|
||||
DROP TABLE devices;
|
||||
|
||||
DROP TABLE device_logs;
|
||||
76
tests/cases/standalone/common/join/join_with_nulls.result
Normal file
76
tests/cases/standalone/common/join/join_with_nulls.result
Normal file
@@ -0,0 +1,76 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_join_with_nulls.test
|
||||
-- Tests JOIN behavior with NULL values
|
||||
CREATE TABLE null_left("id" INTEGER, val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE null_right("id" INTEGER, val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO null_left VALUES (1, 'A', 1000), (2, NULL, 2000), (NULL, 'C', 3000), (4, 'D', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO null_right VALUES (1, 'X', 5000), (NULL, 'Y', 6000), (3, 'Z', 7000), (4, NULL, 8000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- INNER JOIN with NULLs (NULLs don't match)
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l."id" = r."id" ORDER BY l.ts;
|
||||
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| id | val | ts | id | val | ts |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| 1 | A | 1970-01-01T00:00:01 | 1 | X | 1970-01-01T00:00:05 |
|
||||
| 4 | D | 1970-01-01T00:00:04 | 4 | | 1970-01-01T00:00:08 |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
|
||||
-- LEFT JOIN with NULLs
|
||||
SELECT * FROM null_left l LEFT JOIN null_right r ON l."id" = r."id" ORDER BY l.ts;
|
||||
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| id | val | ts | id | val | ts |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| 1 | A | 1970-01-01T00:00:01 | 1 | X | 1970-01-01T00:00:05 |
|
||||
| 2 | | 1970-01-01T00:00:02 | | | |
|
||||
| | C | 1970-01-01T00:00:03 | | | |
|
||||
| 4 | D | 1970-01-01T00:00:04 | 4 | | 1970-01-01T00:00:08 |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
|
||||
-- JOIN on string columns with NULLs
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l.val = r.val ORDER BY l.ts;
|
||||
|
||||
++
|
||||
++
|
||||
|
||||
-- JOIN with IS NOT DISTINCT FROM (treats NULL=NULL as true)
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l."id" IS NOT DISTINCT FROM r."id" ORDER BY l.ts;
|
||||
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| id | val | ts | id | val | ts |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| 1 | A | 1970-01-01T00:00:01 | 1 | X | 1970-01-01T00:00:05 |
|
||||
| | C | 1970-01-01T00:00:03 | | Y | 1970-01-01T00:00:06 |
|
||||
| 4 | D | 1970-01-01T00:00:04 | 4 | | 1970-01-01T00:00:08 |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
|
||||
-- JOIN with NULL-safe conditions
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON (l."id" = r."id" OR (l."id" IS NULL AND r."id" IS NULL)) ORDER BY l.ts;
|
||||
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| id | val | ts | id | val | ts |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
| 1 | A | 1970-01-01T00:00:01 | 1 | X | 1970-01-01T00:00:05 |
|
||||
| | C | 1970-01-01T00:00:03 | | Y | 1970-01-01T00:00:06 |
|
||||
| 4 | D | 1970-01-01T00:00:04 | 4 | | 1970-01-01T00:00:08 |
|
||||
+----+-----+---------------------+----+-----+---------------------+
|
||||
|
||||
DROP TABLE null_right;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE null_left;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
29
tests/cases/standalone/common/join/join_with_nulls.sql
Normal file
29
tests/cases/standalone/common/join/join_with_nulls.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_join_with_nulls.test
|
||||
-- Tests JOIN behavior with NULL values
|
||||
|
||||
CREATE TABLE null_left("id" INTEGER, val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE null_right("id" INTEGER, val VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO null_left VALUES (1, 'A', 1000), (2, NULL, 2000), (NULL, 'C', 3000), (4, 'D', 4000);
|
||||
|
||||
INSERT INTO null_right VALUES (1, 'X', 5000), (NULL, 'Y', 6000), (3, 'Z', 7000), (4, NULL, 8000);
|
||||
|
||||
-- INNER JOIN with NULLs (NULLs don't match)
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l."id" = r."id" ORDER BY l.ts;
|
||||
|
||||
-- LEFT JOIN with NULLs
|
||||
SELECT * FROM null_left l LEFT JOIN null_right r ON l."id" = r."id" ORDER BY l.ts;
|
||||
|
||||
-- JOIN on string columns with NULLs
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l.val = r.val ORDER BY l.ts;
|
||||
|
||||
-- JOIN with IS NOT DISTINCT FROM (treats NULL=NULL as true)
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON l."id" IS NOT DISTINCT FROM r."id" ORDER BY l.ts;
|
||||
|
||||
-- JOIN with NULL-safe conditions
|
||||
SELECT * FROM null_left l INNER JOIN null_right r ON (l."id" = r."id" OR (l."id" IS NULL AND r."id" IS NULL)) ORDER BY l.ts;
|
||||
|
||||
DROP TABLE null_right;
|
||||
|
||||
DROP TABLE null_left;
|
||||
155
tests/cases/standalone/common/join/join_with_subqueries.result
Normal file
155
tests/cases/standalone/common/join/join_with_subqueries.result
Normal file
@@ -0,0 +1,155 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ subquery join tests
|
||||
-- Tests joins involving subqueries
|
||||
CREATE TABLE products_sub(prod_id INTEGER, prod_name VARCHAR, category_id INTEGER, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE categories_sub(cat_id INTEGER, cat_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE sales_sub(sale_id INTEGER, prod_id INTEGER, quantity INTEGER, sale_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO products_sub VALUES
|
||||
(1, 'Laptop', 1, 1200.00, 1000), (2, 'Mouse', 2, 25.00, 2000),
|
||||
(3, 'Monitor', 1, 400.00, 3000), (4, 'Keyboard', 2, 80.00, 4000),
|
||||
(5, 'Tablet', 1, 600.00, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO categories_sub VALUES
|
||||
(1, 'Electronics', 1000), (2, 'Accessories', 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO sales_sub VALUES
|
||||
(1, 1, 2, '2023-01-01', 1000), (2, 2, 10, '2023-01-02', 2000),
|
||||
(3, 3, 1, '2023-01-03', 3000), (4, 1, 1, '2023-01-04', 4000),
|
||||
(5, 4, 5, '2023-01-05', 5000), (6, 5, 2, '2023-01-06', 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
-- Join with aggregated subquery
|
||||
SELECT
|
||||
p.prod_name, p.price, sales_summary.total_quantity, sales_summary.total_sales
|
||||
FROM products_sub p
|
||||
INNER JOIN (
|
||||
SELECT prod_id, SUM(quantity) as total_quantity, COUNT(*) as total_sales
|
||||
FROM sales_sub
|
||||
GROUP BY prod_id
|
||||
) sales_summary ON p.prod_id = sales_summary.prod_id
|
||||
ORDER BY sales_summary.total_quantity DESC;
|
||||
|
||||
+-----------+--------+----------------+-------------+
|
||||
| prod_name | price | total_quantity | total_sales |
|
||||
+-----------+--------+----------------+-------------+
|
||||
| Mouse | 25.0 | 10 | 1 |
|
||||
| Keyboard | 80.0 | 5 | 1 |
|
||||
| Laptop | 1200.0 | 3 | 2 |
|
||||
| Tablet | 600.0 | 2 | 1 |
|
||||
| Monitor | 400.0 | 1 | 1 |
|
||||
+-----------+--------+----------------+-------------+
|
||||
|
||||
-- Join with filtered subquery
|
||||
SELECT
|
||||
p.prod_name, c.cat_name, high_sales.quantity
|
||||
FROM products_sub p
|
||||
INNER JOIN categories_sub c ON p.category_id = c.cat_id
|
||||
INNER JOIN (
|
||||
SELECT prod_id, quantity
|
||||
FROM sales_sub
|
||||
WHERE quantity > 3
|
||||
) high_sales ON p.prod_id = high_sales.prod_id
|
||||
ORDER BY high_sales.quantity DESC;
|
||||
|
||||
+-----------+-------------+----------+
|
||||
| prod_name | cat_name | quantity |
|
||||
+-----------+-------------+----------+
|
||||
| Mouse | Accessories | 10 |
|
||||
| Keyboard | Accessories | 5 |
|
||||
+-----------+-------------+----------+
|
||||
|
||||
-- Join with correlated subquery results
|
||||
SELECT
|
||||
p.prod_name, p.price,
|
||||
(SELECT SUM(s.quantity) FROM sales_sub s WHERE s.prod_id = p.prod_id) as total_sold
|
||||
FROM products_sub p
|
||||
WHERE EXISTS (SELECT 1 FROM sales_sub s WHERE s.prod_id = p.prod_id)
|
||||
ORDER BY total_sold DESC;
|
||||
|
||||
+-----------+--------+------------+
|
||||
| prod_name | price | total_sold |
|
||||
+-----------+--------+------------+
|
||||
| Mouse | 25.0 | 10 |
|
||||
| Keyboard | 80.0 | 5 |
|
||||
| Laptop | 1200.0 | 3 |
|
||||
| Tablet | 600.0 | 2 |
|
||||
| Monitor | 400.0 | 1 |
|
||||
+-----------+--------+------------+
|
||||
|
||||
-- Join subquery with window functions
|
||||
SELECT
|
||||
p.prod_name, ranked_sales.quantity, ranked_sales.sale_rank
|
||||
FROM products_sub p
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
prod_id, quantity,
|
||||
ROW_NUMBER() OVER (PARTITION BY prod_id ORDER BY quantity DESC) as sale_rank
|
||||
FROM sales_sub
|
||||
) ranked_sales ON p.prod_id = ranked_sales.prod_id
|
||||
WHERE ranked_sales.sale_rank = 1
|
||||
ORDER BY ranked_sales.quantity DESC, p.prod_name ASC;
|
||||
|
||||
+-----------+----------+-----------+
|
||||
| prod_name | quantity | sale_rank |
|
||||
+-----------+----------+-----------+
|
||||
| Mouse | 10 | 1 |
|
||||
| Keyboard | 5 | 1 |
|
||||
| Laptop | 2 | 1 |
|
||||
| Tablet | 2 | 1 |
|
||||
| Monitor | 1 | 1 |
|
||||
+-----------+----------+-----------+
|
||||
|
||||
-- Multiple subquery joins
|
||||
SELECT
|
||||
product_stats.prod_name,
|
||||
product_stats.avg_price,
|
||||
category_stats.category_sales
|
||||
FROM (
|
||||
SELECT prod_id, prod_name, AVG(price) as avg_price, category_id
|
||||
FROM products_sub
|
||||
GROUP BY prod_id, prod_name, category_id
|
||||
) product_stats
|
||||
INNER JOIN (
|
||||
SELECT c.cat_id, c.cat_name, COUNT(s.sale_id) as category_sales
|
||||
FROM categories_sub c
|
||||
INNER JOIN products_sub p ON c.cat_id = p.category_id
|
||||
INNER JOIN sales_sub s ON p.prod_id = s.prod_id
|
||||
GROUP BY c.cat_id, c.cat_name
|
||||
) category_stats ON product_stats.category_id = category_stats.cat_id
|
||||
ORDER BY category_stats.category_sales DESC, product_stats.prod_name DESC;
|
||||
|
||||
+-----------+-----------+----------------+
|
||||
| prod_name | avg_price | category_sales |
|
||||
+-----------+-----------+----------------+
|
||||
| Tablet | 600.0 | 4 |
|
||||
| Monitor | 400.0 | 4 |
|
||||
| Laptop | 1200.0 | 4 |
|
||||
| Mouse | 25.0 | 2 |
|
||||
| Keyboard | 80.0 | 2 |
|
||||
+-----------+-----------+----------------+
|
||||
|
||||
DROP TABLE products_sub;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE categories_sub;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE sales_sub;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
90
tests/cases/standalone/common/join/join_with_subqueries.sql
Normal file
90
tests/cases/standalone/common/join/join_with_subqueries.sql
Normal file
@@ -0,0 +1,90 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ subquery join tests
|
||||
-- Tests joins involving subqueries
|
||||
|
||||
CREATE TABLE products_sub(prod_id INTEGER, prod_name VARCHAR, category_id INTEGER, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE categories_sub(cat_id INTEGER, cat_name VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE sales_sub(sale_id INTEGER, prod_id INTEGER, quantity INTEGER, sale_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO products_sub VALUES
|
||||
(1, 'Laptop', 1, 1200.00, 1000), (2, 'Mouse', 2, 25.00, 2000),
|
||||
(3, 'Monitor', 1, 400.00, 3000), (4, 'Keyboard', 2, 80.00, 4000),
|
||||
(5, 'Tablet', 1, 600.00, 5000);
|
||||
|
||||
INSERT INTO categories_sub VALUES
|
||||
(1, 'Electronics', 1000), (2, 'Accessories', 2000);
|
||||
|
||||
INSERT INTO sales_sub VALUES
|
||||
(1, 1, 2, '2023-01-01', 1000), (2, 2, 10, '2023-01-02', 2000),
|
||||
(3, 3, 1, '2023-01-03', 3000), (4, 1, 1, '2023-01-04', 4000),
|
||||
(5, 4, 5, '2023-01-05', 5000), (6, 5, 2, '2023-01-06', 6000);
|
||||
|
||||
-- Join with aggregated subquery
|
||||
SELECT
|
||||
p.prod_name, p.price, sales_summary.total_quantity, sales_summary.total_sales
|
||||
FROM products_sub p
|
||||
INNER JOIN (
|
||||
SELECT prod_id, SUM(quantity) as total_quantity, COUNT(*) as total_sales
|
||||
FROM sales_sub
|
||||
GROUP BY prod_id
|
||||
) sales_summary ON p.prod_id = sales_summary.prod_id
|
||||
ORDER BY sales_summary.total_quantity DESC;
|
||||
|
||||
-- Join with filtered subquery
|
||||
SELECT
|
||||
p.prod_name, c.cat_name, high_sales.quantity
|
||||
FROM products_sub p
|
||||
INNER JOIN categories_sub c ON p.category_id = c.cat_id
|
||||
INNER JOIN (
|
||||
SELECT prod_id, quantity
|
||||
FROM sales_sub
|
||||
WHERE quantity > 3
|
||||
) high_sales ON p.prod_id = high_sales.prod_id
|
||||
ORDER BY high_sales.quantity DESC;
|
||||
|
||||
-- Join with correlated subquery results
|
||||
SELECT
|
||||
p.prod_name, p.price,
|
||||
(SELECT SUM(s.quantity) FROM sales_sub s WHERE s.prod_id = p.prod_id) as total_sold
|
||||
FROM products_sub p
|
||||
WHERE EXISTS (SELECT 1 FROM sales_sub s WHERE s.prod_id = p.prod_id)
|
||||
ORDER BY total_sold DESC;
|
||||
|
||||
-- Join subquery with window functions
|
||||
SELECT
|
||||
p.prod_name, ranked_sales.quantity, ranked_sales.sale_rank
|
||||
FROM products_sub p
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
prod_id, quantity,
|
||||
ROW_NUMBER() OVER (PARTITION BY prod_id ORDER BY quantity DESC) as sale_rank
|
||||
FROM sales_sub
|
||||
) ranked_sales ON p.prod_id = ranked_sales.prod_id
|
||||
WHERE ranked_sales.sale_rank = 1
|
||||
ORDER BY ranked_sales.quantity DESC, p.prod_name ASC;
|
||||
|
||||
-- Multiple subquery joins
|
||||
SELECT
|
||||
product_stats.prod_name,
|
||||
product_stats.avg_price,
|
||||
category_stats.category_sales
|
||||
FROM (
|
||||
SELECT prod_id, prod_name, AVG(price) as avg_price, category_id
|
||||
FROM products_sub
|
||||
GROUP BY prod_id, prod_name, category_id
|
||||
) product_stats
|
||||
INNER JOIN (
|
||||
SELECT c.cat_id, c.cat_name, COUNT(s.sale_id) as category_sales
|
||||
FROM categories_sub c
|
||||
INNER JOIN products_sub p ON c.cat_id = p.category_id
|
||||
INNER JOIN sales_sub s ON p.prod_id = s.prod_id
|
||||
GROUP BY c.cat_id, c.cat_name
|
||||
) category_stats ON product_stats.category_id = category_stats.cat_id
|
||||
ORDER BY category_stats.category_sales DESC, product_stats.prod_name DESC;
|
||||
|
||||
DROP TABLE products_sub;
|
||||
|
||||
DROP TABLE categories_sub;
|
||||
|
||||
DROP TABLE sales_sub;
|
||||
115
tests/cases/standalone/common/join/left_join_patterns.result
Normal file
115
tests/cases/standalone/common/join/left_join_patterns.result
Normal file
@@ -0,0 +1,115 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/left_outer/ pattern tests
|
||||
-- Tests common left join patterns
|
||||
CREATE TABLE accounts(acc_id INTEGER, acc_name VARCHAR, balance DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE transactions(txn_id INTEGER, acc_id INTEGER, amount DOUBLE, txn_type VARCHAR, txn_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO accounts VALUES
|
||||
(1, 'Checking', 1500.00, 1000), (2, 'Savings', 5000.00, 2000),
|
||||
(3, 'Credit', -250.00, 3000), (4, 'Investment', 10000.00, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO transactions VALUES
|
||||
(1, 1, -100.00, 'withdrawal', '2023-01-01', 1000),
|
||||
(2, 1, 500.00, 'deposit', '2023-01-02', 2000),
|
||||
(3, 2, 1000.00, 'deposit', '2023-01-01', 3000),
|
||||
(4, 3, -50.00, 'purchase', '2023-01-03', 4000),
|
||||
(5, 1, -25.00, 'fee', '2023-01-04', 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
-- Left join to find accounts with/without transactions
|
||||
SELECT
|
||||
a.acc_name, a.balance,
|
||||
COUNT(t.txn_id) as transaction_count,
|
||||
COALESCE(SUM(t.amount), 0) as total_activity
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id
|
||||
GROUP BY a.acc_id, a.acc_name, a.balance
|
||||
ORDER BY transaction_count DESC;
|
||||
|
||||
+------------+---------+-------------------+----------------+
|
||||
| acc_name | balance | transaction_count | total_activity |
|
||||
+------------+---------+-------------------+----------------+
|
||||
| Checking | 1500.0 | 3 | 375.0 |
|
||||
| Savings | 5000.0 | 1 | 1000.0 |
|
||||
| Credit | -250.0 | 1 | -50.0 |
|
||||
| Investment | 10000.0 | 0 | 0.0 |
|
||||
+------------+---------+-------------------+----------------+
|
||||
|
||||
-- Left join with date filtering
|
||||
SELECT
|
||||
a.acc_name,
|
||||
COUNT(t.txn_id) as recent_transactions,
|
||||
SUM(CASE WHEN t.amount > 0 THEN t.amount ELSE 0 END) as deposits,
|
||||
SUM(CASE WHEN t.amount < 0 THEN t.amount ELSE 0 END) as withdrawals
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id AND t.txn_date >= '2023-01-02'
|
||||
GROUP BY a.acc_id, a.acc_name
|
||||
ORDER BY recent_transactions DESC, a.acc_name ASC;
|
||||
|
||||
+------------+---------------------+----------+-------------+
|
||||
| acc_name | recent_transactions | deposits | withdrawals |
|
||||
+------------+---------------------+----------+-------------+
|
||||
| Checking | 2 | 500.0 | -25.0 |
|
||||
| Credit | 1 | 0.0 | -50.0 |
|
||||
| Investment | 0 | 0.0 | 0.0 |
|
||||
| Savings | 0 | 0.0 | 0.0 |
|
||||
+------------+---------------------+----------+-------------+
|
||||
|
||||
-- Left join NULL handling
|
||||
SELECT
|
||||
a.acc_name,
|
||||
a.balance,
|
||||
t.txn_id,
|
||||
COALESCE(t.amount, 0) as transaction_amount,
|
||||
CASE WHEN t.txn_id IS NULL THEN 'No Activity' ELSE 'Has Activity' END as status
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id
|
||||
ORDER BY a.acc_id, t.txn_date;
|
||||
|
||||
+------------+---------+--------+--------------------+--------------+
|
||||
| acc_name | balance | txn_id | transaction_amount | status |
|
||||
+------------+---------+--------+--------------------+--------------+
|
||||
| Checking | 1500.0 | 1 | -100.0 | Has Activity |
|
||||
| Checking | 1500.0 | 2 | 500.0 | Has Activity |
|
||||
| Checking | 1500.0 | 5 | -25.0 | Has Activity |
|
||||
| Savings | 5000.0 | 3 | 1000.0 | Has Activity |
|
||||
| Credit | -250.0 | 4 | -50.0 | Has Activity |
|
||||
| Investment | 10000.0 | | 0.0 | No Activity |
|
||||
+------------+---------+--------+--------------------+--------------+
|
||||
|
||||
-- Left join with complex conditions
|
||||
SELECT
|
||||
a.acc_name,
|
||||
COUNT(large_txn.txn_id) as large_transaction_count,
|
||||
AVG(large_txn.amount) as avg_large_amount
|
||||
FROM accounts a
|
||||
LEFT JOIN (
|
||||
SELECT * FROM transactions WHERE ABS(amount) > 100.00
|
||||
) large_txn ON a.acc_id = large_txn.acc_id
|
||||
GROUP BY a.acc_id, a.acc_name
|
||||
ORDER BY large_transaction_count DESC, a.acc_name ASC;
|
||||
|
||||
+------------+-------------------------+------------------+
|
||||
| acc_name | large_transaction_count | avg_large_amount |
|
||||
+------------+-------------------------+------------------+
|
||||
| Checking | 1 | 500.0 |
|
||||
| Savings | 1 | 1000.0 |
|
||||
| Credit | 0 | |
|
||||
| Investment | 0 | |
|
||||
+------------+-------------------------+------------------+
|
||||
|
||||
DROP TABLE accounts;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE transactions;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
65
tests/cases/standalone/common/join/left_join_patterns.sql
Normal file
65
tests/cases/standalone/common/join/left_join_patterns.sql
Normal file
@@ -0,0 +1,65 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/left_outer/ pattern tests
|
||||
-- Tests common left join patterns
|
||||
|
||||
CREATE TABLE accounts(acc_id INTEGER, acc_name VARCHAR, balance DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE transactions(txn_id INTEGER, acc_id INTEGER, amount DOUBLE, txn_type VARCHAR, txn_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO accounts VALUES
|
||||
(1, 'Checking', 1500.00, 1000), (2, 'Savings', 5000.00, 2000),
|
||||
(3, 'Credit', -250.00, 3000), (4, 'Investment', 10000.00, 4000);
|
||||
|
||||
INSERT INTO transactions VALUES
|
||||
(1, 1, -100.00, 'withdrawal', '2023-01-01', 1000),
|
||||
(2, 1, 500.00, 'deposit', '2023-01-02', 2000),
|
||||
(3, 2, 1000.00, 'deposit', '2023-01-01', 3000),
|
||||
(4, 3, -50.00, 'purchase', '2023-01-03', 4000),
|
||||
(5, 1, -25.00, 'fee', '2023-01-04', 5000);
|
||||
|
||||
-- Left join to find accounts with/without transactions
|
||||
SELECT
|
||||
a.acc_name, a.balance,
|
||||
COUNT(t.txn_id) as transaction_count,
|
||||
COALESCE(SUM(t.amount), 0) as total_activity
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id
|
||||
GROUP BY a.acc_id, a.acc_name, a.balance
|
||||
ORDER BY transaction_count DESC;
|
||||
|
||||
-- Left join with date filtering
|
||||
SELECT
|
||||
a.acc_name,
|
||||
COUNT(t.txn_id) as recent_transactions,
|
||||
SUM(CASE WHEN t.amount > 0 THEN t.amount ELSE 0 END) as deposits,
|
||||
SUM(CASE WHEN t.amount < 0 THEN t.amount ELSE 0 END) as withdrawals
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id AND t.txn_date >= '2023-01-02'
|
||||
GROUP BY a.acc_id, a.acc_name
|
||||
ORDER BY recent_transactions DESC, a.acc_name ASC;
|
||||
|
||||
-- Left join NULL handling
|
||||
SELECT
|
||||
a.acc_name,
|
||||
a.balance,
|
||||
t.txn_id,
|
||||
COALESCE(t.amount, 0) as transaction_amount,
|
||||
CASE WHEN t.txn_id IS NULL THEN 'No Activity' ELSE 'Has Activity' END as status
|
||||
FROM accounts a
|
||||
LEFT JOIN transactions t ON a.acc_id = t.acc_id
|
||||
ORDER BY a.acc_id, t.txn_date;
|
||||
|
||||
-- Left join with complex conditions
|
||||
SELECT
|
||||
a.acc_name,
|
||||
COUNT(large_txn.txn_id) as large_transaction_count,
|
||||
AVG(large_txn.amount) as avg_large_amount
|
||||
FROM accounts a
|
||||
LEFT JOIN (
|
||||
SELECT * FROM transactions WHERE ABS(amount) > 100.00
|
||||
) large_txn ON a.acc_id = large_txn.acc_id
|
||||
GROUP BY a.acc_id, a.acc_name
|
||||
ORDER BY large_transaction_count DESC, a.acc_name ASC;
|
||||
|
||||
DROP TABLE accounts;
|
||||
|
||||
DROP TABLE transactions;
|
||||
67
tests/cases/standalone/common/join/left_outer_join.result
Normal file
67
tests/cases/standalone/common/join/left_outer_join.result
Normal file
@@ -0,0 +1,67 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/left_outer/test_left_outer.test
|
||||
-- Tests LEFT OUTER JOIN functionality
|
||||
CREATE TABLE left_t (a INTEGER, b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE right_t (a INTEGER, c INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO left_t VALUES (1, 10, 1000), (2, 20, 2000), (3, 30, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO right_t VALUES (1, 100, 4000), (2, 200, 5000), (4, 400, 6000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Basic LEFT JOIN
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a ORDER BY left_t.a;
|
||||
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| a | b | ts | a | c | ts |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| 1 | 10 | 1970-01-01T00:00:01 | 1 | 100 | 1970-01-01T00:00:04 |
|
||||
| 2 | 20 | 1970-01-01T00:00:02 | 2 | 200 | 1970-01-01T00:00:05 |
|
||||
| 3 | 30 | 1970-01-01T00:00:03 | | | |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
|
||||
-- LEFT JOIN with WHERE on left table
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a WHERE left_t.b > 15 ORDER BY left_t.a;
|
||||
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| a | b | ts | a | c | ts |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| 2 | 20 | 1970-01-01T00:00:02 | 2 | 200 | 1970-01-01T00:00:05 |
|
||||
| 3 | 30 | 1970-01-01T00:00:03 | | | |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
|
||||
-- LEFT JOIN with WHERE on joined result
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a WHERE right_t.c IS NULL ORDER BY left_t.a;
|
||||
|
||||
+---+----+---------------------+---+---+----+
|
||||
| a | b | ts | a | c | ts |
|
||||
+---+----+---------------------+---+---+----+
|
||||
| 3 | 30 | 1970-01-01T00:00:03 | | | |
|
||||
+---+----+---------------------+---+---+----+
|
||||
|
||||
-- LEFT JOIN with complex condition
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a AND left_t.b < 25 ORDER BY left_t.a;
|
||||
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| a | b | ts | a | c | ts |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
| 1 | 10 | 1970-01-01T00:00:01 | 1 | 100 | 1970-01-01T00:00:04 |
|
||||
| 2 | 20 | 1970-01-01T00:00:02 | 2 | 200 | 1970-01-01T00:00:05 |
|
||||
| 3 | 30 | 1970-01-01T00:00:03 | | | |
|
||||
+---+----+---------------------+---+-----+---------------------+
|
||||
|
||||
DROP TABLE right_t;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE left_t;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
26
tests/cases/standalone/common/join/left_outer_join.sql
Normal file
26
tests/cases/standalone/common/join/left_outer_join.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/left_outer/test_left_outer.test
|
||||
-- Tests LEFT OUTER JOIN functionality
|
||||
|
||||
CREATE TABLE left_t (a INTEGER, b INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE right_t (a INTEGER, c INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO left_t VALUES (1, 10, 1000), (2, 20, 2000), (3, 30, 3000);
|
||||
|
||||
INSERT INTO right_t VALUES (1, 100, 4000), (2, 200, 5000), (4, 400, 6000);
|
||||
|
||||
-- Basic LEFT JOIN
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a ORDER BY left_t.a;
|
||||
|
||||
-- LEFT JOIN with WHERE on left table
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a WHERE left_t.b > 15 ORDER BY left_t.a;
|
||||
|
||||
-- LEFT JOIN with WHERE on joined result
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a WHERE right_t.c IS NULL ORDER BY left_t.a;
|
||||
|
||||
-- LEFT JOIN with complex condition
|
||||
SELECT * FROM left_t LEFT JOIN right_t ON left_t.a = right_t.a AND left_t.b < 25 ORDER BY left_t.a;
|
||||
|
||||
DROP TABLE right_t;
|
||||
|
||||
DROP TABLE left_t;
|
||||
183
tests/cases/standalone/common/join/multi_way_joins.result
Normal file
183
tests/cases/standalone/common/join/multi_way_joins.result
Normal file
@@ -0,0 +1,183 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ multi-way join tests
|
||||
-- Tests complex multi-table join scenarios
|
||||
CREATE TABLE users_multi(user_id INTEGER, username VARCHAR, email VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE posts_multi(post_id INTEGER, user_id INTEGER, title VARCHAR, content TEXT, created_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE comments_multi(comment_id INTEGER, post_id INTEGER, user_id INTEGER, comment_text VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE likes_multi(like_id INTEGER, post_id INTEGER, user_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO users_multi VALUES
|
||||
(1, 'alice', 'alice@example.com', 1000), (2, 'bob', 'bob@example.com', 2000),
|
||||
(3, 'charlie', 'charlie@example.com', 3000), (4, 'diana', 'diana@example.com', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO posts_multi VALUES
|
||||
(1, 1, 'First Post', 'Hello World', '2023-01-01', 1000),
|
||||
(2, 2, 'Tech Talk', 'About databases', '2023-01-02', 2000),
|
||||
(3, 1, 'Update', 'Progress report', '2023-01-03', 3000),
|
||||
(4, 3, 'Discussion', 'Open topic', '2023-01-04', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO comments_multi VALUES
|
||||
(1, 1, 2, 'Great post!', 1000), (2, 1, 3, 'Thanks for sharing', 2000),
|
||||
(3, 2, 1, 'Very informative', 3000), (4, 3, 4, 'Keep it up', 4000),
|
||||
(5, 4, 2, 'Interesting discussion', 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO likes_multi VALUES
|
||||
(1, 1, 2, 1000), (2, 1, 3, 2000), (3, 1, 4, 3000),
|
||||
(4, 2, 1, 4000), (5, 2, 3, 5000), (6, 3, 2, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
-- Four-way join
|
||||
SELECT
|
||||
u.username as author,
|
||||
p.title,
|
||||
c.comment_text,
|
||||
commenter.username as commenter
|
||||
FROM users_multi u
|
||||
INNER JOIN posts_multi p ON u.user_id = p.user_id
|
||||
INNER JOIN comments_multi c ON p.post_id = c.post_id
|
||||
INNER JOIN users_multi commenter ON c.user_id = commenter.user_id
|
||||
ORDER BY p.post_id, c.comment_id;
|
||||
|
||||
+---------+------------+------------------------+-----------+
|
||||
| author | title | comment_text | commenter |
|
||||
+---------+------------+------------------------+-----------+
|
||||
| alice | First Post | Great post! | bob |
|
||||
| alice | First Post | Thanks for sharing | charlie |
|
||||
| bob | Tech Talk | Very informative | alice |
|
||||
| alice | Update | Keep it up | diana |
|
||||
| charlie | Discussion | Interesting discussion | bob |
|
||||
+---------+------------+------------------------+-----------+
|
||||
|
||||
-- Multi-way join with aggregation
|
||||
SELECT
|
||||
u.username,
|
||||
COUNT(DISTINCT p.post_id) as posts_created,
|
||||
COUNT(DISTINCT c.comment_id) as comments_made,
|
||||
COUNT(DISTINCT l.like_id) as likes_given
|
||||
FROM users_multi u
|
||||
LEFT JOIN posts_multi p ON u.user_id = p.user_id
|
||||
LEFT JOIN comments_multi c ON u.user_id = c.user_id
|
||||
LEFT JOIN likes_multi l ON u.user_id = l.user_id
|
||||
GROUP BY u.user_id, u.username
|
||||
ORDER BY posts_created DESC, comments_made DESC;
|
||||
|
||||
+----------+---------------+---------------+-------------+
|
||||
| username | posts_created | comments_made | likes_given |
|
||||
+----------+---------------+---------------+-------------+
|
||||
| alice | 2 | 1 | 1 |
|
||||
| bob | 1 | 2 | 2 |
|
||||
| charlie | 1 | 1 | 2 |
|
||||
| diana | 0 | 1 | 1 |
|
||||
+----------+---------------+---------------+-------------+
|
||||
|
||||
-- Post engagement analysis
|
||||
SELECT
|
||||
p.title,
|
||||
u.username as author,
|
||||
COUNT(DISTINCT c.comment_id) as comment_count,
|
||||
COUNT(DISTINCT l.like_id) as like_count,
|
||||
COUNT(DISTINCT c.user_id) as unique_commenters
|
||||
FROM posts_multi p
|
||||
INNER JOIN users_multi u ON p.user_id = u.user_id
|
||||
LEFT JOIN comments_multi c ON p.post_id = c.post_id
|
||||
LEFT JOIN likes_multi l ON p.post_id = l.post_id
|
||||
GROUP BY p.post_id, p.title, u.username
|
||||
ORDER BY like_count DESC, comment_count DESC;
|
||||
|
||||
+------------+---------+---------------+------------+-------------------+
|
||||
| title | author | comment_count | like_count | unique_commenters |
|
||||
+------------+---------+---------------+------------+-------------------+
|
||||
| First Post | alice | 2 | 3 | 2 |
|
||||
| Tech Talk | bob | 1 | 2 | 1 |
|
||||
| Update | alice | 1 | 1 | 1 |
|
||||
| Discussion | charlie | 1 | 0 | 1 |
|
||||
+------------+---------+---------------+------------+-------------------+
|
||||
|
||||
-- Complex multi-join with conditions
|
||||
SELECT
|
||||
author.username as post_author,
|
||||
p.title,
|
||||
commenter.username as commenter,
|
||||
liker.username as liker
|
||||
FROM posts_multi p
|
||||
INNER JOIN users_multi author ON p.user_id = author.user_id
|
||||
INNER JOIN comments_multi c ON p.post_id = c.post_id
|
||||
INNER JOIN users_multi commenter ON c.user_id = commenter.user_id
|
||||
INNER JOIN likes_multi l ON p.post_id = l.post_id
|
||||
INNER JOIN users_multi liker ON l.user_id = liker.user_id
|
||||
WHERE author.user_id != commenter.user_id
|
||||
AND author.user_id != liker.user_id
|
||||
ORDER BY p.post_id, commenter.user_id, liker.user_id;
|
||||
|
||||
+-------------+------------+-----------+---------+
|
||||
| post_author | title | commenter | liker |
|
||||
+-------------+------------+-----------+---------+
|
||||
| alice | First Post | bob | bob |
|
||||
| alice | First Post | bob | charlie |
|
||||
| alice | First Post | bob | diana |
|
||||
| alice | First Post | charlie | bob |
|
||||
| alice | First Post | charlie | charlie |
|
||||
| alice | First Post | charlie | diana |
|
||||
| bob | Tech Talk | alice | alice |
|
||||
| bob | Tech Talk | alice | charlie |
|
||||
| alice | Update | diana | bob |
|
||||
+-------------+------------+-----------+---------+
|
||||
|
||||
-- Multi-join with subqueries
|
||||
SELECT
|
||||
u.username,
|
||||
popular_posts.title,
|
||||
popular_posts.engagement_score
|
||||
FROM users_multi u
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
p.post_id, p.user_id, p.title,
|
||||
COUNT(DISTINCT l.like_id) + COUNT(DISTINCT c.comment_id) as engagement_score
|
||||
FROM posts_multi p
|
||||
LEFT JOIN likes_multi l ON p.post_id = l.post_id
|
||||
LEFT JOIN comments_multi c ON p.post_id = c.post_id
|
||||
GROUP BY p.post_id, p.user_id, p.title
|
||||
HAVING COUNT(DISTINCT l.like_id) + COUNT(DISTINCT c.comment_id) > 2
|
||||
) popular_posts ON u.user_id = popular_posts.user_id
|
||||
ORDER BY popular_posts.engagement_score DESC;
|
||||
|
||||
+----------+------------+------------------+
|
||||
| username | title | engagement_score |
|
||||
+----------+------------+------------------+
|
||||
| alice | First Post | 5 |
|
||||
| bob | Tech Talk | 3 |
|
||||
+----------+------------+------------------+
|
||||
|
||||
DROP TABLE users_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE posts_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE comments_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE likes_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
110
tests/cases/standalone/common/join/multi_way_joins.sql
Normal file
110
tests/cases/standalone/common/join/multi_way_joins.sql
Normal file
@@ -0,0 +1,110 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ multi-way join tests
|
||||
-- Tests complex multi-table join scenarios
|
||||
|
||||
CREATE TABLE users_multi(user_id INTEGER, username VARCHAR, email VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE posts_multi(post_id INTEGER, user_id INTEGER, title VARCHAR, content TEXT, created_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE comments_multi(comment_id INTEGER, post_id INTEGER, user_id INTEGER, comment_text VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE likes_multi(like_id INTEGER, post_id INTEGER, user_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO users_multi VALUES
|
||||
(1, 'alice', 'alice@example.com', 1000), (2, 'bob', 'bob@example.com', 2000),
|
||||
(3, 'charlie', 'charlie@example.com', 3000), (4, 'diana', 'diana@example.com', 4000);
|
||||
|
||||
INSERT INTO posts_multi VALUES
|
||||
(1, 1, 'First Post', 'Hello World', '2023-01-01', 1000),
|
||||
(2, 2, 'Tech Talk', 'About databases', '2023-01-02', 2000),
|
||||
(3, 1, 'Update', 'Progress report', '2023-01-03', 3000),
|
||||
(4, 3, 'Discussion', 'Open topic', '2023-01-04', 4000);
|
||||
|
||||
INSERT INTO comments_multi VALUES
|
||||
(1, 1, 2, 'Great post!', 1000), (2, 1, 3, 'Thanks for sharing', 2000),
|
||||
(3, 2, 1, 'Very informative', 3000), (4, 3, 4, 'Keep it up', 4000),
|
||||
(5, 4, 2, 'Interesting discussion', 5000);
|
||||
|
||||
INSERT INTO likes_multi VALUES
|
||||
(1, 1, 2, 1000), (2, 1, 3, 2000), (3, 1, 4, 3000),
|
||||
(4, 2, 1, 4000), (5, 2, 3, 5000), (6, 3, 2, 6000);
|
||||
|
||||
-- Four-way join
|
||||
SELECT
|
||||
u.username as author,
|
||||
p.title,
|
||||
c.comment_text,
|
||||
commenter.username as commenter
|
||||
FROM users_multi u
|
||||
INNER JOIN posts_multi p ON u.user_id = p.user_id
|
||||
INNER JOIN comments_multi c ON p.post_id = c.post_id
|
||||
INNER JOIN users_multi commenter ON c.user_id = commenter.user_id
|
||||
ORDER BY p.post_id, c.comment_id;
|
||||
|
||||
-- Multi-way join with aggregation
|
||||
SELECT
|
||||
u.username,
|
||||
COUNT(DISTINCT p.post_id) as posts_created,
|
||||
COUNT(DISTINCT c.comment_id) as comments_made,
|
||||
COUNT(DISTINCT l.like_id) as likes_given
|
||||
FROM users_multi u
|
||||
LEFT JOIN posts_multi p ON u.user_id = p.user_id
|
||||
LEFT JOIN comments_multi c ON u.user_id = c.user_id
|
||||
LEFT JOIN likes_multi l ON u.user_id = l.user_id
|
||||
GROUP BY u.user_id, u.username
|
||||
ORDER BY posts_created DESC, comments_made DESC;
|
||||
|
||||
-- Post engagement analysis
|
||||
SELECT
|
||||
p.title,
|
||||
u.username as author,
|
||||
COUNT(DISTINCT c.comment_id) as comment_count,
|
||||
COUNT(DISTINCT l.like_id) as like_count,
|
||||
COUNT(DISTINCT c.user_id) as unique_commenters
|
||||
FROM posts_multi p
|
||||
INNER JOIN users_multi u ON p.user_id = u.user_id
|
||||
LEFT JOIN comments_multi c ON p.post_id = c.post_id
|
||||
LEFT JOIN likes_multi l ON p.post_id = l.post_id
|
||||
GROUP BY p.post_id, p.title, u.username
|
||||
ORDER BY like_count DESC, comment_count DESC;
|
||||
|
||||
-- Complex multi-join with conditions
|
||||
SELECT
|
||||
author.username as post_author,
|
||||
p.title,
|
||||
commenter.username as commenter,
|
||||
liker.username as liker
|
||||
FROM posts_multi p
|
||||
INNER JOIN users_multi author ON p.user_id = author.user_id
|
||||
INNER JOIN comments_multi c ON p.post_id = c.post_id
|
||||
INNER JOIN users_multi commenter ON c.user_id = commenter.user_id
|
||||
INNER JOIN likes_multi l ON p.post_id = l.post_id
|
||||
INNER JOIN users_multi liker ON l.user_id = liker.user_id
|
||||
WHERE author.user_id != commenter.user_id
|
||||
AND author.user_id != liker.user_id
|
||||
ORDER BY p.post_id, commenter.user_id, liker.user_id;
|
||||
|
||||
-- Multi-join with subqueries
|
||||
SELECT
|
||||
u.username,
|
||||
popular_posts.title,
|
||||
popular_posts.engagement_score
|
||||
FROM users_multi u
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
p.post_id, p.user_id, p.title,
|
||||
COUNT(DISTINCT l.like_id) + COUNT(DISTINCT c.comment_id) as engagement_score
|
||||
FROM posts_multi p
|
||||
LEFT JOIN likes_multi l ON p.post_id = l.post_id
|
||||
LEFT JOIN comments_multi c ON p.post_id = c.post_id
|
||||
GROUP BY p.post_id, p.user_id, p.title
|
||||
HAVING COUNT(DISTINCT l.like_id) + COUNT(DISTINCT c.comment_id) > 2
|
||||
) popular_posts ON u.user_id = popular_posts.user_id
|
||||
ORDER BY popular_posts.engagement_score DESC;
|
||||
|
||||
DROP TABLE users_multi;
|
||||
|
||||
DROP TABLE posts_multi;
|
||||
|
||||
DROP TABLE comments_multi;
|
||||
|
||||
DROP TABLE likes_multi;
|
||||
87
tests/cases/standalone/common/join/multiple_joins.result
Normal file
87
tests/cases/standalone/common/join/multiple_joins.result
Normal file
@@ -0,0 +1,87 @@
|
||||
-- Migrated from DuckDB test: Multiple join scenarios
|
||||
-- Tests multiple table joins
|
||||
CREATE TABLE customers_multi("id" INTEGER, "name" VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE orders_multi("id" INTEGER, customer_id INTEGER, product_id INTEGER, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE products_multi("id" INTEGER, "name" VARCHAR, category VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO customers_multi VALUES (1, 'Alice', 'NYC', 1000), (2, 'Bob', 'LA', 2000), (3, 'Carol', 'Chicago', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO orders_multi VALUES (101, 1, 1001, 150.0, 4000), (102, 2, 1002, 200.0, 5000), (103, 1, 1001, 150.0, 6000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO products_multi VALUES (1001, 'Laptop', 'Electronics', 1000.0, 7000), (1002, 'Book', 'Education', 25.0, 8000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
-- Three table join
|
||||
SELECT c."name", p."name" as product, o.amount
|
||||
FROM customers_multi c
|
||||
JOIN orders_multi o ON c."id" = o.customer_id
|
||||
JOIN products_multi p ON o.product_id = p."id"
|
||||
ORDER BY c."name", p."name";
|
||||
|
||||
+-------+---------+--------+
|
||||
| name | product | amount |
|
||||
+-------+---------+--------+
|
||||
| Alice | Laptop | 150.0 |
|
||||
| Alice | Laptop | 150.0 |
|
||||
| Bob | Book | 200.0 |
|
||||
+-------+---------+--------+
|
||||
|
||||
-- Multiple joins with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
p.category,
|
||||
COUNT(*) as order_count,
|
||||
SUM(o.amount) as total_amount
|
||||
FROM customers_multi c
|
||||
JOIN orders_multi o ON c."id" = o.customer_id
|
||||
JOIN products_multi p ON o.product_id = p."id"
|
||||
GROUP BY c.city, p.category
|
||||
ORDER BY c.city, p.category;
|
||||
|
||||
+------+-------------+-------------+--------------+
|
||||
| city | category | order_count | total_amount |
|
||||
+------+-------------+-------------+--------------+
|
||||
| LA | Education | 1 | 200.0 |
|
||||
| NYC | Electronics | 2 | 300.0 |
|
||||
+------+-------------+-------------+--------------+
|
||||
|
||||
-- Mixed join types
|
||||
SELECT c."name", p."name" as product, o.amount
|
||||
FROM customers_multi c
|
||||
LEFT JOIN orders_multi o ON c."id" = o.customer_id
|
||||
INNER JOIN products_multi p ON o.product_id = p."id"
|
||||
ORDER BY c."name", p."name" NULLS LAST;
|
||||
|
||||
+-------+---------+--------+
|
||||
| name | product | amount |
|
||||
+-------+---------+--------+
|
||||
| Alice | Laptop | 150.0 |
|
||||
| Alice | Laptop | 150.0 |
|
||||
| Bob | Book | 200.0 |
|
||||
+-------+---------+--------+
|
||||
|
||||
DROP TABLE products_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE orders_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE customers_multi;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
46
tests/cases/standalone/common/join/multiple_joins.sql
Normal file
46
tests/cases/standalone/common/join/multiple_joins.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
-- Migrated from DuckDB test: Multiple join scenarios
|
||||
-- Tests multiple table joins
|
||||
|
||||
CREATE TABLE customers_multi("id" INTEGER, "name" VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE orders_multi("id" INTEGER, customer_id INTEGER, product_id INTEGER, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE products_multi("id" INTEGER, "name" VARCHAR, category VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO customers_multi VALUES (1, 'Alice', 'NYC', 1000), (2, 'Bob', 'LA', 2000), (3, 'Carol', 'Chicago', 3000);
|
||||
|
||||
INSERT INTO orders_multi VALUES (101, 1, 1001, 150.0, 4000), (102, 2, 1002, 200.0, 5000), (103, 1, 1001, 150.0, 6000);
|
||||
|
||||
INSERT INTO products_multi VALUES (1001, 'Laptop', 'Electronics', 1000.0, 7000), (1002, 'Book', 'Education', 25.0, 8000);
|
||||
|
||||
-- Three table join
|
||||
SELECT c."name", p."name" as product, o.amount
|
||||
FROM customers_multi c
|
||||
JOIN orders_multi o ON c."id" = o.customer_id
|
||||
JOIN products_multi p ON o.product_id = p."id"
|
||||
ORDER BY c."name", p."name";
|
||||
|
||||
-- Multiple joins with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
p.category,
|
||||
COUNT(*) as order_count,
|
||||
SUM(o.amount) as total_amount
|
||||
FROM customers_multi c
|
||||
JOIN orders_multi o ON c."id" = o.customer_id
|
||||
JOIN products_multi p ON o.product_id = p."id"
|
||||
GROUP BY c.city, p.category
|
||||
ORDER BY c.city, p.category;
|
||||
|
||||
-- Mixed join types
|
||||
SELECT c."name", p."name" as product, o.amount
|
||||
FROM customers_multi c
|
||||
LEFT JOIN orders_multi o ON c."id" = o.customer_id
|
||||
INNER JOIN products_multi p ON o.product_id = p."id"
|
||||
ORDER BY c."name", p."name" NULLS LAST;
|
||||
|
||||
DROP TABLE products_multi;
|
||||
|
||||
DROP TABLE orders_multi;
|
||||
|
||||
DROP TABLE customers_multi;
|
||||
60
tests/cases/standalone/common/join/natural_join.result
Normal file
60
tests/cases/standalone/common/join/natural_join.result
Normal file
@@ -0,0 +1,60 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/natural/natural_join.test
|
||||
-- Tests NATURAL JOIN functionality
|
||||
CREATE TABLE emp_natural("id" INTEGER, "name" VARCHAR, dept_id INTEGER, ts1 TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE dept_natural(dept_id INTEGER, dept_name VARCHAR, ts2 TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO emp_natural VALUES (1, 'Alice', 10, 1000), (2, 'Bob', 20, 2000), (3, 'Carol', 10, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO dept_natural VALUES (10, 'Engineering', 4000), (20, 'Sales', 5000), (30, 'Marketing', 6000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- NATURAL JOIN (joins on common column names)
|
||||
SELECT * FROM emp_natural NATURAL JOIN dept_natural ORDER BY "id";
|
||||
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| id | name | ts1 | dept_id | dept_name | ts2 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
| 2 | Bob | 1970-01-01T00:00:02 | 20 | Sales | 1970-01-01T00:00:05 |
|
||||
| 3 | Carol | 1970-01-01T00:00:03 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
|
||||
-- NATURAL LEFT JOIN
|
||||
SELECT * FROM emp_natural NATURAL LEFT JOIN dept_natural ORDER BY "id";
|
||||
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| id | name | ts1 | dept_id | dept_name | ts2 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
| 2 | Bob | 1970-01-01T00:00:02 | 20 | Sales | 1970-01-01T00:00:05 |
|
||||
| 3 | Carol | 1970-01-01T00:00:03 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
|
||||
-- NATURAL RIGHT JOIN
|
||||
SELECT * FROM emp_natural NATURAL RIGHT JOIN dept_natural ORDER BY dept_id;
|
||||
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| id | name | ts1 | dept_id | dept_name | ts2 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
| 1 | Alice | 1970-01-01T00:00:01 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
| 3 | Carol | 1970-01-01T00:00:03 | 10 | Engineering | 1970-01-01T00:00:04 |
|
||||
| 2 | Bob | 1970-01-01T00:00:02 | 20 | Sales | 1970-01-01T00:00:05 |
|
||||
| | | | 30 | Marketing | 1970-01-01T00:00:06 |
|
||||
+----+-------+---------------------+---------+-------------+---------------------+
|
||||
|
||||
DROP TABLE dept_natural;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE emp_natural;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
23
tests/cases/standalone/common/join/natural_join.sql
Normal file
23
tests/cases/standalone/common/join/natural_join.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/natural/natural_join.test
|
||||
-- Tests NATURAL JOIN functionality
|
||||
|
||||
CREATE TABLE emp_natural("id" INTEGER, "name" VARCHAR, dept_id INTEGER, ts1 TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE dept_natural(dept_id INTEGER, dept_name VARCHAR, ts2 TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO emp_natural VALUES (1, 'Alice', 10, 1000), (2, 'Bob', 20, 2000), (3, 'Carol', 10, 3000);
|
||||
|
||||
INSERT INTO dept_natural VALUES (10, 'Engineering', 4000), (20, 'Sales', 5000), (30, 'Marketing', 6000);
|
||||
|
||||
-- NATURAL JOIN (joins on common column names)
|
||||
SELECT * FROM emp_natural NATURAL JOIN dept_natural ORDER BY "id";
|
||||
|
||||
-- NATURAL LEFT JOIN
|
||||
SELECT * FROM emp_natural NATURAL LEFT JOIN dept_natural ORDER BY "id";
|
||||
|
||||
-- NATURAL RIGHT JOIN
|
||||
SELECT * FROM emp_natural NATURAL RIGHT JOIN dept_natural ORDER BY dept_id;
|
||||
|
||||
DROP TABLE dept_natural;
|
||||
|
||||
DROP TABLE emp_natural;
|
||||
113
tests/cases/standalone/common/join/natural_join_advanced.result
Normal file
113
tests/cases/standalone/common/join/natural_join_advanced.result
Normal file
@@ -0,0 +1,113 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/natural/ advanced tests
|
||||
-- Tests advanced natural join patterns
|
||||
CREATE TABLE employees_nat(emp_id INTEGER, "name" VARCHAR, dept_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE departments_nat(dept_id INTEGER, dept_name VARCHAR, budget INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE dept_locations(dept_id INTEGER, "location" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO employees_nat VALUES
|
||||
(1, 'Alice', 10, 1000), (2, 'Bob', 20, 2000), (3, 'Charlie', 10, 3000),
|
||||
(4, 'Diana', 30, 4000), (5, 'Eve', 20, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO departments_nat VALUES
|
||||
(10, 'Engineering', 500000, 1000), (20, 'Marketing', 300000, 2000), (30, 'Sales', 200000, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO dept_locations VALUES
|
||||
(10, 'Building A', 1000), (20, 'Building B', 2000), (30, 'Building C', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Basic natural join
|
||||
SELECT * FROM employees_nat NATURAL JOIN departments_nat ORDER BY emp_id;
|
||||
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
| emp_id | name | dept_id | dept_name | budget | ts |
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
| 1 | Alice | 10 | Engineering | 500000 | 1970-01-01T00:00:01 |
|
||||
| 2 | Bob | 20 | Marketing | 300000 | 1970-01-01T00:00:02 |
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
|
||||
-- Natural join with filtering
|
||||
SELECT * FROM employees_nat NATURAL JOIN departments_nat
|
||||
WHERE budget > 250000 ORDER BY emp_id;
|
||||
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
| emp_id | name | dept_id | dept_name | budget | ts |
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
| 1 | Alice | 10 | Engineering | 500000 | 1970-01-01T00:00:01 |
|
||||
| 2 | Bob | 20 | Marketing | 300000 | 1970-01-01T00:00:02 |
|
||||
+--------+-------+---------+-------------+--------+---------------------+
|
||||
|
||||
-- Multi-table natural join
|
||||
SELECT
|
||||
emp_id, "name", dept_name, "location", budget
|
||||
FROM employees_nat
|
||||
NATURAL JOIN departments_nat
|
||||
NATURAL JOIN dept_locations
|
||||
ORDER BY emp_id;
|
||||
|
||||
+--------+-------+-------------+------------+--------+
|
||||
| emp_id | name | dept_name | location | budget |
|
||||
+--------+-------+-------------+------------+--------+
|
||||
| 1 | Alice | Engineering | Building A | 500000 |
|
||||
| 2 | Bob | Marketing | Building B | 300000 |
|
||||
+--------+-------+-------------+------------+--------+
|
||||
|
||||
-- Natural join with aggregation
|
||||
SELECT
|
||||
dept_name,
|
||||
COUNT(emp_id) as employee_count,
|
||||
AVG(budget) as avg_budget,
|
||||
"location"
|
||||
FROM employees_nat
|
||||
NATURAL JOIN departments_nat
|
||||
NATURAL JOIN dept_locations
|
||||
GROUP BY dept_name, "location", budget
|
||||
ORDER BY employee_count DESC, dept_name ASC;
|
||||
|
||||
+-------------+----------------+------------+------------+
|
||||
| dept_name | employee_count | avg_budget | location |
|
||||
+-------------+----------------+------------+------------+
|
||||
| Engineering | 1 | 500000.0 | Building A |
|
||||
| Marketing | 1 | 300000.0 | Building B |
|
||||
+-------------+----------------+------------+------------+
|
||||
|
||||
-- Natural join with expressions
|
||||
SELECT
|
||||
"name",
|
||||
dept_name,
|
||||
budget,
|
||||
CASE WHEN budget > 400000 THEN 'High Budget' ELSE 'Normal Budget' END as budget_tier
|
||||
FROM employees_nat NATURAL JOIN departments_nat
|
||||
ORDER BY budget DESC, "name";
|
||||
|
||||
+-------+-------------+--------+---------------+
|
||||
| name | dept_name | budget | budget_tier |
|
||||
+-------+-------------+--------+---------------+
|
||||
| Alice | Engineering | 500000 | High Budget |
|
||||
| Bob | Marketing | 300000 | Normal Budget |
|
||||
+-------+-------------+--------+---------------+
|
||||
|
||||
DROP TABLE employees_nat;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE departments_nat;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE dept_locations;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
60
tests/cases/standalone/common/join/natural_join_advanced.sql
Normal file
60
tests/cases/standalone/common/join/natural_join_advanced.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/natural/ advanced tests
|
||||
-- Tests advanced natural join patterns
|
||||
|
||||
CREATE TABLE employees_nat(emp_id INTEGER, "name" VARCHAR, dept_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE departments_nat(dept_id INTEGER, dept_name VARCHAR, budget INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE dept_locations(dept_id INTEGER, "location" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO employees_nat VALUES
|
||||
(1, 'Alice', 10, 1000), (2, 'Bob', 20, 2000), (3, 'Charlie', 10, 3000),
|
||||
(4, 'Diana', 30, 4000), (5, 'Eve', 20, 5000);
|
||||
|
||||
INSERT INTO departments_nat VALUES
|
||||
(10, 'Engineering', 500000, 1000), (20, 'Marketing', 300000, 2000), (30, 'Sales', 200000, 3000);
|
||||
|
||||
INSERT INTO dept_locations VALUES
|
||||
(10, 'Building A', 1000), (20, 'Building B', 2000), (30, 'Building C', 3000);
|
||||
|
||||
-- Basic natural join
|
||||
SELECT * FROM employees_nat NATURAL JOIN departments_nat ORDER BY emp_id;
|
||||
|
||||
-- Natural join with filtering
|
||||
SELECT * FROM employees_nat NATURAL JOIN departments_nat
|
||||
WHERE budget > 250000 ORDER BY emp_id;
|
||||
|
||||
-- Multi-table natural join
|
||||
SELECT
|
||||
emp_id, "name", dept_name, "location", budget
|
||||
FROM employees_nat
|
||||
NATURAL JOIN departments_nat
|
||||
NATURAL JOIN dept_locations
|
||||
ORDER BY emp_id;
|
||||
|
||||
-- Natural join with aggregation
|
||||
SELECT
|
||||
dept_name,
|
||||
COUNT(emp_id) as employee_count,
|
||||
AVG(budget) as avg_budget,
|
||||
"location"
|
||||
FROM employees_nat
|
||||
NATURAL JOIN departments_nat
|
||||
NATURAL JOIN dept_locations
|
||||
GROUP BY dept_name, "location", budget
|
||||
ORDER BY employee_count DESC, dept_name ASC;
|
||||
|
||||
-- Natural join with expressions
|
||||
SELECT
|
||||
"name",
|
||||
dept_name,
|
||||
budget,
|
||||
CASE WHEN budget > 400000 THEN 'High Budget' ELSE 'Normal Budget' END as budget_tier
|
||||
FROM employees_nat NATURAL JOIN departments_nat
|
||||
ORDER BY budget DESC, "name";
|
||||
|
||||
DROP TABLE employees_nat;
|
||||
|
||||
DROP TABLE departments_nat;
|
||||
|
||||
DROP TABLE dept_locations;
|
||||
133
tests/cases/standalone/common/join/outer_join_complex.result
Normal file
133
tests/cases/standalone/common/join/outer_join_complex.result
Normal file
@@ -0,0 +1,133 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/full_outer/ complex tests
|
||||
-- Tests complex outer join scenarios
|
||||
CREATE TABLE employees(emp_id INTEGER, "name" VARCHAR, dept_id INTEGER, salary INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE departments(dept_id INTEGER, dept_name VARCHAR, manager_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE projects(proj_id INTEGER, proj_name VARCHAR, dept_id INTEGER, budget INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO employees VALUES
|
||||
(1, 'Alice', 10, 75000, 1000), (2, 'Bob', 20, 65000, 2000),
|
||||
(3, 'Charlie', 10, 80000, 3000), (4, 'Diana', 30, 70000, 4000), (5, 'Eve', NULL, 60000, 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
INSERT INTO departments VALUES
|
||||
(10, 'Engineering', 1, 1000), (20, 'Marketing', 2, 2000), (40, 'HR', NULL, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO projects VALUES
|
||||
(101, 'ProjectA', 10, 100000, 1000), (102, 'ProjectB', 20, 150000, 2000),
|
||||
(103, 'ProjectC', 30, 75000, 3000), (104, 'ProjectD', 50, 200000, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Full outer join with multiple conditions
|
||||
SELECT
|
||||
e.emp_id, e."name", d.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
ORDER BY e.emp_id, d.dept_id;
|
||||
|
||||
+--------+---------+---------+-------------+
|
||||
| emp_id | name | dept_id | dept_name |
|
||||
+--------+---------+---------+-------------+
|
||||
| 1 | Alice | 10 | Engineering |
|
||||
| 2 | Bob | 20 | Marketing |
|
||||
| 3 | Charlie | 10 | Engineering |
|
||||
| 4 | Diana | | |
|
||||
| 5 | Eve | | |
|
||||
| | | 40 | HR |
|
||||
+--------+---------+---------+-------------+
|
||||
|
||||
-- Left outer join with IS NULL filter
|
||||
SELECT
|
||||
e.emp_id, e."name", e.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
LEFT OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
WHERE d.dept_id IS NULL
|
||||
ORDER BY e.emp_id;
|
||||
|
||||
+--------+-------+---------+-----------+
|
||||
| emp_id | name | dept_id | dept_name |
|
||||
+--------+-------+---------+-----------+
|
||||
| 4 | Diana | 30 | |
|
||||
| 5 | Eve | | |
|
||||
+--------+-------+---------+-----------+
|
||||
|
||||
-- Right outer join
|
||||
SELECT
|
||||
e.emp_id, e."name", d.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
RIGHT OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
ORDER BY d.dept_id, e.emp_id;
|
||||
|
||||
+--------+---------+---------+-------------+
|
||||
| emp_id | name | dept_id | dept_name |
|
||||
+--------+---------+---------+-------------+
|
||||
| 1 | Alice | 10 | Engineering |
|
||||
| 3 | Charlie | 10 | Engineering |
|
||||
| 2 | Bob | 20 | Marketing |
|
||||
| | | 40 | HR |
|
||||
+--------+---------+---------+-------------+
|
||||
|
||||
-- Triple outer join
|
||||
SELECT
|
||||
e."name", d.dept_name, p.proj_name, p.budget
|
||||
FROM employees e
|
||||
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
FULL OUTER JOIN projects p ON d.dept_id = p.dept_id
|
||||
ORDER BY e.emp_id, p.proj_id;
|
||||
|
||||
+---------+-------------+-----------+--------+
|
||||
| name | dept_name | proj_name | budget |
|
||||
+---------+-------------+-----------+--------+
|
||||
| Alice | Engineering | ProjectA | 100000 |
|
||||
| Bob | Marketing | ProjectB | 150000 |
|
||||
| Charlie | Engineering | ProjectA | 100000 |
|
||||
| Diana | | | |
|
||||
| Eve | | | |
|
||||
| | | ProjectC | 75000 |
|
||||
| | | ProjectD | 200000 |
|
||||
| | HR | | |
|
||||
+---------+-------------+-----------+--------+
|
||||
|
||||
-- Outer join with aggregation
|
||||
SELECT
|
||||
d.dept_name,
|
||||
COUNT(e.emp_id) as employee_count,
|
||||
AVG(e.salary) as avg_salary,
|
||||
SUM(p.budget) as total_project_budget
|
||||
FROM departments d
|
||||
LEFT OUTER JOIN employees e ON d.dept_id = e.dept_id
|
||||
LEFT OUTER JOIN projects p ON d.dept_id = p.dept_id
|
||||
GROUP BY d.dept_name
|
||||
ORDER BY d.dept_name;
|
||||
|
||||
+-------------+----------------+------------+----------------------+
|
||||
| dept_name | employee_count | avg_salary | total_project_budget |
|
||||
+-------------+----------------+------------+----------------------+
|
||||
| Engineering | 2 | 77500.0 | 200000 |
|
||||
| HR | 0 | | |
|
||||
| Marketing | 1 | 65000.0 | 150000 |
|
||||
+-------------+----------------+------------+----------------------+
|
||||
|
||||
DROP TABLE employees;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE departments;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE projects;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
67
tests/cases/standalone/common/join/outer_join_complex.sql
Normal file
67
tests/cases/standalone/common/join/outer_join_complex.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/full_outer/ complex tests
|
||||
-- Tests complex outer join scenarios
|
||||
|
||||
CREATE TABLE employees(emp_id INTEGER, "name" VARCHAR, dept_id INTEGER, salary INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE departments(dept_id INTEGER, dept_name VARCHAR, manager_id INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE projects(proj_id INTEGER, proj_name VARCHAR, dept_id INTEGER, budget INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO employees VALUES
|
||||
(1, 'Alice', 10, 75000, 1000), (2, 'Bob', 20, 65000, 2000),
|
||||
(3, 'Charlie', 10, 80000, 3000), (4, 'Diana', 30, 70000, 4000), (5, 'Eve', NULL, 60000, 5000);
|
||||
|
||||
INSERT INTO departments VALUES
|
||||
(10, 'Engineering', 1, 1000), (20, 'Marketing', 2, 2000), (40, 'HR', NULL, 3000);
|
||||
|
||||
INSERT INTO projects VALUES
|
||||
(101, 'ProjectA', 10, 100000, 1000), (102, 'ProjectB', 20, 150000, 2000),
|
||||
(103, 'ProjectC', 30, 75000, 3000), (104, 'ProjectD', 50, 200000, 4000);
|
||||
|
||||
-- Full outer join with multiple conditions
|
||||
SELECT
|
||||
e.emp_id, e."name", d.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
ORDER BY e.emp_id, d.dept_id;
|
||||
|
||||
-- Left outer join with IS NULL filter
|
||||
SELECT
|
||||
e.emp_id, e."name", e.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
LEFT OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
WHERE d.dept_id IS NULL
|
||||
ORDER BY e.emp_id;
|
||||
|
||||
-- Right outer join
|
||||
SELECT
|
||||
e.emp_id, e."name", d.dept_id, d.dept_name
|
||||
FROM employees e
|
||||
RIGHT OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
ORDER BY d.dept_id, e.emp_id;
|
||||
|
||||
-- Triple outer join
|
||||
SELECT
|
||||
e."name", d.dept_name, p.proj_name, p.budget
|
||||
FROM employees e
|
||||
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
|
||||
FULL OUTER JOIN projects p ON d.dept_id = p.dept_id
|
||||
ORDER BY e.emp_id, p.proj_id;
|
||||
|
||||
-- Outer join with aggregation
|
||||
SELECT
|
||||
d.dept_name,
|
||||
COUNT(e.emp_id) as employee_count,
|
||||
AVG(e.salary) as avg_salary,
|
||||
SUM(p.budget) as total_project_budget
|
||||
FROM departments d
|
||||
LEFT OUTER JOIN employees e ON d.dept_id = e.dept_id
|
||||
LEFT OUTER JOIN projects p ON d.dept_id = p.dept_id
|
||||
GROUP BY d.dept_name
|
||||
ORDER BY d.dept_name;
|
||||
|
||||
DROP TABLE employees;
|
||||
|
||||
DROP TABLE departments;
|
||||
|
||||
DROP TABLE projects;
|
||||
113
tests/cases/standalone/common/join/right_join_patterns.result
Normal file
113
tests/cases/standalone/common/join/right_join_patterns.result
Normal file
@@ -0,0 +1,113 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/right_outer/ pattern tests
|
||||
-- Tests right join patterns
|
||||
CREATE TABLE inventory(item_id INTEGER, item_name VARCHAR, stock_quantity INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE purchase_orders(po_id INTEGER, item_id INTEGER, ordered_qty INTEGER, order_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO inventory VALUES
|
||||
(1, 'Widget A', 100, 1000), (2, 'Widget B', 50, 2000), (3, 'Widget C', 0, 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO purchase_orders VALUES
|
||||
(1, 1, 25, '2023-01-01', 1000), (2, 2, 30, '2023-01-02', 2000),
|
||||
(3, 4, 15, '2023-01-03', 3000), (4, 5, 40, '2023-01-04', 4000),
|
||||
(5, 1, 10, '2023-01-05', 5000);
|
||||
|
||||
Affected Rows: 5
|
||||
|
||||
-- Right join to show all orders (including for non-inventory items)
|
||||
SELECT
|
||||
po.po_id, po.ordered_qty, po.order_date,
|
||||
COALESCE(i.item_name, 'Unknown Item') as item_name,
|
||||
COALESCE(i.stock_quantity, 0) as current_stock
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
ORDER BY po.order_date, po.po_id;
|
||||
|
||||
+-------+-------------+------------+--------------+---------------+
|
||||
| po_id | ordered_qty | order_date | item_name | current_stock |
|
||||
+-------+-------------+------------+--------------+---------------+
|
||||
| 1 | 25 | 2023-01-01 | Widget A | 100 |
|
||||
| 2 | 30 | 2023-01-02 | Widget B | 50 |
|
||||
| 3 | 15 | 2023-01-03 | Unknown Item | 0 |
|
||||
| 4 | 40 | 2023-01-04 | Unknown Item | 0 |
|
||||
| 5 | 10 | 2023-01-05 | Widget A | 100 |
|
||||
+-------+-------------+------------+--------------+---------------+
|
||||
|
||||
-- Right join with aggregation
|
||||
SELECT
|
||||
po.item_id,
|
||||
COUNT(po.po_id) as order_count,
|
||||
SUM(po.ordered_qty) as total_ordered,
|
||||
COALESCE(MAX(i.stock_quantity), 0) as stock_level,
|
||||
CASE WHEN i.item_id IS NULL THEN 'Not In Inventory' ELSE 'In Stock' END as inventory_status
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
GROUP BY po.item_id, i.item_id
|
||||
ORDER BY order_count DESC, po.item_id ASC;
|
||||
|
||||
+---------+-------------+---------------+-------------+------------------+
|
||||
| item_id | order_count | total_ordered | stock_level | inventory_status |
|
||||
+---------+-------------+---------------+-------------+------------------+
|
||||
| 1 | 2 | 35 | 100 | In Stock |
|
||||
| 2 | 1 | 30 | 50 | In Stock |
|
||||
| 4 | 1 | 15 | 0 | Not In Inventory |
|
||||
| 5 | 1 | 40 | 0 | Not In Inventory |
|
||||
+---------+-------------+---------------+-------------+------------------+
|
||||
|
||||
-- Right join to identify missing inventory records
|
||||
SELECT
|
||||
po.item_id as missing_item_id,
|
||||
COUNT(*) as orders_for_missing_item,
|
||||
SUM(po.ordered_qty) as total_qty_ordered
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
WHERE i.item_id IS NULL
|
||||
GROUP BY po.item_id
|
||||
ORDER BY total_qty_ordered DESC, po.item_id ASC;
|
||||
|
||||
+-----------------+-------------------------+-------------------+
|
||||
| missing_item_id | orders_for_missing_item | total_qty_ordered |
|
||||
+-----------------+-------------------------+-------------------+
|
||||
| 5 | 1 | 40 |
|
||||
| 4 | 1 | 15 |
|
||||
+-----------------+-------------------------+-------------------+
|
||||
|
||||
-- Right join with filtering and conditions
|
||||
SELECT
|
||||
po.po_id,
|
||||
po.item_id,
|
||||
po.ordered_qty,
|
||||
COALESCE(i.item_name, 'Missing from inventory') as item_description,
|
||||
CASE
|
||||
WHEN i.item_id IS NULL THEN 'Order for unknown item'
|
||||
WHEN i.stock_quantity < po.ordered_qty THEN 'Insufficient stock'
|
||||
ELSE 'Can fulfill'
|
||||
END as fulfillment_status
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
ORDER BY po.order_date;
|
||||
|
||||
+-------+---------+-------------+------------------------+------------------------+
|
||||
| po_id | item_id | ordered_qty | item_description | fulfillment_status |
|
||||
+-------+---------+-------------+------------------------+------------------------+
|
||||
| 1 | 1 | 25 | Widget A | Can fulfill |
|
||||
| 2 | 2 | 30 | Widget B | Can fulfill |
|
||||
| 3 | 4 | 15 | Missing from inventory | Order for unknown item |
|
||||
| 4 | 5 | 40 | Missing from inventory | Order for unknown item |
|
||||
| 5 | 1 | 10 | Widget A | Can fulfill |
|
||||
+-------+---------+-------------+------------------------+------------------------+
|
||||
|
||||
DROP TABLE inventory;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE purchase_orders;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
65
tests/cases/standalone/common/join/right_join_patterns.sql
Normal file
65
tests/cases/standalone/common/join/right_join_patterns.sql
Normal file
@@ -0,0 +1,65 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/right_outer/ pattern tests
|
||||
-- Tests right join patterns
|
||||
|
||||
CREATE TABLE inventory(item_id INTEGER, item_name VARCHAR, stock_quantity INTEGER, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE purchase_orders(po_id INTEGER, item_id INTEGER, ordered_qty INTEGER, order_date DATE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO inventory VALUES
|
||||
(1, 'Widget A', 100, 1000), (2, 'Widget B', 50, 2000), (3, 'Widget C', 0, 3000);
|
||||
|
||||
INSERT INTO purchase_orders VALUES
|
||||
(1, 1, 25, '2023-01-01', 1000), (2, 2, 30, '2023-01-02', 2000),
|
||||
(3, 4, 15, '2023-01-03', 3000), (4, 5, 40, '2023-01-04', 4000),
|
||||
(5, 1, 10, '2023-01-05', 5000);
|
||||
|
||||
-- Right join to show all orders (including for non-inventory items)
|
||||
SELECT
|
||||
po.po_id, po.ordered_qty, po.order_date,
|
||||
COALESCE(i.item_name, 'Unknown Item') as item_name,
|
||||
COALESCE(i.stock_quantity, 0) as current_stock
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
ORDER BY po.order_date, po.po_id;
|
||||
|
||||
-- Right join with aggregation
|
||||
SELECT
|
||||
po.item_id,
|
||||
COUNT(po.po_id) as order_count,
|
||||
SUM(po.ordered_qty) as total_ordered,
|
||||
COALESCE(MAX(i.stock_quantity), 0) as stock_level,
|
||||
CASE WHEN i.item_id IS NULL THEN 'Not In Inventory' ELSE 'In Stock' END as inventory_status
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
GROUP BY po.item_id, i.item_id
|
||||
ORDER BY order_count DESC, po.item_id ASC;
|
||||
|
||||
-- Right join to identify missing inventory records
|
||||
SELECT
|
||||
po.item_id as missing_item_id,
|
||||
COUNT(*) as orders_for_missing_item,
|
||||
SUM(po.ordered_qty) as total_qty_ordered
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
WHERE i.item_id IS NULL
|
||||
GROUP BY po.item_id
|
||||
ORDER BY total_qty_ordered DESC, po.item_id ASC;
|
||||
|
||||
-- Right join with filtering and conditions
|
||||
SELECT
|
||||
po.po_id,
|
||||
po.item_id,
|
||||
po.ordered_qty,
|
||||
COALESCE(i.item_name, 'Missing from inventory') as item_description,
|
||||
CASE
|
||||
WHEN i.item_id IS NULL THEN 'Order for unknown item'
|
||||
WHEN i.stock_quantity < po.ordered_qty THEN 'Insufficient stock'
|
||||
ELSE 'Can fulfill'
|
||||
END as fulfillment_status
|
||||
FROM inventory i
|
||||
RIGHT JOIN purchase_orders po ON i.item_id = po.item_id
|
||||
ORDER BY po.order_date;
|
||||
|
||||
DROP TABLE inventory;
|
||||
|
||||
DROP TABLE purchase_orders;
|
||||
58
tests/cases/standalone/common/join/right_outer_join.result
Normal file
58
tests/cases/standalone/common/join/right_outer_join.result
Normal file
@@ -0,0 +1,58 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/right_outer/test_right_outer.test
|
||||
-- Tests RIGHT OUTER JOIN scenarios
|
||||
CREATE TABLE products_right("id" INTEGER, "name" VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE inventory_right(product_id INTEGER, stock INTEGER, "warehouse" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO products_right VALUES (1, 'Laptop', 1000.0, 1000), (2, 'Mouse', 25.0, 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO inventory_right VALUES (1, 5, 'WH1', 3000), (2, 10, 'WH1', 4000), (3, 15, 'WH2', 5000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- Basic RIGHT JOIN
|
||||
SELECT * FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id ORDER BY i.product_id;
|
||||
|
||||
+----+--------+--------+---------------------+------------+-------+-----------+---------------------+
|
||||
| id | name | price | ts | product_id | stock | warehouse | ts |
|
||||
+----+--------+--------+---------------------+------------+-------+-----------+---------------------+
|
||||
| 1 | Laptop | 1000.0 | 1970-01-01T00:00:01 | 1 | 5 | WH1 | 1970-01-01T00:00:03 |
|
||||
| 2 | Mouse | 25.0 | 1970-01-01T00:00:02 | 2 | 10 | WH1 | 1970-01-01T00:00:04 |
|
||||
| | | | | 3 | 15 | WH2 | 1970-01-01T00:00:05 |
|
||||
+----+--------+--------+---------------------+------------+-------+-----------+---------------------+
|
||||
|
||||
-- RIGHT JOIN with WHERE on left table
|
||||
SELECT * FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id WHERE p.price IS NULL ORDER BY i.product_id;
|
||||
|
||||
+----+------+-------+----+------------+-------+-----------+---------------------+
|
||||
| id | name | price | ts | product_id | stock | warehouse | ts |
|
||||
+----+------+-------+----+------------+-------+-----------+---------------------+
|
||||
| | | | | 3 | 15 | WH2 | 1970-01-01T00:00:05 |
|
||||
+----+------+-------+----+------------+-------+-----------+---------------------+
|
||||
|
||||
-- RIGHT JOIN with aggregation
|
||||
SELECT i."warehouse", COUNT(*) as items, AVG(p.price) as avg_price
|
||||
FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id
|
||||
GROUP BY i."warehouse" ORDER BY i."warehouse";
|
||||
|
||||
+-----------+-------+-----------+
|
||||
| warehouse | items | avg_price |
|
||||
+-----------+-------+-----------+
|
||||
| WH1 | 2 | 512.5 |
|
||||
| WH2 | 1 | |
|
||||
+-----------+-------+-----------+
|
||||
|
||||
DROP TABLE inventory_right;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE products_right;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
25
tests/cases/standalone/common/join/right_outer_join.sql
Normal file
25
tests/cases/standalone/common/join/right_outer_join.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/right_outer/test_right_outer.test
|
||||
-- Tests RIGHT OUTER JOIN scenarios
|
||||
|
||||
CREATE TABLE products_right("id" INTEGER, "name" VARCHAR, price DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE inventory_right(product_id INTEGER, stock INTEGER, "warehouse" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO products_right VALUES (1, 'Laptop', 1000.0, 1000), (2, 'Mouse', 25.0, 2000);
|
||||
|
||||
INSERT INTO inventory_right VALUES (1, 5, 'WH1', 3000), (2, 10, 'WH1', 4000), (3, 15, 'WH2', 5000);
|
||||
|
||||
-- Basic RIGHT JOIN
|
||||
SELECT * FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id ORDER BY i.product_id;
|
||||
|
||||
-- RIGHT JOIN with WHERE on left table
|
||||
SELECT * FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id WHERE p.price IS NULL ORDER BY i.product_id;
|
||||
|
||||
-- RIGHT JOIN with aggregation
|
||||
SELECT i."warehouse", COUNT(*) as items, AVG(p.price) as avg_price
|
||||
FROM products_right p RIGHT JOIN inventory_right i ON p."id" = i.product_id
|
||||
GROUP BY i."warehouse" ORDER BY i."warehouse";
|
||||
|
||||
DROP TABLE inventory_right;
|
||||
|
||||
DROP TABLE products_right;
|
||||
90
tests/cases/standalone/common/join/self_join.result
Normal file
90
tests/cases/standalone/common/join/self_join.result
Normal file
@@ -0,0 +1,90 @@
|
||||
-- Migrated from DuckDB test: Self join scenarios
|
||||
-- Tests self join operations
|
||||
CREATE TABLE employees_self(
|
||||
"id" INTEGER,
|
||||
"name" VARCHAR,
|
||||
manager_id INTEGER,
|
||||
salary INTEGER,
|
||||
ts TIMESTAMP TIME INDEX
|
||||
);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO employees_self VALUES
|
||||
(1, 'CEO', NULL, 100000, 1000),
|
||||
(2, 'Manager1', 1, 80000, 2000),
|
||||
(3, 'Manager2', 1, 75000, 3000),
|
||||
(4, 'Employee1', 2, 50000, 4000),
|
||||
(5, 'Employee2', 2, 55000, 5000),
|
||||
(6, 'Employee3', 3, 48000, 6000);
|
||||
|
||||
Affected Rows: 6
|
||||
|
||||
-- Basic self join to get employee-manager pairs
|
||||
SELECT e."name" as employee, m."name" as manager
|
||||
FROM employees_self e
|
||||
LEFT JOIN employees_self m ON e.manager_id = m."id"
|
||||
ORDER BY e."id";
|
||||
|
||||
+-----------+----------+
|
||||
| employee | manager |
|
||||
+-----------+----------+
|
||||
| CEO | |
|
||||
| Manager1 | CEO |
|
||||
| Manager2 | CEO |
|
||||
| Employee1 | Manager1 |
|
||||
| Employee2 | Manager1 |
|
||||
| Employee3 | Manager2 |
|
||||
+-----------+----------+
|
||||
|
||||
-- Self join to find employees earning more than their manager
|
||||
SELECT e."name" as employee, e.salary, m."name" as manager, m.salary as manager_salary
|
||||
FROM employees_self e
|
||||
JOIN employees_self m ON e.manager_id = m."id"
|
||||
WHERE e.salary > m.salary
|
||||
ORDER BY e."name";
|
||||
|
||||
++
|
||||
++
|
||||
|
||||
-- Self join to find colleagues (same manager)
|
||||
SELECT e1."name" as employee1, e2."name" as employee2, m."name" as shared_manager
|
||||
FROM employees_self e1
|
||||
JOIN employees_self e2 ON e1.manager_id = e2.manager_id AND e1."id" < e2."id"
|
||||
JOIN employees_self m ON e1.manager_id = m."id"
|
||||
ORDER BY shared_manager, employee1;
|
||||
|
||||
+-----------+-----------+----------------+
|
||||
| employee1 | employee2 | shared_manager |
|
||||
+-----------+-----------+----------------+
|
||||
| Manager1 | Manager2 | CEO |
|
||||
| Employee1 | Employee2 | Manager1 |
|
||||
+-----------+-----------+----------------+
|
||||
|
||||
-- Hierarchical query using self join
|
||||
SELECT
|
||||
e."name" as employee,
|
||||
e.salary,
|
||||
m."name" as manager,
|
||||
COUNT(sub."id") as direct_reports
|
||||
FROM employees_self e
|
||||
LEFT JOIN employees_self m ON e.manager_id = m."id"
|
||||
LEFT JOIN employees_self sub ON e."id" = sub.manager_id
|
||||
GROUP BY e."id", e."name", e.salary, m."name"
|
||||
ORDER BY e."id";
|
||||
|
||||
+-----------+--------+----------+----------------+
|
||||
| employee | salary | manager | direct_reports |
|
||||
+-----------+--------+----------+----------------+
|
||||
| CEO | 100000 | | 2 |
|
||||
| Manager1 | 80000 | CEO | 2 |
|
||||
| Manager2 | 75000 | CEO | 1 |
|
||||
| Employee1 | 50000 | Manager1 | 0 |
|
||||
| Employee2 | 55000 | Manager1 | 0 |
|
||||
| Employee3 | 48000 | Manager2 | 0 |
|
||||
+-----------+--------+----------+----------------+
|
||||
|
||||
DROP TABLE employees_self;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
52
tests/cases/standalone/common/join/self_join.sql
Normal file
52
tests/cases/standalone/common/join/self_join.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
-- Migrated from DuckDB test: Self join scenarios
|
||||
-- Tests self join operations
|
||||
|
||||
CREATE TABLE employees_self(
|
||||
"id" INTEGER,
|
||||
"name" VARCHAR,
|
||||
manager_id INTEGER,
|
||||
salary INTEGER,
|
||||
ts TIMESTAMP TIME INDEX
|
||||
);
|
||||
|
||||
INSERT INTO employees_self VALUES
|
||||
(1, 'CEO', NULL, 100000, 1000),
|
||||
(2, 'Manager1', 1, 80000, 2000),
|
||||
(3, 'Manager2', 1, 75000, 3000),
|
||||
(4, 'Employee1', 2, 50000, 4000),
|
||||
(5, 'Employee2', 2, 55000, 5000),
|
||||
(6, 'Employee3', 3, 48000, 6000);
|
||||
|
||||
-- Basic self join to get employee-manager pairs
|
||||
SELECT e."name" as employee, m."name" as manager
|
||||
FROM employees_self e
|
||||
LEFT JOIN employees_self m ON e.manager_id = m."id"
|
||||
ORDER BY e."id";
|
||||
|
||||
-- Self join to find employees earning more than their manager
|
||||
SELECT e."name" as employee, e.salary, m."name" as manager, m.salary as manager_salary
|
||||
FROM employees_self e
|
||||
JOIN employees_self m ON e.manager_id = m."id"
|
||||
WHERE e.salary > m.salary
|
||||
ORDER BY e."name";
|
||||
|
||||
-- Self join to find colleagues (same manager)
|
||||
SELECT e1."name" as employee1, e2."name" as employee2, m."name" as shared_manager
|
||||
FROM employees_self e1
|
||||
JOIN employees_self e2 ON e1.manager_id = e2.manager_id AND e1."id" < e2."id"
|
||||
JOIN employees_self m ON e1.manager_id = m."id"
|
||||
ORDER BY shared_manager, employee1;
|
||||
|
||||
-- Hierarchical query using self join
|
||||
SELECT
|
||||
e."name" as employee,
|
||||
e.salary,
|
||||
m."name" as manager,
|
||||
COUNT(sub."id") as direct_reports
|
||||
FROM employees_self e
|
||||
LEFT JOIN employees_self m ON e.manager_id = m."id"
|
||||
LEFT JOIN employees_self sub ON e."id" = sub.manager_id
|
||||
GROUP BY e."id", e."name", e.salary, m."name"
|
||||
ORDER BY e."id";
|
||||
|
||||
DROP TABLE employees_self;
|
||||
125
tests/cases/standalone/common/join/using_clause_joins.result
Normal file
125
tests/cases/standalone/common/join/using_clause_joins.result
Normal file
@@ -0,0 +1,125 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ USING clause tests
|
||||
-- Tests USING clause join syntax
|
||||
CREATE TABLE orders_using(order_id INTEGER, customer_id INTEGER, total DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE customers_using(customer_id INTEGER, customer_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE payments_using(payment_id INTEGER, order_id INTEGER, amount DOUBLE, "method" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO orders_using VALUES
|
||||
(1, 101, 250.00, 1000), (2, 102, 180.00, 2000), (3, 101, 420.00, 3000), (4, 103, 95.00, 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
INSERT INTO customers_using VALUES
|
||||
(101, 'John Doe', 'NYC', 1000), (102, 'Jane Smith', 'LA', 2000), (103, 'Bob Wilson', 'Chicago', 3000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
INSERT INTO payments_using VALUES
|
||||
(1, 1, 250.00, 'Credit', 1000), (2, 2, 180.00, 'Debit', 2000),
|
||||
(3, 3, 420.00, 'Credit', 3000), (4, 4, 95.00, 'Cash', 4000);
|
||||
|
||||
Affected Rows: 4
|
||||
|
||||
-- Basic USING clause join
|
||||
SELECT order_id, customer_name, total
|
||||
FROM orders_using
|
||||
INNER JOIN customers_using USING (customer_id)
|
||||
ORDER BY order_id;
|
||||
|
||||
+----------+---------------+-------+
|
||||
| order_id | customer_name | total |
|
||||
+----------+---------------+-------+
|
||||
| 1 | John Doe | 250.0 |
|
||||
| 2 | Jane Smith | 180.0 |
|
||||
| 3 | John Doe | 420.0 |
|
||||
| 4 | Bob Wilson | 95.0 |
|
||||
+----------+---------------+-------+
|
||||
|
||||
-- Multiple USING clause joins
|
||||
SELECT o.order_id, c.customer_name, p.amount, p."method"
|
||||
FROM orders_using o
|
||||
INNER JOIN customers_using c USING (customer_id)
|
||||
INNER JOIN payments_using p USING (order_id)
|
||||
ORDER BY o.order_id;
|
||||
|
||||
+----------+---------------+--------+--------+
|
||||
| order_id | customer_name | amount | method |
|
||||
+----------+---------------+--------+--------+
|
||||
| 1 | John Doe | 250.0 | Credit |
|
||||
| 2 | Jane Smith | 180.0 | Debit |
|
||||
| 3 | John Doe | 420.0 | Credit |
|
||||
| 4 | Bob Wilson | 95.0 | Cash |
|
||||
+----------+---------------+--------+--------+
|
||||
|
||||
-- LEFT JOIN with USING
|
||||
SELECT c.customer_name, o.order_id, o.total
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
ORDER BY c.customer_id, o.order_id;
|
||||
|
||||
+---------------+----------+-------+
|
||||
| customer_name | order_id | total |
|
||||
+---------------+----------+-------+
|
||||
| John Doe | 1 | 250.0 |
|
||||
| John Doe | 3 | 420.0 |
|
||||
| Jane Smith | 2 | 180.0 |
|
||||
| Bob Wilson | 4 | 95.0 |
|
||||
+---------------+----------+-------+
|
||||
|
||||
-- USING with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
COUNT(o.order_id) as order_count,
|
||||
SUM(o.total) as total_sales
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
GROUP BY c.city
|
||||
ORDER BY total_sales DESC;
|
||||
|
||||
+---------+-------------+-------------+
|
||||
| city | order_count | total_sales |
|
||||
+---------+-------------+-------------+
|
||||
| NYC | 2 | 670.0 |
|
||||
| LA | 1 | 180.0 |
|
||||
| Chicago | 1 | 95.0 |
|
||||
+---------+-------------+-------------+
|
||||
|
||||
-- USING with complex expressions
|
||||
SELECT
|
||||
c.customer_name,
|
||||
COUNT(o.order_id) as order_count,
|
||||
COALESCE(SUM(o.total), 0) as total_spent
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
GROUP BY c.customer_id, c.customer_name
|
||||
HAVING COUNT(o.order_id) != 0
|
||||
ORDER BY total_spent DESC;
|
||||
|
||||
+---------------+-------------+-------------+
|
||||
| customer_name | order_count | total_spent |
|
||||
+---------------+-------------+-------------+
|
||||
| John Doe | 2 | 670.0 |
|
||||
| Jane Smith | 1 | 180.0 |
|
||||
| Bob Wilson | 1 | 95.0 |
|
||||
+---------------+-------------+-------------+
|
||||
|
||||
DROP TABLE orders_using;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE customers_using;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE payments_using;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
64
tests/cases/standalone/common/join/using_clause_joins.sql
Normal file
64
tests/cases/standalone/common/join/using_clause_joins.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/ USING clause tests
|
||||
-- Tests USING clause join syntax
|
||||
|
||||
CREATE TABLE orders_using(order_id INTEGER, customer_id INTEGER, total DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE customers_using(customer_id INTEGER, customer_name VARCHAR, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE payments_using(payment_id INTEGER, order_id INTEGER, amount DOUBLE, "method" VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO orders_using VALUES
|
||||
(1, 101, 250.00, 1000), (2, 102, 180.00, 2000), (3, 101, 420.00, 3000), (4, 103, 95.00, 4000);
|
||||
|
||||
INSERT INTO customers_using VALUES
|
||||
(101, 'John Doe', 'NYC', 1000), (102, 'Jane Smith', 'LA', 2000), (103, 'Bob Wilson', 'Chicago', 3000);
|
||||
|
||||
INSERT INTO payments_using VALUES
|
||||
(1, 1, 250.00, 'Credit', 1000), (2, 2, 180.00, 'Debit', 2000),
|
||||
(3, 3, 420.00, 'Credit', 3000), (4, 4, 95.00, 'Cash', 4000);
|
||||
|
||||
-- Basic USING clause join
|
||||
SELECT order_id, customer_name, total
|
||||
FROM orders_using
|
||||
INNER JOIN customers_using USING (customer_id)
|
||||
ORDER BY order_id;
|
||||
|
||||
-- Multiple USING clause joins
|
||||
SELECT o.order_id, c.customer_name, p.amount, p."method"
|
||||
FROM orders_using o
|
||||
INNER JOIN customers_using c USING (customer_id)
|
||||
INNER JOIN payments_using p USING (order_id)
|
||||
ORDER BY o.order_id;
|
||||
|
||||
-- LEFT JOIN with USING
|
||||
SELECT c.customer_name, o.order_id, o.total
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
ORDER BY c.customer_id, o.order_id;
|
||||
|
||||
-- USING with aggregation
|
||||
SELECT
|
||||
c.city,
|
||||
COUNT(o.order_id) as order_count,
|
||||
SUM(o.total) as total_sales
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
GROUP BY c.city
|
||||
ORDER BY total_sales DESC;
|
||||
|
||||
-- USING with complex expressions
|
||||
SELECT
|
||||
c.customer_name,
|
||||
COUNT(o.order_id) as order_count,
|
||||
COALESCE(SUM(o.total), 0) as total_spent
|
||||
FROM customers_using c
|
||||
LEFT JOIN orders_using o USING (customer_id)
|
||||
GROUP BY c.customer_id, c.customer_name
|
||||
HAVING COUNT(o.order_id) != 0
|
||||
ORDER BY total_spent DESC;
|
||||
|
||||
DROP TABLE orders_using;
|
||||
|
||||
DROP TABLE customers_using;
|
||||
|
||||
DROP TABLE payments_using;
|
||||
84
tests/cases/standalone/common/join/using_join.result
Normal file
84
tests/cases/standalone/common/join/using_join.result
Normal file
@@ -0,0 +1,84 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_using_join.test
|
||||
-- Tests JOIN USING clause
|
||||
CREATE TABLE users_join(user_id INTEGER, username VARCHAR, email VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE orders_join(order_id INTEGER, user_id INTEGER, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO users_join VALUES (1, 'alice', 'alice@test.com', 1000), (2, 'bob', 'bob@test.com', 2000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
INSERT INTO orders_join VALUES (101, 1, 150.0, 3000), (102, 1, 200.0, 4000), (103, 2, 75.0, 5000);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- JOIN USING (automatically joins on common column)
|
||||
SELECT * FROM users_join JOIN orders_join USING (user_id) ORDER BY order_id;
|
||||
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| username | email | ts | order_id | user_id | amount | ts |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 101 | 1 | 150.0 | 1970-01-01T00:00:03 |
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 102 | 1 | 200.0 | 1970-01-01T00:00:04 |
|
||||
| bob | bob@test.com | 1970-01-01T00:00:02 | 103 | 2 | 75.0 | 1970-01-01T00:00:05 |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
|
||||
-- LEFT JOIN USING
|
||||
SELECT * FROM users_join LEFT JOIN orders_join USING (user_id) ORDER BY user_id, order_id NULLS LAST;
|
||||
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| username | email | ts | order_id | user_id | amount | ts |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 101 | 1 | 150.0 | 1970-01-01T00:00:03 |
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 102 | 1 | 200.0 | 1970-01-01T00:00:04 |
|
||||
| bob | bob@test.com | 1970-01-01T00:00:02 | 103 | 2 | 75.0 | 1970-01-01T00:00:05 |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
|
||||
-- JOIN USING with WHERE
|
||||
SELECT * FROM users_join JOIN orders_join USING (user_id) WHERE amount > 100 ORDER BY amount;
|
||||
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| username | email | ts | order_id | user_id | amount | ts |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 101 | 1 | 150.0 | 1970-01-01T00:00:03 |
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 102 | 1 | 200.0 | 1970-01-01T00:00:04 |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+
|
||||
|
||||
-- Multiple table JOIN USING
|
||||
CREATE TABLE user_profiles(user_id INTEGER, age INTEGER, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO user_profiles VALUES (1, 25, 'NYC', 6000), (2, 30, 'LA', 7000);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
SELECT * FROM users_join
|
||||
JOIN orders_join USING (user_id)
|
||||
JOIN user_profiles USING (user_id)
|
||||
ORDER BY order_id;
|
||||
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+---------+-----+------+---------------------+
|
||||
| username | email | ts | order_id | user_id | amount | ts | user_id | age | city | ts |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+---------+-----+------+---------------------+
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 101 | 1 | 150.0 | 1970-01-01T00:00:03 | 1 | 25 | NYC | 1970-01-01T00:00:06 |
|
||||
| alice | alice@test.com | 1970-01-01T00:00:01 | 102 | 1 | 200.0 | 1970-01-01T00:00:04 | 1 | 25 | NYC | 1970-01-01T00:00:06 |
|
||||
| bob | bob@test.com | 1970-01-01T00:00:02 | 103 | 2 | 75.0 | 1970-01-01T00:00:05 | 2 | 30 | LA | 1970-01-01T00:00:07 |
|
||||
+----------+----------------+---------------------+----------+---------+--------+---------------------+---------+-----+------+---------------------+
|
||||
|
||||
DROP TABLE user_profiles;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE orders_join;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
DROP TABLE users_join;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
34
tests/cases/standalone/common/join/using_join.sql
Normal file
34
tests/cases/standalone/common/join/using_join.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Migrated from DuckDB test: test/sql/join/inner/test_using_join.test
|
||||
-- Tests JOIN USING clause
|
||||
|
||||
CREATE TABLE users_join(user_id INTEGER, username VARCHAR, email VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
CREATE TABLE orders_join(order_id INTEGER, user_id INTEGER, amount DOUBLE, ts TIMESTAMP TIME INDEX);
|
||||
|
||||
INSERT INTO users_join VALUES (1, 'alice', 'alice@test.com', 1000), (2, 'bob', 'bob@test.com', 2000);
|
||||
|
||||
INSERT INTO orders_join VALUES (101, 1, 150.0, 3000), (102, 1, 200.0, 4000), (103, 2, 75.0, 5000);
|
||||
|
||||
-- JOIN USING (automatically joins on common column)
|
||||
SELECT * FROM users_join JOIN orders_join USING (user_id) ORDER BY order_id;
|
||||
|
||||
-- LEFT JOIN USING
|
||||
SELECT * FROM users_join LEFT JOIN orders_join USING (user_id) ORDER BY user_id, order_id NULLS LAST;
|
||||
|
||||
-- JOIN USING with WHERE
|
||||
SELECT * FROM users_join JOIN orders_join USING (user_id) WHERE amount > 100 ORDER BY amount;
|
||||
|
||||
-- Multiple table JOIN USING
|
||||
CREATE TABLE user_profiles(user_id INTEGER, age INTEGER, city VARCHAR, ts TIMESTAMP TIME INDEX);
|
||||
INSERT INTO user_profiles VALUES (1, 25, 'NYC', 6000), (2, 30, 'LA', 7000);
|
||||
|
||||
SELECT * FROM users_join
|
||||
JOIN orders_join USING (user_id)
|
||||
JOIN user_profiles USING (user_id)
|
||||
ORDER BY order_id;
|
||||
|
||||
DROP TABLE user_profiles;
|
||||
|
||||
DROP TABLE orders_join;
|
||||
|
||||
DROP TABLE users_join;
|
||||
Reference in New Issue
Block a user