SQL Server,在映射表中匹配一个对象ID与其他对象ID

时间:2012-04-10 17:58:05

标签: sql-server tsql

我完全被我用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 个答案:

答案 0 :(得分: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 :(得分: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

答案 2 :(得分: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

..要求澄清评论。

答案 3 :(得分: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 )

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