Force partial join order in SQL Server

 ̄綄美尐妖づ 提交于 2019-12-23 19:22:00

问题


Edit: People are having a hard time understanding what i want. So here's pretty pictures that explains it in excruciating detail.

First join Transactions to Strange:

The results so far

Customer  Invoice  TransactionID  Mass  Length                LeptonNumber
========  =======  =============  ====  ====================  ============
Ian       One      1              Ian   Judgement Spaulders   50
Ian       One      1              Ian   Glorious Breastplate  50
Chris     Two      2              Chris Barenavel             2

Now attempt to join remaining rows with Down:

The results so far

Customer  Invoice         TransactionID  Mass  Length                LeptonNumber
========  =======         =============  ====  ====================  ============
Ian       One             1              Ian   Judgement Spaulders   50
Ian       One             1              Ian   Glorious Breastplate  50
Chris     Two             2              Chris Barenavel             2
Jamie     Krol Blade      3              Jay   Krol Blade            90
Jay       Arcanite Reaper 4              Ian   Arcanite Reaper       90

Finally, join any leftover rows to Charmed:

The results so far

Customer  Invoice         TransactionID  Mass    Length                LeptonNumber
========  =======         =============  ====    ====================  ============
Ian       One             1              Ian     Judgement Spaulders   50
Ian       One             1              Ian     Glorious Breastplate  50
Chris     Two             2              Chris   Barenavel             2
Jamie     Krol Blade      3              Jay     Krol Blade            90
Jay       Arcanite Reaper 4              Ian     Arcanite Reaper       90
Potatoe   Dan Quayle      5              Potatoe Dan Quayle            90

And how look at the rows we have left over:

Giving me my desired results set

Customer  Invoice         TransactionID  Mass    Length                LeptonNumber
========  =======         =============  ====    ====================  ============
Ian       One             1              Ian     Judgement Spaulders   50
Ian       One             1              Ian     Glorious Breastplate  50
Chris     Two             2              Chris   Barenavel             2
Jamie     Krol Blade      3              Jay     Krol Blade            90
Jay       Arcanite Reaper 4              Ian     Arcanite Reaper       90
Potatoe   Dan Quayle      5              Potatoe Dan Quayle            90
Stapler   Alexstraza      6              NULL    NULL                  NULL

i have a main table:

Transactions
+----------+
|          |
|          | 
|          |
|          |
|          |
|          |
|          |
+----------+

i want each row in this table to join only one possible matching table:

Tranasctions        Strange
+----------+      +----------+
| row 1 ===|=====>| row 1    |         Down
| row 2 ===|=====>| row 2    |       +---------+
| row 3 ===|======+----------+======>| row 1   |        Charmed
| row 4 ===|========================>| row 2   |       +---------+
| row 5 ===|=========================+---------+======>| row 1   |
| row 6 ===|==========================================>| row 2   |
+----------+                                           +---------+

Which normally i'd perform as a join of Transactions to the set of Strange || Down || Charmed:

SELECT
   Transactions.*,
   Quarks.Mass,
   Quarks.Length,
   Quarks.LeptonNumber
FROM Transactions
    INNER JOIN NationalSecurityLetters
    ON Transactions.TransactionID = NationalSecurityLetters.ReferenceNumber

    LEFT JOIN (
       SELECT 'Strange' AS Type, * FROM Strange
       UNION ALL
       SELECT 'Down' AS Type, * FROM Down
       UNION ALL
       SELECT 'Charmed' AS Type, * FROM Charmed
    ) Quarks
    ON (
        (Quarks.Type = 'Strange' AND Transactions.Customer = Quarks.Mass)
        OR
        (Quarks.Type = 'Down' AND Transactions.Invoice = Quarks.Length)
        OR
        (Quarks.Type = 'Charmed' AND Transactions.Customer = Quarks.Length)    
    )       

The problem is that i want the join to happen in that preferred order:

  • Strange
  • Down
  • Charmed

It's entirely possible that a single Transaction can have matching entries in multiple tables. But for each possible JOIN of Transactions to the other tables, i want SQL Server to prefer the Strange table. If there's no match then go to the Down table. If there's no match to go the Charmed table.

If you find a match in      Prefer the matching row from
==========================  ============================
Strange                     Strange
Strange and Down            Strange
Strange, Down, and Charmed  Strange
Down                        Down
Down and Charmed            Down
Charmed                     Charmed
(no match?)                 (then there's no match)

i've thought about using an OPTION(FORCE ORDER) clause:

SELECT *
FROM Transactions
    INNER JOIN NationalSecurityLetters
    ON Transactions.TransactionID = NationalSecurityLetters.ReferenceNumber

    LEFT JOIN (
       SELECT 'Strange' AS Type, * FROM Strange
       UNION ALL
       SELECT 'Down' AS Type, * FROM Strange
       UNION ALL
       SELECT 'Charmed' AS Type, * FROM Strange
    ) Quarks
    ON (
        (Quarks.Type = 'Strange' AND Transactions.Customer = Quarks.Mass)
        OR
        (Quarks.Type = 'Down' AND Transactions.Invoice = Quarks.Length)
        OR
        (Quarks.Type = 'Charmed' AND Transactions.Customer = Quarks.Length)    
    )       
OPTION (FORCE ORDER)

But i don't want to force SQL Server to join

  • Transactions ==> NationalSecurityLetters, when it may be more advantageous to join
  • NationalSecurityLetters ==> Transactions


回答1:


Maybe this solution will help you:

SET ANSI_WARNINGS ON;
GO
BEGIN TRAN;

CREATE TABLE dbo.TableA (
    TableAID INT PRIMARY KEY,
    DescriptionA VARCHAR(50) NOT NULL
);
INSERT dbo.TableA 
VALUES (1,'A-1'), (2,'A-2');

CREATE TABLE dbo.TableB (
    TableBID INT PRIMARY KEY,
    DescriptionB VARCHAR(50) NOT NULL
);
INSERT dbo.TableB
VALUES (1,'B-1'), (2,'B-2'), (4,'B-4');

CREATE TABLE dbo.TableC (
    TableCID INT PRIMARY KEY,
    DescriptionC VARCHAR(50) NOT NULL
);
INSERT dbo.TableC
VALUES (1,'C-1'),(3,'C-3'), (4,'C-4');
GO

CREATE TABLE dbo.[Transaction] (
    TransactionID INT IDENTITY PRIMARY KEY,
    TranDate DATE NOT NULL,
    Col1 INT NULL
);
INSERT dbo.[Transaction]
VALUES ('20120101', 1), ('20120202',2), ('20120303',3), ('20120404',4), ('20120505',5);
GO

SELECT  *
FROM    dbo.[Transaction] t
OUTER APPLY (
    SELECT * FROM TableA a WHERE t.Col1=a.TableAID
) j1 --first join
OUTER APPLY (
    SELECT * FROM TableB b WHERE j1.TableAID IS NULL AND t.Col1=b.TableBID --First condition will force the join order (dbo.TableA.TableAID should be NOT NULL)
) j2 --second join
OUTER APPLY (
    SELECT * FROM TableC c WHERE j1.TableAID IS NULL AND j2.TableBID IS NULL AND t.Col1=c.TableCID ---First two conditions will force the join order (dbo.TableA.TableAID & dbo.TableB.TableBID should be NOT NULL)
) j3 --third join
WHERE   j1.TableAID IS NOT NULL
OR      j2.TableBID IS NOT NULL
OR      j3.TableCID IS NOT NULL

ROLLBACK;

In this case, the join order is:

1) t.Col1=a.TableAID

2) if not 1) then t.Col1=b.TableBID

3) if not 1) and 2) then t.Col1=c.TableCID

Results:

TransactionID TranDate   Col1 TableAID DescriptionA TableBID DescriptionB TableCID DescriptionC
------------- ---------- ---- -------- ------------ -------- ------------ -------- ------------
1             2012-01-01 1    1        A-1          NULL     NULL         NULL     NULL
2             2012-02-02 2    2        A-2          NULL     NULL         NULL     NULL
3             2012-03-03 3    NULL     NULL         NULL     NULL         3        C-3
4             2012-04-04 4    NULL     NULL         4        B-4          NULL     NULL



回答2:


As @AaronBertrand mentioned, I'm a little unclear on what you are trying to do, but if you are talking about changing your output, could you just use COALESCE? Example:

SELECT COALESCE(s.Value, d.Value, c.Value), t.*
FROM Transactions as t
LEFT JOIN Strange as s
ON t.id = s.tid
LEFT JOIN Down as d
ON t.id = d.tid
LEFT JOIN Charmed as c
ON t.id = c.tid


来源:https://stackoverflow.com/questions/12503709/force-partial-join-order-in-sql-server

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!