在SQL Server力部分连接顺序(Force partial join order in SQL

2019-09-22 15:54发布

编辑 :人们有一个很难理解我想要什么。 因此,这里的漂亮的图片,说明它在痛苦的细节。

首先加入交易 陌生

迄今为止的结果

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

现在尝试加入唐氏其余行:

迄今为止的结果

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

最后,加入任何剩余的行迷住

迄今为止的结果

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

而如何在行看我们留下:

给我我想要的结果集

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

我有一个主表:

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

我想在这个表中每行只能加入一个可能的匹配表:

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

通常我会为加盟进行Transactions的一套Strange || Down || Charmed Strange || Down || Charmed 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)    
    )       

问题是,我想加入该首选的顺序发生:

  • Strange
  • Down
  • Charmed

这是完全有可能是一个单一的交易可以在多个表匹配的条目。 但是,对于每个可能的连接Transactions到其他表,我想SQL Server来喜欢 Strange表。 如果没有匹配然后去Down表。 如果没有匹配去Charmed表。

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)

我想过使用OPTION(FORCE ORDER)子句:

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)

但我不希望强制SQL Server加盟

  • Transactions ==> NationalSecurityLetters ,当其可能是更有利的加入
  • NationalSecurityLetters ==> Transactions

Answer 1:

也许这个解决方案将帮助你:

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;

在这种情况下, 连接顺序是:

1)t.Col1 = a.TableAID

2)如果不是1),则t.Col1 = b.TableBID

3)如果不为1)和2)然后t.Col1 = c.TableCID

结果:

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


Answer 2:

作为@AaronBertrand提到的,我对你正在尝试做的有点不清楚,但如果你谈论的是改变你的输出,可你只需要使用COALESCE? 例:

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


文章来源: Force partial join order in SQL Server