2010-07-30 58 views
1

考慮下面的(大量簡化)表:對於表的每一行,得到任何一個鏈接行中的另一個表

create table Tags (
    TagId int Primary Key 
) 

create table OrderLines (
    Branch int, 
    Station int, 
    TransNo int, 
    TagId int foreign key references Tags, 
    primary key (Branch, Station, TransNo) 
) 

我需要的標籤與它引用的每個標籤的訂單行沿列表。我期望零或一個OrderLines引用每個標籤,但數據庫中沒有任何限制來執行此操作。

這樣給定的輸入:

OrderLines          Tags 
Branch Station TransNo TagId     TagId 
1  100  2345 1      1 
1  100  2346 1      2 
1  101  5223 2      3 
3  100  6677 4      4 

我想輸出是這樣的:

TagId  Branch Station TransNo 
    1   1   100  2345  <-- it could list 2346 here, don't care 
    2   1   101  5223  
    3   null  null  null 
    4   3   100  6677 

注意,雖然標籤識別1引用了兩次,我只輸出,包含了一個。無論OrderLine與哪個OrderLine一起列出,都無關緊要,但輸出中每個標籤只能有一個實例。

什麼是最有效的方法來做到這一點?

我無法修改數據庫模式。

+0

好吧,我只是通過您的文章脫脂但不會一個簡單的內部連接就足夠了? – 2010-07-30 10:48:45

+0

不,這會讓我在輸出中重複TagIds,當有多個OrderLine引用一個Tag時 – Blorgbeard 2010-07-30 10:49:46

回答

2

你不得不看的執行計劃來衡量效率

;WITH O AS 
(
SELECT Branch, Station, TransNo, TagId, 
ROW_NUMBER() OVER (PARTITION BY TagId ORDER BY TagId) AS RN 
FROM OrderLines 
) 
    SELECT T.TagID, O.Branch, O.Station, O.TransNo 
     FROM Tags T 
     LEFT JOIN O ON T.TagID = O.TagID and RN=1 
+0

謝謝,這對我很好。有沒有什麼理由使用CTE而不是子查詢?兩者似乎對我都有同樣的表現。 – Blorgbeard 2010-07-30 11:49:02

+0

@Blorgbeard - 他們都有相同的執行計劃以及個人喜好。 – 2010-07-30 12:06:52

0
SELECT Tags.TagID, Branch, Station, TransNo 
    FROM Tags 
    LEFT JOIN OrderLines ON Tags.TagID = OrderLines.TagID 
    ORDER BY Tags.TagID 

左連接將確保所有標籤都被列出,即使那些沒有附加任何訂單行的連接。 唯一的缺陷是,如果多個訂單行引用一個標籤,則每個訂單行將爲該標籤列出一次。一些DBMS(例如MySQL)允許你使用GROUP BY來解決這個問題,並且仍然選擇你沒有分組的列,但它不是標準的,並且不能保證你會得到哪個OrderLine。如果你想這樣做,你將不得不求助於子查詢,聯合,臨時表或視圖(但是由於你不能更改模式,所以最後一個選項不存在)。

+0

是的,那個缺陷是殺手。我也肯定堅持使用MSSQL:P – Blorgbeard 2010-07-30 10:51:44

0
select 
     T.tagid, 
     O.branch, 
     O.station, 
     O.transno 
    from orderlines O 
    right join tags T on (t.tagid=O.tagid) 
    group by t.tagid 

結果:

tagid branch station transno 
1 1 100 2345 
2 1 101 5223 
3 NULL NULL NULL 
4 3 100 6677 
+0

無法在sql服務器中執行此操作 - branch,station,transno必須位於聚合或group by子句中。 – Blorgbeard 2010-07-30 11:07:04

2
select t.TagId, t.Station, t.Branch, t.TransNo 
from (
SELECT Station, Branch, TransNo, TagId, ROW_NUMBER() OVER (partition by TagId order by TagId) r 
From OrderLines) t 
WHERE r = 1 
UNION ALL 
SELECT TagId, NULL, NULL, NULL 
from Tags 
WHERE NOT EXISTS (Select 1 from OrderLines ol Where ol.TagId = Tags.Id) 
+0

看起來有趣,但我認爲你有幾個語法錯誤..我在'rank()'後面添加了'over',但仍然得到「排名函數」Rank「必須有一個ORDER BY子句」..我' m不擅長排名功能,所以不知道如何解決。 – Blorgbeard 2010-07-30 11:11:13

+0

我認爲你需要改變它爲'Rank()OVER(通過TagId ORDER BY TagId分區)'我做到了這一點,並與我的回答相比得到了執行計劃http://img148.imageshack.us/img148/ 4105/executionplan.png – 2010-07-30 11:19:49

+0

@Blorgbeard。是的,OVER缺失,需要'order by'子句(查詢已更改)。如果您有任何偏好,應選擇哪些訂單行作爲標籤 - 更改'訂購方式'列表。 也期待馬丁史密斯解決方案 - 它也有效的方式。不知道這會更快'左連接'或'工會'與'不存在' – 2010-07-30 11:22:19

相關問題