问题
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:
StrangeDownCharmed
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 joinNationalSecurityLetters==>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