There are a few ways to compare two similar tables in SQL. Assuming PostgreSQL syntax, we might have this schema:
CREATE TABLE t1 (a INT, b INT, c INT); CREATE TABLE t2 (a INT, b INT, c INT); INSERT INTO t1 VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9); INSERT INTO t2 VALUES (4, 5, 6), (7, 8, 9), (10, 11, 12);
It is now possible to use UNION and EXCEPT as suggested by Chris Saxon:
In PostgreSQL, we can write:
(TABLE t1 EXCEPT TABLE t2) UNION (TABLE t2 EXCEPT TABLE t1) ORDER BY a, b, c
Notice how TABLE x is just standard SQL, and PostgreSQL, syntax sugar for SELECT * FROM x.
And we’ll get:
a |b |c | --|--|--| 1| 2| 3| 10|11|12|
Unfortunately, this requires two accesses to each table. Can we do it with a single access?
Using NATURAL FULL JOIN
Yes! Using NATURAL FULL JOIN, another rare use-case for this esoteric operator.
We can write this:
SELECT * FROM ( SELECT 't1' AS t1, t1.* FROM t1 ) t1 NATURAL FULL JOIN ( SELECT 't2' AS t2, t2.* FROM t2 ) t2 WHERE NOT (t1, t2) IS NOT NULL;
This produces:
a |b |c |t1|t2| --|--|--|--|--| 1| 2| 3|t1| | 10|11|12| |t2|
Why? Because a NATURAL JOIN is syntax sugar for joining using all the shared column names of the two tables, and the FULL JOIN makes sure we can retrieve also the columns that are not matched by the join predicate. Another way to write this is:
-- Use JOIN .. USING, instead of NATURAL JOIN SELECT * FROM ( SELECT 't1' AS t1, t1.* FROM t1 ) t1 FULL JOIN ( SELECT 't2' AS t2, t2.* FROM t2 ) t2 USING (a, b, c) WHERE NOT (t1, t2) IS NOT NULL;
Or:
-- Use JOIN .. ON, instead of JOIN .. USING SELECT coalesce(t1.a, t2.a) AS a, coalesce(t1.b, t2.b) AS b, coalesce(t1.c, t2.c) AS c, t1.t1, t2.t2 FROM ( SELECT 't1' AS t1, t1.* FROM t1 ) t1 FULL JOIN ( SELECT 't2' AS t2, t2.* FROM t2 ) t2 ON (t1.a, t1.b, t1.c) = (t2.a, t2.b, t2.c) WHERE NOT (t1, t2) IS NOT NULL;
Pros and cons
Pros and cons compared to the set operator solution using UNION and EXCEPT:
Pros
- Each table is accessed only once
- Comparison is now name based, not column index based, i.e. it can still work if only parts of the columns are the shared
Cons
- If index based column comparison was desired (because the tables are the same structurally, but do not share the exact same column names), then we’d have to rename each individual column to a common column name.
- If there’s duplicate data, there’s going to be a cartesian product, which might make this solution quite slower
Row value expression NULL predicate
Observe the usage of the esoteric NULL predicate for row value expressions, which uses the following truth table:
+-----------------------+-----------+---------------+---------------+-------------------+ | Expression | R IS NULL | R IS NOT NULL | NOT R IS NULL | NOT R IS NOT NULL | +-----------------------+-----------+---------------+---------------+-------------------+ | degree 1: null | true | false | false | true | | degree 1: not null | false | true | true | false | | degree > 1: all null | true | false | false | true | | degree > 1: some null | false | false | true | true | | degree > 1: none null | false | true | true | false | +-----------------------+-----------+---------------+---------------+-------------------+
Yes. R IS NULL and NOT R IS NOT NULL are not the same thing in SQL…
It’s just another way of writing:
SELECT * FROM ( SELECT 't1' AS t1, t1.* FROM t1 ) t1 NATURAL FULL JOIN ( SELECT 't2' AS t2, t2.* FROM t2 ) t2 WHERE t1 IS NOT NULL OR t2 IS NOT NULL;
from Java, SQL and jOOQ. https://ift.tt/3fx7ULW
via IFTTT


No comments:
Post a Comment