2012-04-10 21 views
1

我完全難以理解我在T-SQL中編寫的查詢。我有一個映射表,其中書ID存儲在BookId列中,AttributeId存儲在另一列中。SQL Server,在映射表中將一個對象ID與其他對象ID進行匹配

CREATE TABLE BookMap (
BookId int not null, 
AttributeId int not null 
) 

每本書都可以有1到10個屬性。如果第一本書的屬性是3-6,我希望找到另外一些書,它們的屬性也是3-6。出於某種原因,我想不出如何編寫這個查詢。

有什麼建議嗎?

下面是編輯: 爲了進一步解釋,我有這樣的數據:

INSERT INTO BookMap (BookId, AttributeId) VALUES (1, 3); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (1, 6); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (2, 3); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (2, 4); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (2, 6); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (5, 3); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (5, 6); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (6, 3); 
INSERT INTO BookMap (BookId, AttributeId) VALUES (6, 5); 

我想查詢基於BOOKID = 1,並返回BOOKID有確切3和6,而不是更多或更少的。另一種方法是返回BookId列表和百分比匹配,按百分比遞減排序。要麼爲我的任務工作。

+0

如果書1有屬性3和6,你是否希望a)所有屬性爲3的書和所有屬性爲6的書,或者b)只有屬於3和6的書?另外,說書2有屬性3,5和6.是否應該排除它,因爲它有一個額外的不匹配的屬性? – Tony 2012-04-10 18:17:18

+0

第1冊有第3和第6個。我想返回,例如第5冊,它具有屬性3和6.我不想返回第2本書,它具有屬性3,4和6.我將編輯爲進一步解釋。 – 2012-04-10 19:37:56

回答

0

編輯:下面是產生預期的結果幾個查詢。根據指標,統計數據......他們可能會有相當不同的表現。用真實數據檢查執行計劃應該是有啓發性的。

-- Sample data. 
declare @BookMap as Table (BookId int not null, AttributeId int not null) 
insert into @BookMap (BookId, AttributeId) values 
    (1, 3), (1, 6), 
    (2, 3), (2, 4), (2, 6), 
    (5, 3), (5, 6), 
    (6, 3), (6, 5) 
select * from @BookMap 

-- Target book. 
declare @BookId as Int = 1 
select AttributeId 
    from @BookMap 
    where BookId = @BookId 

-- Books with matching attributes using NOT EXISTS in the last line. 
select BookId 
    from ( 
    select BookId, Sum(1) as MatchCount 
     from @BookMap as BM 
     where BookId <> @BookId and AttributeId in (select AttributeId from @BookMap where BookId = @BookId) 
     group by BookId) as Ellen 
    where 
     -- The number of matching attributes is the number of desired attributes. 
     MatchCount = (select Count(42) from @BookMap where BookId = @BookId) and 
     -- There are no other attributes as determined by looking for additional attributes. 
     not exists (select 42 from @BookMap where BookId = Ellen.BookId and AttributeId not in (select AttributeId from @BookMap where BookId = @BookId)) 

-- Books with matching attributes using COUNT() in the last line. 
select BookId 
    from ( 
    select BookId, Sum(1) as MatchCount 
     from @BookMap as BM 
     where BookId <> @BookId and AttributeId in (select AttributeId from @BookMap where BookId = @BookId) 
     group by BookId) as Ellen 
    where 
     -- The number of matching attributes is the number of desired attributes. 
     MatchCount = (select Count(42) from @BookMap where BookId = @BookId) and 
     -- There are no other attributes as determined by counting attributes. 
     (select Count(42) from @BookMap where BookId = Ellen.BookId) = (select Count(42) from @BookMap where BookId = @BookId) 



-- Display the attributes that we must, and must not, match. 
select distinct AttributeId, 
    case when AttributeId in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustMatch, 
    case when AttributeId not in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustNotMatch 
    from @BookMap 



-- Get the similar books using SUM() in the last line. 
; with A as (
    -- All attributes with MustMatch/MustNotMatch flags. 
    select distinct AttributeId, 
    case when AttributeId in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustMatch, 
    case when AttributeId not in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustNotMatch 
    from @BookMap 
) 
select BookId 
    from @BookMap as B inner join 
    A as A on A.AttributeId = B.AttributeId 
    where BookId <> @BookId 
    group by BookId 
    having Sum(MustNotMatch) = 0 and Sum(MustMatch) = (select Count(42) from @BookMap where BookId = @BookId) 

-- Get the similar books using MAX() in the last line. 
; with A as (
    -- All attributes with MustMatch/MustNotMatch flags. 
    select distinct AttributeId, 
    case when AttributeId in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustMatch, 
    case when AttributeId not in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustNotMatch 
    from @BookMap 
) 
select BookId 
    from @BookMap as B inner join 
    A as A on A.AttributeId = B.AttributeId 
    where BookId <> @BookId 
    group by BookId 
    having Max(MustNotMatch) = 0 and Sum(MustMatch) = (select Count(42) from @BookMap where BookId = @BookId) 



-- Get the similar books without using SUM() and with extra credit for using a Cartesian product. 
-- Using MAX() in the last line. 
; with A as (
    -- All attributes with MustMatch/MustNotMatch flags. 
    select distinct AttributeId, 
    case when AttributeId in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustMatch 
    from @BookMap 
), 
B as (
    -- All books except the search pattern book. 
    select distinct BookId 
    from @BookMap 
    where BookId <> @BookId), 
P as (
    -- Cross product plus original data and coefficient of wickedness. 
    select B.BookId, A.AttributeId, A.MustMatch, 
    case 
     when MustMatch = 1 and T.AttributeId is not NULL then 0 
     when MustMatch = 0 and T.AttributeId is NULL then 0 
     else 1 
     end as Wicked 
    from B cross join 
     A left outer join 
     @BookMap as T on T.BookId = B.BookId and T.AttributeId = A.AttributeId 
) 
select BookId 
    from B 
    where (select Max(Wicked) from P where P.BookId = B.BookId) = 0 

-- Get the similar books without using SUM() and with extra credit for using a Cartesian product. 
-- Using NOT EXISTS in the last line. 
; with A as (
    -- All attributes with MustMatch/MustNotMatch flags. 
    select distinct AttributeId, 
    case when AttributeId in (select AttributeId from @BookMap where BookId = @BookId) then 1 else 0 end as MustMatch 
    from @BookMap 
), 
B as (
    -- All books except the search pattern book. 
    select distinct BookId 
    from @BookMap 
    where BookId <> @BookId), 
P as (
    -- Cross product plus original data and coefficient of wickedness. 
    select B.BookId, A.AttributeId, A.MustMatch, 
    case 
     when MustMatch = 1 and T.AttributeId is not NULL then 0 
     when MustMatch = 0 and T.AttributeId is NULL then 0 
     else 1 
     end as Wicked 
    from B cross join 
     A left outer join 
     @BookMap as T on T.BookId = B.BookId and T.AttributeId = A.AttributeId 
) 
select BookId 
    from B 
    where not exists (select 42 from P where P.BookId = B.BookId and Wicked = 1) 

至少我有點乏味的營銷演示過程中很好地利用我的時間。

+0

這是一個很棒的答案,謝謝。仍在測試性能,但似乎完全符合法案。 – 2012-04-11 19:00:56

0

到具有匹配屬性得到圖書列表:

select distinct B1.BookId, B1.AttributeId, B2.BookId as MatchingBookId 
from BookMap as B1 
    inner join BookMap as B2 on B1.AttributeId = B2.AttributeId 
where B1.BookId <> B2.BookId 

得到公正匹配的圖書清單:

select distinct B1.BookId, B2.BookId as MatchingBookId 
from BookMap as B1 
    inner join BookMap as B2 on B1.AttributeId = B2.AttributeId 
where B1.BookId <> B2.BookId 

..要求澄清的評論。

+0

第一個查詢將返回愚蠢的行爲,因爲Book1將位於其已分配的每個屬性的「JOIN」的兩側,而列表中的每一本書都將如此。與第二個查詢相同。 – JNK 2012-04-10 18:36:09

+0

我添加了where子句來防止雙方的書,但自從上面的澄清之後,它就沒有任何意義。 – Tony 2012-04-11 14:34:21

1
SELECT b.bookID 
FROM BookMaP A 
INNER JOIN BookMap B 
    ON a.attributeID = B.AttributeID 
WHERE a.BookID = 1 -- The id you want to compare against 
GROUP BY b.bookID 
HAVING COUNT(DISTINCT b.AttributeID) = COUNT(DISTINCT a.AttributeID) 

我想聚集和自JOIN是要走的路。這可能需要調整,並且您可能需要在HAVING子句中指定計數。

+1

我在這裏測試了它(http://www.sqlfiddle.com/#!3/a9eec/1),它似乎會返回每個有共同屬性的書。我認爲這是因爲當你通過b.BookId分組然後計數(在這些行上)不同的b。AttributeID記錄並比較它們(在內部加入AttributeID的那些相同行上)不同的a.AttributeID記錄,您將始終獲得相同的結果。這是因爲只有與B匹配的a.AttributeID記錄將由於內部聯接而位於該組中。 – 2012-04-10 20:47:13

1

我測試了我的答案在這裏:http://www.sqlfiddle.com/#!3/a9eec/4(以及對我的本地服務器)

;WITH AttributeSet AS 
(
    SELECT DISTINCT 
    B.BookId 
    , SUBSTRING((SELECT 
        (',' + CAST(A.AttributeId AS VARCHAR(4))) 
       FROM BookMap A 
       WHERE A.BookId = B.BookId 
       ORDER BY A.AttributeId 
       FOR XML PATH ('')),2,9999) AS AttributeSet 
    FROM BookMap B 
) 
SELECT 
    MatchingBooks.BookId 
FROM AttributeSet BaseBook 
INNER JOIN AttributeSet MatchingBooks 
    ON MatchingBooks.AttributeSet = BaseBook.AttributeSet 
WHERE BaseBook.BookId = 1 
+1

在組裝屬性集時,應該有'ORDER BY'。如果您不指定它,則SQL Server可以按任何順序返回結果。 – HABO 2012-04-10 22:22:53

+0

@ user92546我在我的一個測試查詢中做過,但是當我粘貼到sqlfiddle和這裏時,看起來我忘了它。感謝您指出!我現在更新。 – 2012-04-10 22:41:00

+0

儘管此解決方案在示例場景中有效,但與我的真實世界數據集(總共650萬條記錄中的50萬個對象ID)的可擴展性不同,作爲user92546的答案。 – 2012-04-11 19:03:27

相關問題