1

我完全被我用 T-SQL 编写的查询所困扰。我有一个映射表,其中书籍 ID 存储在BookId列中并AttributeId存储在另一列中。

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

每本书可以有 1 到 10 个属性。如果书 1 具有属性 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 进行查询并返回恰好具有 3 和 6 的 BookId,但不会或多或少。另一种方法是返回 BookId 的列表和百分比匹配,按百分比降序排序。要么适用于我的任务。

4

4 回答 4

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
于 2012-04-10T20:43:33.947 回答
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子句中指定计数。

于 2012-04-10T18:07:08.783 回答
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 )

至少我在一个有点乏味的营销演示中充分利用了我的时间。

于 2012-04-10T22:43:52.783 回答
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

..要求在评论中澄清。

于 2012-04-10T18:03:58.613 回答