2012-04-13 4 views
2

Ich habe diese Verbindungstabelle:specifiy Mindestanzahl von Join Punkte in SQL

CREATE TABLE [TagMap](
[intItemId] [bigint] NOT NULL, 
[intTagId] [bigint] NOT NULL, 
CONSTRAINT [PK_TagMap_intItemId] PRIMARY KEY CLUSTERED 
(
[intItemId] ASC, 
[intTagId] ASC 
)) 

Meine sproc entdeckt die Tags, die mit einem seedItemId zugeordnet sind, und wählt dann andere intItemIds, die mit diesen Tags zugeordnet sind, wie folgt aus:

declare @baseTags table (intTagId bigint primary key clustered); 

INSERT INTO @baseTags (intTagId) 
SELECT TOP 20 t1.intTagId 
FROM TagMap t1 
WHERE t1.intItemId = 776 

SELECT TOP 500 t1.intItemId 
FROM TagMap t1 
     JOIN @baseTags t2 
     ON t1.intTagId = t2.intTagId 
GROUP BY t1.intItemId 
ORDER BY Count(*) DESC 

Was muss ich, ist eine Mindestanzahl von Tag Vereinigungen angeben - etwa 10 - zwischen zwei intItemIds unter dem ein Wert wird nicht zurückgegeben. Mit anderen Worten, in der TagMap-Tabelle können zehn oder mehr intTagIds gefunden werden, die zwei intItemIds gemeinsam haben, es ist gut und wir wählen es aus - andernfalls ignorieren wir es.

zum Beispiel also diese Daten gegeben:

CREATE TABLE #TagMap(
[intItemId] [bigint] NOT NULL, 
[intTagId] [bigint] NOT NULL, 
CONSTRAINT [PK_TagMap_intItemId] PRIMARY KEY CLUSTERED 
(
[intItemId] ASC, 
[intTagId] ASC 
)) 

insert into #TagMap 
(intItemId, intTagId) 
values 
(1, 100),(1, 200),(1, 300), 
(2, 100),(2, 200),   (2, 500),(2, 600), 
(3, 100),     (3, 500),(3, 600) 

Angenommen, die Vergleichsschwelle zwei ist.

Wenn der Seed-Wert intItemId 1 ist, sollte nur intItemId 2 zurückgegeben werden (er hat zwei übereinstimmende Tag-IDs: 100 und 200, während intItemId 3 einen Wert von 100 hat, der unter dem Schwellenwert liegt).

Wenn der Startwert intItemId 2 ist, sollten sowohl intItemId 1 als auch 3 zurückgegeben werden (intItemId 1 stimmt mit den Tag-IDs 100 und 200 überein, während intItemId 3 mit den Tag-IDs 500 und 600 übereinstimmt).

Wenn der Seed-Wert intItemId 3 ist, sollte nur intItemId 2 zurückgegeben werden (er hat zwei übereinstimmende Tag-IDs: 500 und 600, während intItemId einen Wert von 100 unter dem Schwellenwert hat).

Irgendwelche Ideen, wie man das bitte macht?

Cheers, Matt

Antwort

2
SELECT 
    [foreign].intItemID 
FROM 
    #TagMap AS [primary] 
INNER JOIN 
    #TagMap AS [foreign] 
    ON [foreign].intTagID = [primary].intTagID 
WHERE 
    [primary].intItemID = 1 
GROUP BY 
    [foreign].intItemID 
HAVING 
    COUNT(distinct [foreign].intTagID) >= @threshold 

Beachten Sie jedoch, dass dies ziemlich schlecht skaliert; weil die JOIN Suche nach „jede dieser Tags hat“, und Sie können dann nur in der HAVING Klausel angeben „hat alle dieser Tags“.

Aus meiner Erfahrung können Sie geringfügige Optimierungen vornehmen, aber der lohnendste Ansatz, den ich gefunden habe, bestand darin, die Ergebnisse in einer Standardmappingtabelle zwischenzuspeichern. Aktualisieren sie bei Bedarf. (. Der Inhalt der Variablen Daten sehr selten zu ändern)

+0

Danke für die Warnung über die Leistung - das kann mich wieder beißen, wenn ich dies auf die echten Daten anwende. –

1

Dies scheint es zu tun (variieren @InterestItem zu 1,2,3, und es scheint, um das Ergebnis zu erzeugen Sie gefragt haben):

declare @InterestItem int 
set @InterestItem = 1 
declare @Threshold int 
set @Threshold = 2 

select 
    tm2.intItemId, 
    COUNT(*) 
from 
    #TagMap tm1 
     inner join 
    #TagMap tm2 
     on 
      tm1.intTagId = tm2.intTagId and 
      tm2.intItemId <> tm1.intItemId 
where 
    tm1.intItemId = @InterestItem 
group by 
    tm2.intItemId 
having 
    COUNT(*) >= @Threshold 

Ich bin derzeit die COUNT in der Ergebnismenge enthalten, aber das ist nicht erforderlich, um dies zu tun.