我完全被我用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列表和百分比匹配,按百分比降序排序。要么适用于我的任务。
答案 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 )
至少我在一个有点乏味的营销演示中充分利用了我的时间。