【问题标题】:SQL Server : joins new syntax(ANSI vs. non-ANSI SQL JOIN syntax)SQL Server:加入新语法(ANSI 与非 ANSI SQL JOIN 语法)
【发布时间】:2016-03-03 15:57:25
【问题描述】:

我尝试将旧的 MS sql 连接语法转换为新的连接语法,但结果中的行数不匹配。

原始SQL:

select  
    b.Amount
from 
    TableA a, TableB b,TableC c, TableD d 
where 
    a.inv_no *= b.inv_no and 
    a.inv_item *= b.inv_item and 
    c.currency *= b.cash_ccy and
    d.tx_code *= b.cash_receipt

转换后的 SQL:

SELECT
    b.AMOUNT
FROM
    (TableA AS a 
LEFT OUTER JOIN
    TableB AS b ON a.INV_NO = b.INV_NO 
                AND a.inv_item = b.inv_item 
LEFT OUTER JOIN 
    TableC AS c ON c.currency = b.cash_ccy)
LEFT OUTER JOIN
    TableD as d ON d.tx_code = b.cash_receipt

调查结果

原始 SQL 和修改后的 SQL 最多连接 3 个表的结果相同,但将第四个表 (TableD) 连接到修改后的 SQL 时,返回的行数不同。

【问题讨论】:

  • 去掉括号。
  • 前两个 LEFT OUTER JOINs 周围的括号 (...) 是什么?
  • * 在旧式联接中被置于强制一侧。所以看起来有些连接必须反转。
  • *= 应该做什么?
  • 所有连接都发生了变化,实际上是INNER。如果您选择b.amount,为什么会有a 中的一行没有b 中的对应行?

标签: sql sql-server


【解决方案1】:

在使用 SQL Server(已弃用)专有的 ANSI 89 连接语法 *==* 时,谓词中的字段顺序很重要

所以当

SELECT  *
FROM    TableA AS A
        LEFT JOIN TableB AS B
            ON A.ColA = B.ColB;

完全一样

SELECT  *
FROM    TableA AS A
        LEFT JOIN TableB AS B
            ON B.ColB = A.ColA;     -- NOTE ORDER HERE

等价

SELECT  *
FROM    TableA AS A, TableB AS b
WHERE   A.ColA *= B.ColB;

不一样

SELECT  *
FROM    TableA AS A, TableB AS b
WHERE   B.ColA *= A.ColB;

最后一个查询的 ANSI 92 等效项是

SELECT  *
FROM    TableA AS A
        RIGHT JOIN TableB AS B
            ON A.ColA = B.ColB;

或者如果你和我一样不喜欢RIGHT JOIN,你可能会写:

SELECT  *
FROM    TableB AS B
        LEFT OUTER JOIN TableA AS A
            ON B.ColB = A.ColA;

所以实际上 ANSI 92 连接语法中的等效查询将涉及从 TableA、TableC 和 TableD 开始(因为这些是原始 WHERE 子句中的前导字段)。那么由于三者之间没有直接联系,你最终得到了一个交叉连接

SELECT  b.Amount
FROM    TableA AS a     
        CROSS JOIN TableD AS d
        CROSS JOIN TableC AS c
        LEFT JOIN TableB AS B
            ON c.currency = b.cash_ccy
            AND d.tx_code = b.cash_receipt
            AND a.INV_NO = b.INV_NO 
            AND a.inv_item = b.inv_item;

这是等效的重写,并解释了行数的差异

工作示例

需要在 SQL Server 2008 或更早版本上运行,兼容级别为 80 或更低

-- SAMPLE DATA -- 
CREATE TABLE #TableA (Inv_No INT, Inv_item INT);
CREATE TABLE #TableB (Inv_No INT, Inv_item INT, cash_ccy INT, cash_receipt INT, Amount INT);
CREATE TABLE #TableC (currency INT);
CREATE TABLE #TableD (tx_code INT);

INSERT #TableA (inv_no, inv_item) VALUES (1, 1), (2, 2);
INSERT #TableB (inv_no, inv_item, cash_ccy, cash_receipt, Amount) VALUES (1, 1, 1, 1, 1), (2, 2, 2, 2, 2);
INSERT #TableC (currency) VALUES (1), (2), (3), (4);
INSERT #TableD (tx_code) VALUES (1), (2), (3), (4);

-- ORIGINAL QUERY(32 ROWS)
SELECT  
    b.Amount
FROM 
    #TableA a, #TableB b,#TableC c, #TableD d 
WHERE 
    a.inv_no *= b.inv_no and 
    a.inv_item *= b.inv_item and 
    c.currency *= b.cash_ccy and
    d.tx_code *= b.cash_receipt

-- INCORRECT ANSI 92 REWRITE (2 ROWS)
SELECT  b.AMOUNT
FROM    #TableA AS a 
        LEFT OUTER JOIN #TableB AS b 
            ON a.INV_NO = b.INV_NO 
            and a.inv_item = b.inv_item 
        LEFT OUTER JOIN #TableC AS c 
            ON c.currency = b.cash_ccy
        LEFT OUTER JOIN #TableD as d 
            ON d.tx_code = b.cash_receipt;


-- CORRECT ANSI 92 REWRITE (32 ROWS)
SELECT  b.Amount
FROM    #TableA AS a        
        CROSS JOIN #TableD AS d
        CROSS JOIN #TableC AS c
        LEFT JOIN #TableB AS B
            ON c.currency = b.cash_ccy
            AND d.tx_code = b.cash_receipt
            AND a.INV_NO = b.INV_NO 
            AND a.inv_item = b.inv_item;

【讨论】:

  • 可以将该查询简化为看起来不那么笨拙的东西吗?
  • @JoePhilllips 原来的查询有点疯狂,这确实是一个问题。你有点想从头开始重写整个事情,了解原始查询到底想要做什么,这是我们不太清楚的事情。这基本上是一种“机器翻译”——它产生相同的结果,但有点难看。
猜你喜欢
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 2015-09-30
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多