查找链接表中的相关行

时间:2012-06-06 14:24:55

标签: sql-server-2008 relational-database

我的问题域是广告,为此,我有一个包含名为ADVERT的表的数据库。广告可以具有方面(即,准分类描述性术语)。因此,有一个用于定义构面的FACET表和一个包含每个构面值的FACETTERM表。 ADVERTFACETTERMASSIGNMENT是链接表,其中说明哪个方面的术语值分配给哪个广告。

所以,你可能有一个广告,一辆车的价值为“Honda”,因为它的“Make”方面,而“Sussex”的“Location”方面。因此,如果广告是广告{PK = 14},而本田是分面术语{PK = 1}而苏塞克斯是分面术语{PK = 2},则您希望ADVERTFACETTERMASSIGNMENT { AdvertId, FacetTermId }: 14, 1和{{1}中的行}。

鉴于此安排,我如何找到所有针对苏塞克斯的本田的其他广告?换句话说,如何在14, 2中找到一组与给定广告中该行的行匹配但不是来自该广告的行?

我正在使用SQL Server 2008.我尝试过使用IN子句,但这会返回部分匹配,即除Sussex之外的所有Hondas以及Sussex中不是Hondas等的所有汽车。

要重申我的要求,我需要找到ADVERTFACETTERMASSIGNMENT中的所有行,其中这些行至少包含与另一个给定广告相同的facet-term id。如果它具有更多的方面条款并不重要,只要它与所选的比较器广告至少具有完全相同的方面术语。

3 个答案:

答案 0 :(得分:2)

这基本上是一个EAV - 具有固定值选择的实体,属性,价值模型。

WITH FLATTENED AS (
    SELECT a.ADVERT_ID, ft.FACETTERM_ID
    FROM ADVERT a
    INNER JOIN ADVERTFACETTERMASSIGNMENT afta
        ON afto.ADVERT_ID = a.ADVERT_ID
    INNER JOIN FACETTERM ft
        ON ft.FACETTERM_ID = afta.FACETTERM_ID
    INNER JOIN FACET f
        ON f.FACET_ID = ft.FACET_ID
)
SELECT rhs.ADVERT_ID, COUNT(*)
FROM FLATTENED lhs
INNER JOIN FLATTENED rhs
    ON lhs.ADVERT_ID = @SOME_ID
    AND rhs.ADVERT_ID <> lhs.ADVERT_ID
    AND rhs.FACETTERM_ID = lhs.FACETTERM_ID
GROUP BY rhs.ADVERT_ID
HAVING COUNT(*) = (SELECT COUNT(*) FROM FLATTENED WHERE ADVERT_ID = @SOME_ID)

这里的技巧是任何两个广告之间的内部联接中匹配的面数必须等于左侧对象广告的面数。

答案 1 :(得分:0)

好的,其中ADVERTFACETTERMASSIGNMENT是{AdvertId,FacetTermId} 进行两个学期的搜索...

select fta1.AdvertID 
from ADVERTFACETTERMASSIGNMENT fta1
join ADVERTFACETTERMASSIGNMENT fta2 on fta1.AdvertID = fta2.AdvertID
where fta1.FacetTermId = @searchFacet1 and fta2.FacetTermID = @searchFacet2
and fta1.AdvertID <> @searchAdvertId

工作示例的一般答案:

declare @AdvertFacetTermAssignment table (AdvertId int, FacetTermId int)

insert into @AdvertFacetTermAssignment values
(1,10), (1,11), (2,10), (3,11), (4,10), (4,11), (5,10), (5,11), (5,12), (6,10), (6,12), (6,13), (7, 10), (7, 11), (8, 12), (9,10), (9,11), (9,12), (10, 10), (10,12)

declare @searchAdvertId int = 1
declare @targetMatch int = (select COUNT(*) from @AdvertFacetTermAssignment where AdvertId = @searchAdvertId)

select aft2.AdvertId from @AdvertFacetTermAssignment aft1 
join @AdvertFacetTermAssignment aft2
on aft1.FacetTermId = aft2.FacetTermId and aft1.AdvertId <> aft2.AdvertId
where aft1.AdvertId = @searchAdvertId
group by aft2.AdvertId 
having COUNT(*) = @targetMatch

结果= 4,5,7,9

最后一个不是所要求的,但是抓住所有类似的东西(一些匹配的方面)和订单的相似程度。
(所有比赛都得到平等对待)

 select aft2.AdvertId, COUNT(aft1.AdvertId) as matches, ABS(COUNT(*)-@targetMatch) as nonMatches 
 from @AdvertFacetTermAssignment aft1
 right outer join @AdvertFacetTermAssignment aft2
 on aft2.FacetTermId = aft1.FacetTermId 
    and aft1.AdvertId = @searchAdvertId
    and aft2.AdvertId <> @searchAdvertId
 group by aft2.AdvertId 
 having COUNT(aft1.AdvertId) > 0
 order by COUNT(aft1.AdvertId) DESC, ABS(COUNT(*)-@targetMatch) ASC 

结果:

 AdvertId matches nonMatches
 4        2       0
 7        2       0
 9        2       1
 5        2       1
 10       1       0
 6        1       1
 2        1       1
 3        1       1

(顺便说一句,我是从威斯康星州苏塞克斯发布的)

答案 2 :(得分:0)

一种方法可以是在facettermid上将advertfacettermassignment表内连接到自身, 并且通过广告进行分组,以计算广告之间的匹配方面的数量。

然后,您可以将其与第一个广告中的构面总数进行比较,以及匹配的数量 如果它是相同的,那么它至少与所选择的比较器具有完全相同的方面项。

在SQL SERVER 2008中,您可以使用CTE使这更容易一些。像这样:

;WITH m AS
    (SELECT advertid,candidateid,COUNT(*) as matchingfacets FROM (
        SELECT a.advertid,b.advertid as candidateid FROM advertfacettermassignment a 
        INNER JOIN advertfacettermassignment b ON a.facettermid=b.facettermid) sub
    GROUP BY advertid,candidateid)
,t AS
    (SELECT advertid,COUNT(*) as TotalFacets FROM advertfacettermassignment GROUP BY advertid)
SELECT 
    totalfacets.advertid,
    matchingfacets.candidateid,
    t.totalfacets,
    m.matchingFacets
FROM m INNER JOIN t
ON m.advertid=t.advertid
WHERE matchingfacets=totalfacets