Question

Je travaille actuellement sur un cas d'utilisation particulièrement complexe. Ci-dessous Simplifier:)

Tout d'abord, un dossier client a beaucoup à une relation avec un ensemble de services, qui est, un client peut avoir plusieurs services qui y sont associés.

Dans mon déclencheur, je suis en train d'écrire une requête qui renvoie l'identifiant d'un client en fonction de certains critères. Les critères sont les suivants,

  1. Si au moins un service est de type B, et aucun service de type A existe, id de retour
  2. Si au moins un service est de type C, et aucun service de type B ou A existe, id de retour
  3. Si au moins un service est de type D, et aucun service de type C ou B ou A exist, id retour

et mon approche actuelle est de former une requête similaire à celui ci-dessous

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')))

[dbo].[Get_ServicesByClientIdAndType] est une fonction qui renvoie des services associés pour l'ID client spécifié et le type de service. Similaire à

-- 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

En supposant qu'il est moyen optimal d'exprimer ce cas d'utilisation, fonctionnerait sous-requête [dbo].[Get_ServicesByClientIdAndType] être mises en cache ou ne changeant le paramètre de service nécessite une nouvelle évaluation chaque appel? [Je suis cette chose comme invoque pas 9 fois !!! exécutant SQL Server 2005]

Je sais que Sql Server 2005 prend en charge certaines optimisations sous-requêtes, comme les résultats de la mise en cache, mais je ne sais pas pour certains dans quelles circonstances ou comment former mes sous-requêtes [ou fonction] de telle sorte que je fais le plus de Sql les capacités du serveur.


EDIT: en revue mes critères ci-dessus, et ne pouvait pas laisser aller d'une chose lancinante sentiment était éteint. J'ai joué avec une certaine logique dans ma tête, et est venu avec cette formulation [beaucoup plus simple]

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')))

essentiellement, il existe pas de scénario impliquant B qui conduirait à un rejet, de même pour C et D, de sorte que toute configuration est acceptable. nous nous soucions seulement que A est présent dans aucune sélection. Arg! Charlie Brown!


laissant les deux expressions pour examen, et je reste très bien apprécier les réponses concernant les performances utilisateur WRT SQL Server fonctions définies.

Était-ce utile?

La solution

Je rédigeais une réponse à votre question et en attendant, vous avez changé vos besoins, mais vous ne devriez pas avoir de problèmes pour convertir ma solution à vos besoins spécifiques ..

Mais permettez-moi de commencer par le début. Je suis assez sûr que SELECT * FROM Get_ServicesByClientIdAndType (c.ClientId, 'A') ne cache en tout cas par le serveur. Il est pas intelligent,.) Il est calculé plusieurs fois dans votre requête principale

Donc, votre première optimisation doit aller dans cette direction. Vous devez réduire le nombre de fois où Get_ServicesByClientIdAndType est appelé. Vous pouvez le faire à bien des égards. Mais la règle générale est que vous devez calculer tous les résultats possibles de cette fonction pour tous vos clients. Ces résultats doivent être mis dans une table temporarty ou ils seront puted dans une whis table virtuelle est faite par SQL Server lui-même.

Lorsque vous avez obtenu vos résultats possibles que vous les rejoindre simplement avec votre table de clients. Mais vous les rejoins seulement UNE FOIS .

Bien sûr, beaucoup de choses et astuces d'optimisation dépend de votre exemple réel. Dans l'exemple que vous avez donné il n'y a même pas besoin de Get_ServicesByClientIdAndType d'utilisation. Pourquoi ne pas simplement se joindre à ces deux tables et d'effectuer des calculs sur eux?

Jetez un oeil à cette requête:

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))

Dans la requête interne nous rejoignons les tables. Nous jetons la fonction depuis nous ne besoin. , On calcule plutôt le nombre de services différents pour chaque client. Suivant sur les résultats de la requête interne, nous mettons en œuvre vos conditions. Nous vérifions simplement l'apparition de services donnés dans un ensemble particual.

Le résultat est comme ceci:

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

Bien sûr, vous pouvez enlever le résultat final des colonnes de service. Je les ai inclus parce que je l'aime cette façon ;-) Et il permet de vérifier si la requête fonctionne correctement. Vous pouvez même écrire une requête qui ne calcule pas le nombre de type de service donné pour un client donné. Il fonctionnera encore plus vite et vous donner les résultats appropriés.

Aussi, si vous avez vraiment besoin de votre fonction, pourquoi ne pas changer sa mise en œuvre d'une manière que la fonction retourne et ID après la première jointure succesfull? Il vous permettra d'économiser beaucoup de temps.

Mais que vous savez l'image plus grande si j'écrit peut-être des déchets ici; -)

Quoi qu'il en soit, je l'espère, je vous ai aidé en quelque sorte.

Autres conseils

Je suppose que sql server appelle votre fonction Get_ServicesByClientIdAndType une fois pour chaque combinaison de valeurs de paramètres, mais que pour chaque ligne de la table des clients. Vous avez trois combinaisons de valeurs, donc pour 100 lignes dans la table des clients, vous pouvez voir 300 appels de la fonction.

Mais pour être sûr, exécuter la requête en studio de gestion de serveur sql et activez l'option « plan d'exécution du spectacle ». De cette façon, vous pouvez facilement détecter quelle partie de votre requête consomme le plus de ressources et conentrate sur l'optimisation de cette partie.

Une chose à garder à l'esprit est d'éviter « pas » si possible. « NOT » est non-sargable, il ne sera pas en mesure de prendre tous les avantages de l'indexation. À première vue, je ne vois pas un moyen de réécrire pour éviter les pas des expressions bien. FWIW, YMMV. : -)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top