Chandralal
Chandralal

Reputation: 559

SQL Server : joins new syntax(ANSI vs. non-ANSI SQL JOIN syntax)

I have tried to convert old MS sql join syntax to new join syntax but number of rows in the results not matching.

Original 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

Converted 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

Findings

Results are same on both original SQL and modified SQL upto joining of 3 tables but when joining the fourth table (TableD) to the modified SQL, the number of rows returned is different.

Upvotes: 1

Views: 3518

Answers (1)

GarethD
GarethD

Reputation: 69759

The order of fields within predicates is important when using SQL Server's (deprecated) proprietary ANSI 89 join syntax *= or =*

So while

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

Is exactly the same as

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

The eqivalent

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

Is not the same as

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

This last query's ANSI 92 equivalent would be

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

Or if you dislike RIGHT JOIN as much as I do you would probably write:

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

So actually the equivalent query in ANSI 92 join syntax would involve starting with TableA, TableC and TableD (since these are the leading fields in the original WHERE Clause). Then since there is no direct link between the three, you end up with a cross join

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;

This is the equivalent rewrite, and explans the difference in the number of rows

WORKING EXAMPLE

Needs to be run on SQL Server 2008 or earlier with compatibility level 80 or less

-- 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;

Upvotes: 5

Related Questions