首页 > 解决方案 > 可以以某种方式将 UNION 转换为 JOIN 吗?

问题描述

这是一个有点奇怪的问题,但我想知道,如果使用两个结构相同的表,可以进行连接以模拟常见的联合查询,例如:

SELECT * FROM table1
UNION
SELECT * FROM table2

我唯一的(黑客)想法是有一个“NULL行”table1并将该值交叉连接到表2,然后对table2中的每个值使用COALESCE来“覆盖”table1中的那个......我知道正确的方法要做到这一点是与一个联合,但这只是我想知道是否有可能将一个联合转换为一个连接。

标签: mysqlsql

解决方案


这不能在 MySQL 中完成,但如果您使用的 SQL 版本支持FULL OUTER JOIN,则可以使用始终为假的JOIN条件来模拟UNION例如

SELECT COALESCE(t1.a, t2.a) AS a,
       COALESCE(t1.b, t2.b) AS b
FROM table1 t1
FULL OUTER JOIN table2 t2 ON 0 = 1

因为不支持FULL OUTER JOIN,所以在 MySQL 中是做不到的,模拟的方式FULL OUTER JOIN是用 a UNIONof aLEFT JOIN和 a RIGHT JOIN

更新

使用行号,可以模拟其中任一行号为 1 的两个表中的 a,然后再次使用UNION第二个表来复制两个行号均为 1 的行。然后一些复杂的行排序对行进行排序与以下顺序相同:JOINLEFT JOINFULL OUTER JOIN

WITH CTE1 AS (
  SELECT *, 
         ROW_NUMBER() OVER (ORDER BY RAND()) AS rn
  FROM table1
),
CTE2 AS (
  SELECT *, 
         ROW_NUMBER() OVER (ORDER BY RAND()) AS rn
  FROM table2
)
SELECT CASE WHEN c3.rn = 2 THEN CTE2.a 
            WHEN CTE2.rn = 1 THEN CTE1.a 
            ELSE CTE2.a END AS a,
       CASE WHEN c3.rn = 2 THEN CTE2.b
            WHEN CTE2.rn = 1 THEN CTE1.b 
            ELSE CTE2.b END AS b
FROM CTE1
JOIN CTE2 ON CTE1.rn = 1 OR CTE2.rn = 1
LEFT JOIN CTE2 c3 ON CTE1.rn = 1 AND CTE2.rn = 1 AND c3.rn IN (1, 2)
ORDER BY CTE2.rn, 
         CASE WHEN COALESCE(c3.rn, 1) = 1 THEN 0 ELSE 1 END,
         CTE1.rn

dbfiddle 上的演示

注意我使用 CTE 和窗口函数(自 MySQL 8 起可用)来生成行号;在早期版本中有很多方法可以模拟行号,在这种情况下,您需要用子查询替换 CTE。


推荐阅读