SQL Server:加入新语法(ANSI与非ANSI SQL JOIN语法)

Cha*_*lal 1 sql sql-server

我试图将旧的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
Run Code Online (Sandbox Code Playgroud)

转换的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
Run Code Online (Sandbox Code Playgroud)

发现

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

Gar*_*thD 5

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

所以虽然

SELECT  *
FROM    TableA AS A
        LEFT JOIN TableB AS B
            ON A.ColA = B.ColB;
Run Code Online (Sandbox Code Playgroud)

完全一样

SELECT  *
FROM    TableA AS A
        LEFT JOIN TableB AS B
            ON B.ColB = A.ColA;     -- NOTE ORDER HERE
Run Code Online (Sandbox Code Playgroud)

等价的

SELECT  *
FROM    TableA AS A, TableB AS b
WHERE   A.ColA *= B.ColB;
Run Code Online (Sandbox Code Playgroud)

是不一样的

SELECT  *
FROM    TableA AS A, TableB AS b
WHERE   B.ColA *= A.ColB;
Run Code Online (Sandbox Code Playgroud)

最后一个查询的ANSI 92等价物将是

SELECT  *
FROM    TableA AS A
        RIGHT JOIN TableB AS B
            ON A.ColA = B.ColB;
Run Code Online (Sandbox Code Playgroud)

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

SELECT  *
FROM    TableB AS B
        LEFT OUTER JOIN TableA AS A
            ON B.ColB = A.ColA;
Run Code Online (Sandbox Code Playgroud)

实际上,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;
Run Code Online (Sandbox Code Playgroud)

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

工作实例

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

-- 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;
Run Code Online (Sandbox Code Playgroud)