Frage

Zur Zeit arbeite ich an einem besonders komplexen Anwendungsfall. Vereinfachen unten:)

Zuerst wird ein Client-Datensatz eine viele-zu-eins-Beziehung mit einer Sammlung von Diensten hat, das heißt, ein einzelner Client mehrere Dienste haben mit ihr verbunden sind.

In meinem Stecher, ich schreibe eine Abfrage, die eine Client-ID gibt auf der Grundlage bestimmter Kriterien. Die Kriterien sind wie folgt,

  1. Wenn mindestens ein Dienst vom Typ B und keine Leistungen des Typs A vorhanden sind, Rückkehr id
  2. Wenn mindestens ein Dienst vom Typ C, und keine Dienste vom Typ B oder A vorhanden ist, Rückkehr id
  3. Falls mindestens ein Dienst vom Typ D ist, und keine Dienste vom Typ C oder B oder A exist, kehrt id

und mein aktueller Ansatz ist unter

eine Abfrage ähnlich der bilden,
SELECT c.ClientId
FROM
  Clients AS c
    -- actually INNER JOIN is superfluous in this sample, but required for
    -- other auxilliary criteria i have left out. illustrates relationship
    -- between Clients and Services table
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
-- has at least one service of type B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR 

-- has at least one service of type C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A'))) OR

-- has at least one service of type D, no C, no B, no A
(EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) AND
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')))

wo [dbo].[Get_ServicesByClientIdAndType] ist eine Funktion, die damit verbundenen Dienstleistungen für die angegebene Client-ID und der Diensttyp zurückgibt. Ähnlich wie bei

-- this query is actually significantly more complex than shown
-- below, but this illustrates use of parameters client id and
-- service type
SELECT s.ServiceType
FROM
  Services AS s
WHERE
  s.ClientId = @clientId AND
  s.ServiceType = @serviceType

Unter der Annahme, dies ist ein optimales Mittel, um diesen Anwendungsfall auszudrücken, würde [dbo].[Get_ServicesByClientIdAndType] Unter Abfrage zwischengespeichert wird, funktionieren oder nicht ändert Serviceparameter eine neue Auswertung jeden Aufruf erforderlich machen? [Ich bin Berufung auf dieses Ding wie 9 mal !!! Ausführen von SQL Server 2005]

Ich weiß, dass SQL Server 2005 unterstützt einige Sub-Query-Optimierungen, wie Caching Ergebnisse, aber ich weiß nicht genau, unter welchen Umständen und wie meine Unterabfragen [oder Funktion] so bilden, dass ich die meisten Sql machen Server Fähigkeiten.


EDIT: Bewertung über meine Kriterien und konnte nicht geht ein nagenden Gefühl, etwas abzulassen war. Ich spielte mit einiger Logik in meinem Kopf herum und kam mit dieser [viel einfacher] Formulierung

SELECT c.ClientId
FROM
  Clients AS c
    INNER JOIN Services AS s ON c.ClientId = s.ClientId
WHERE
  NOT EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A')) AND
    (EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'B')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'C')) OR 
    EXISTS (SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'D')))

Im Wesentlichen gibt es kein Szenario mit B, die zur Ablehnung führen würden, in ähnlicher Weise für C und D, so dass jede Konfiguration akzeptabel ist. wir kümmern uns nur, dass A nicht in irgendeiner Auswahl ist. Arg! Charlie Brown!


verlassen beiden Ausdrücke für die Überprüfung, und ich immer noch sehr schätzt die Antworten in Bezug auf SQL Server-Leistung WRT benutzerdefinierte Funktionen.

War es hilfreich?

Lösung

Ich war eine Antwort auf Ihre Frage zu schreiben und in der Zwischenzeit Sie Ihre Anforderungen haben sich geändert, aber Sie sollten keine Probleme haben, meine Lösung Ihrer spezifischen Bedürfnisse zu konvertieren ..

Aber lassen Sie mich von Anfang an beginnen. Ich bin mir ziemlich sicher, dass SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') nicht in irgendeiner Weise von dem Server zwischengespeichert wird. Es ist nicht so klug;). Also ist es mehrfach in Ihrer Haupt-Abfrage

berechnet

So Ihre erste Optimierung sollte in diese Richtung gehen. Sie sollten die Anzahl der Zeiten reduzieren, wenn Get_ServicesByClientIdAndType genannt wird. Sie können es in vielerlei Hinsicht tun. Aber die allgemeine Regel ist, dass, dass Sie für alle Ihre Kunden alle möglichen Ergebnisse dieser Funktion berechnen soll. Diese Ergebnisse sollten in einigen temporarty Tisch gelegt werden, oder sie werden in eine virtuelle Tabelle whis wird von SQL Server selbst vorgenommen werden tritten.

Wenn Sie alle möglichen Ergebnisse bekam man einfach verbinden sie mit Ihren Kunden Tisch. Aber Sie JOIN sie nur ONCE .

Natürlich sind viele Dinge und Optimierung Trick hängt von Ihrem realen Beispiel. Im Beispiel haben Sie gegeben gibt es sogar keine Notwendigkeit für den Einsatz Get_ServicesByClientIdAndType. Warum nicht einfach, diese beiden Tabellen verbinden und einige Berechnungen auf sie durchführen?

Werfen Sie einen Blick auf diese Abfrage:

SELECT A.* FROM
(
 SELECT C.ClientID,
  SUM(CASE(S.ServiceType) WHEN 'A' THEN 1 ELSE 0 END) AS ServiceA,
  SUM(CASE(S.ServiceType) WHEN 'B' THEN 1 ELSE 0 END) AS ServiceB,
  SUM(CASE(S.ServiceType) WHEN 'C' THEN 1 ELSE 0 END) AS ServiceC,
  SUM(CASE(S.ServiceType) WHEN 'D' THEN 1 ELSE 0 END) AS ServiceD
 FROM Clients AS C
 INNER JOIN Services AS s ON c.ClientId = s.ClientId
 GROUP BY C.ClientID
) A
WHERE ((A.ServiceB > 0) AND (A.ServiceA = 0)) 
 OR ((A.ServiceC > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0))
 OR ((A.ServiceD > 0) AND (A.ServiceA = 0) AND (A.ServiceB = 0) AND (A.ServiceC = 0))

In der inneren Abfrage verbinden wir die Tabellen. Wir werfen die Funktion weg, da wir es nicht brauchen. Stattdessen berechnen wir die Anzahl der verschiedenen Dienste für jeden Kunden. Weiter über die inneren Abfrageergebnisse wir Ihre Bedingungen umzusetzen. Wir prüfen nur für das Auftreten von bestimmten Diensten in einem particual Satz.

Das Ergebnis ist wie folgt:

ClientID ServiceA ServiceB ServiceC ServiceD
-------- -------- -------- -------- --------
26915       0        4        2        2
26917       0        0        1        1
26921       0        3        2        3
26927       0        4        2        4

Natürlich können Sie das Endergebnis von Service-Spalten abzustreifen. Ich habe sie aufgenommen, weil ich es so gerne ;-) Und es ermöglicht zu prüfen, ob die Abfrage ordnungsgemäß funktioniert. Sie können sogar eine Abfrage schreiben, die die Anzahl der angegebenen Diensttyp für einen bestimmten Client nicht berechnen. Es funktioniert noch schneller und geben Sie die richtigen Ergebnisse.

Auch wenn Sie wirklich Ihre Funktion benötigen, warum nicht seine Umsetzung in einer Art und Weise zu ändern, dass die Funktion zurückkehren und ID nach dem ersten erfolgreichem beitreten? Es wird Ihnen eine Menge Zeit sparen.

Aber nur Sie wissen, das größere Bild so alles, was ich hier geschrieben Müll sein könnte; -)

Wie auch immer, ich hoffe, ich helfe Ihnen in gewisser Weise.

Andere Tipps

Ich würde vermuten, dass SQL Server Ihre Funktion Get_ServicesByClientIdAndType rufen einmal für jede Kombination von Parameterwerten aber, dass für jede Zeile in der Tabelle Clients. Sie haben drei Kombinationen von Werten, so dass für 100 Zeilen in Client-Tabelle, die Sie 300 Aufrufe der Funktion sehen könnten.

Aber sicher zu sein, die Abfrage in SQL Server Management Studio laufen und schalten Sie die Option „Show Ausführungsplan“. So können Sie leicht, welcher Teil Ihrer Abfrage die meisten Ressourcen und Conentrate verbraucht zu optimieren, dass ein Teil erfassen kann.

Eine Sache im Auge zu behalten ist, „NOT“ wenn überhaupt möglich zu vermeiden. „NOT“ ist nicht sargable, es wird nicht in der Lage sein, alle Vorteile der Indizierung zu nehmen. Auf den ersten Blick sehe ich nicht ein Weg, um es neu zu schreiben, obwohl die Ausdrücke nicht zu vermeiden. FWIW, YMMV. : -)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top