2009-05-28 8 views
0

Ich werde beschreiben, was ich zu erreichen versuchen:T-SQL-Filterung auf dynamischen Namen-Wert-Paaren

Ich bin vorbei bis zu einer SP eine XML mit dem Namen Wert-Paaren, die ich in eine Tabelle Variable setzen, sagen wir mal @nameValuePairs. Ich muss eine Liste von IDs für Ausdrücke (eine Tabelle) mit genau dieser Übereinstimmung der Name-Wert-Paare (Attribute, eine andere Tabelle) zugeordnet abrufen.

Das ist mein Schema:

Expressions Tabelle -> (expressionId, attributeId)

Tabelle Attribute -> (attributeId, attribute, attribute)

Nach dem Versuch, komplizierte Sachen mit dynamischem SQL und bösen Cursorn (was funktioniert, aber es ist schmerzhaft langsam), das ist, was ich jetzt habe:

--do the magic plz! 

-- retrieve number of name-value pairs 
SET @noOfAttributes = select count(*) from @nameValuePairs 

select distinct 
    e.expressionId, a.attributeName, a.attributeValue 
into 
    #temp 
from 
    expressions e 
join 
    attributes a 
on 
    e.attributeId = a.attributeId 
join --> this join does the filtering 
    @nameValuePairs nvp 
on 
    a.attributeName = nvp.name and a.attributeValue = nvp.value 
group by 
    e.expressionId, a.attributeName, a.attributeValue 

-- now select the IDs I need 
-- since I did a select distinct above if the number of matches 
-- for a given ID is the same as noOfAttributes then BINGO! 
select distinct 
    expressionId 
from 
    #temp 
group by expressionId 
having count(*) = @noOfAttributes 

Können Leute bitte überprüfen und sehen, ob sie irgendwelche Probleme entdecken können? Gibt es einen besseren Weg, dies zu tun?

Jede Hilfe wird geschätzt!

+0

Wie ich unten bemerkte, glaube ich nicht, dass diese Abfrage das Szenario behandeln würde, in dem ein Ausdruck mehr als die übereinstimmenden Attribute hatte. h. Ausdruck hat Attribut-IDs (1,2,3,4) und die erforderlichen Attribute waren (2,3) – CAbbott

+0

In Ihrem Beispiel behandelt diese Abfrage das Szenario, in dem der Ausdruck mindestens "(2,3) hat. In meinem Szenario ist das akzeptabel, aber jetzt hast du mich zum Nachdenken gebracht. Wie würden Sie das Szenario bearbeiten, das eine exakte Übereinstimmung erfordert (erforderliches Attribut 1,2 und Ausdrücke mit 1,2, X werden nicht zurückgegeben)? – JohnIdol

+0

Meine Abfrage sollte die exakte Übereinstimmung behandeln (E = {2,3} & A = {2,3}); Szenarien wie Überschreitungen (E = {2,3,4} & A = {2,3}) sowie Minderjährige (E = {3} & A = {2,3}) sind von der endgültigen Ergebnismenge ausgeschlossen. – CAbbott

Antwort

1

Ich glaube, dass dies die Anforderung erfüllen würde, die Sie versuchen zu erfüllen.Ich bin mir nicht sicher, wie viel schöner es ist, aber es sollte funktionieren und würde nicht eine temporäre Tabelle erforderlich:

SET @noOfAttributes = select count(*) from @nameValuePairs 

SELECT e.expressionid 
FROM expression e 
LEFT JOIN (
      SELECT attributeid 
      FROM attributes a 
      JOIN @nameValuePairs nvp ON nvp.name = a.Name AND nvp.Value = a.value 
      ) t ON t.attributeid = e.attributeid 
GROUP BY e.expressionid 
HAVING SUM(CASE WHEN t.attributeid IS NULL THEN (@noOfAttributes + 1) ELSE 1 END) = @noOfAttributes 

EDIT: Nach etwas mehr Bewertung zu tun, fand ich ein Problem, wo bestimmte Ausdrücke enthalten sein würden, dass hätte nicht sein sollen. Ich habe meine Abfrage geändert, um dies zu berücksichtigen.

+0

danke für die Hilfe - Ihr Skript ist kompakter, aber ein bisschen weniger lesbar, Sie denken, dieser Ansatz würde die Leistung verbessern? – JohnIdol

+0

Ja, ich merke, dass es ein funky Ansatz ist, aber es hielt alles in einer Abfrage. leistungsfähig, denke ich, dass nicht eine temporäre Tabelle erstellen eine leichte Leistungsverbesserung wäre. Sie können sie immer profilieren, um zu sehen, ob es Einsparungen gibt. – CAbbott

+0

+1 für Ihren hilfreichen Beitrag – JohnIdol

1

Dies ist kein schlechter Ansatz, abhängig von den Größen und Indizes der Tabellen, einschließlich @nameValuePairs. Wenn diese Zeilenanzahl hoch oder sonst langsam ist, sollten Sie stattdessen @namValuePairs in eine temporäre Tabelle einfügen, entsprechende Indizes hinzufügen und eine einzige Abfrage anstelle von zwei separaten Abfragen verwenden.

Ich stelle fest, dass Sie Spalten in #temp setzen, die Sie nicht verwenden, wäre schneller, sie auszuschließen (obwohl es doppelte Zeilen in #temp bedeuten würde). Auch Ihre zweite Abfrage hat sowohl eine "distinct" als auch eine "group by" in denselben Spalten. Du brauchst beide nicht, also würde ich das "distinct" fallen lassen (wahrscheinlich wird das die Leistung nicht beeinflussen, weil der Optimierer das bereits herausgefunden hat).

Endlich wäre #temp wahrscheinlich schneller mit einem geclusterten, nicht eindeutigen Index für expressionid (ich gehe davon aus, dass dies SQL 2005 ist). Sie können es nach dem SELECT..INTO hinzufügen, aber es ist normalerweise so schnell oder schneller, es hinzuzufügen, bevor Sie laden. Dies würde erfordern, dass Sie zuerst #temp erstellen, das Clustered hinzufügen und dann INSERT..SELECT verwenden, um es stattdessen zu laden.

ich ein Beispiel hinzufügen, werden die Abfragen in einer mintue der Verschmelzung ... Ok, hier ist ein Weg, um sie in einer einzigen Abfrage zu fusionieren (dies sollte auch 2000-kompatibel):

-- retrieve number of name-value pairs 
SET @noOfAttributes = select count(*) from @nameValuePairs 

-- now select the IDs I need 
-- since I did a select distinct above if the number of matches 
-- for a given ID is the same as noOfAttributes then BINGO! 
select 
    expressionId 
from 
    (
     select distinct 
      e.expressionId, a.attributeName, a.attributeValue 
     from 
      expressions e 
     join 
      attributes a 
     on 
      e.attributeId = a.attributeId 
     join --> this join does the filtering 
      @nameValuePairs nvp 
     on 
      a.attributeName = nvp.name and a.attributeValue = nvp.value 
    ) as Temp 
group by expressionId 
having count(*) = @noOfAttributes 
+0

@nameValuePairs hat nicht mehr als 10 Zeilen (Worst-Case-Szenario - es ist in der Regel 5-6) – JohnIdol

+0

auch - auf einer vollständig bevölkerten db #temp hat etwa 9k Zeilen nach der Filterung – JohnIdol

+0

Ich habe einen gruppierten Index auf Ausdrücke für expressionId (PK), attributeId (PK, FK), ein Clustered-Index für Attribute für attributeId (PK) - Ich habe auch Nonclustered-Index für Expression-ID für (expressionId und attributeId) und einen anderen Nonclustered-Index für alle Felder in der Attributtabelle hinzugefügt - wie hier - -> http://stackoverflow.com/questions/916940/how-to-speed-up-simple-join/917447#917447 – JohnIdol

1

One Fehler Ich sehe, dass Sie keine Tabelle mit einem Alias ​​von b haben, aber Sie verwenden: a.attributeId = b.attributeId.

Versuchen Sie, das zu beheben und zu sehen, ob es funktioniert, es sei denn, ich vermisse etwas.

EDIT: Ich denke, Sie haben das gerade in Ihrem Edit behoben, aber soll es a.attributeId = e.attributeId sein?

+0

war ein Tippfehler! danke – JohnIdol