2010-07-07 5 views
4

J'ai une fonction définie par l'utilisateur (par exemple myUDF(a,b)) qui renvoie un entier.Clause WHERE sur une colonne résultant d'un UDF

Je suis en train d'assurer cette fonction sera appelée une seule fois et ses résultats peuvent être utilisés comme condition dans la clause WHERE:

SELECT col1, col2, col3, 
     myUDF(col1,col2) AS X 
From myTable 
WHERE x>0 

SQL Server essaie de détecter x comme colonne, mais il est vraiment un alias pour une valeur calculée.

Comment pouvez-vous réécrire cette requête afin que le filtrage puisse être effectué sur la valeur calculée sans avoir à exécuter le fichier UDF plusieurs fois?

Répondre

5

Si vous utilisez SQL Server 2005 et au-delà, vous pouvez utiliser Cross Appliquer:

Select T.col1, T.col2, FuncResult.X 
From Table As T 
    Cross Apply (Select myUdf(T.col1, T.col2) As X) As FuncResult 
Where FuncResult.X > 0 
+0

Qu'est-ce que Cross Apply exactement? – Gzim

+0

@Gzim - Pensez à Cross Apply en tant que mélange entre une table dérivée et une sous-requête corrélée. En effet, il vous permet de rejoindre une sous-requête qui référence des éléments en dehors de la requête. Dans mon exemple, je fais référence à col1 et col2 à partir d'une autre table de la clause From. Voici un autre article sur le sujet: http://www.sqlteam.com/article/using-cross-apply-in-sql-server-2005 – Thomas

+0

Oh mon garçon c'est si puissant. Surtout quand vous avez des cas compliqués lors de l'insertion et la sélection d'une table (en même temps) et aussi d'une table UDF qui accepte les paramètres de la table sélectionnée. Thanx Thomas. – Gzim

4

essayer

SELECT col1, col2, col3, dbo.myUDF(col1,col2) AS X 
From myTable 
WHERE dbo.myUDF(col1,col2) >0 

mais sachez que cela entraînera une analyse car il ne SARGable

Voici une autre façon

select * from(
SELECT col1, col2, col3, dbo.myUDF(col1,col2) AS X 
From myTable) as y 
WHERE x>0 
+0

ouais mais cette fonction prend tellement de temps et j'essaie de l'éviter en utilisant deux fois – Gzim

+0

Eh bien, le premier cas devrait vraiment seulement l'exécuter une fois, et le second - la façon d'exprimer la table commune (ou la réponse de Baaju) calculer une fois – Rup

+0

Juste parce que vous avez tapé la fonction deux fois ne signifie pas qu'il sera appelé deux fois. SQL Server reconnaîtra probablement que c'est la même fonction dans les deux endroits et ne l'évalue qu'une seule fois. – Gabe

2

SQL Server ne vous permet pas de faire référence à des colonnes par alias. Soit vous devez écrire la colonne deux fois:

SELECT col1, col2, col3, myUDF(col1,col2) AS X 
From table myTable 
WHERE myUDF(col1,col2) > 0 

Ou utilisez un sous-requête:

SELECT * 
FROM (
     SELECT col1, col2, col3, myUDF(col1,col2) AS X 
     From table myTable 
     ) as subq 
WHERE x > 0 
0

Je ne suis pas sûr à 100% de ce que vous faites, mais puisque x n'est pas une colonne, je l'enlèverais de votre instruction SQL pour que vous ayez:

SELECT col1, col2, col3, myUDF(col1,col2) AS X From myTable 

Et puis ajoutez la condition à votre code si vous appelez seulement quand x > 0

7
With Tbl AS 
(SELECT col1, col2, col3, myUDF(col1,col2) AS X 
     From table myTable ) 

SELECT * FROM Tbl WHERE X > 0 
+1

Excellente réponse pour SQL Server 2005+! –

1

Selon le udf et l'utilité ou fréquemment utilisé est, vous pouvez envisager d'ajouter à la table comme computed column . Vous pouvez ensuite filtrer sur la colonne comme d'habitude et ne pas avoir à écrire la fonction du tout dans les requêtes.

0

Votre question est mieux répondu par les clauses "With" (CTE's je pense, dans MSSS). Vraiment la meilleure question est: Dois-je stocker cette valeur calculée ou recalculer pour chaque ligne, chaque fois que je interroge la table.

Y a-t-il 10 lignes dans le tableau et toujours 10 lignes?

Des lignes sont-elles ajoutées en permanence?

Avez-vous une stratégie de purge en place ou laissez-la simplement pousser?

Interrogez cette table seulement une fois par mois?

S'il s'agit d'une fonction «longue durée» (même après que vous l'ayez optimisée), pourquoi voulez-vous l'exécuter plus d'une fois, jamais?

Vous avez demandé une fois, mais vous demandez vraiment une fois par ligne, par requête.

Stocker la réponse dans un index ou "colonne virtuelle"

Plus:

Calculer exactement une fois par ligne. Les temps de requête ne croissent pas linéairement.

Moins: Augmentations insérer/mettre à jour le temps

Calcul chaque fois

Plus:

temps d'insertion/mise à jour optimisée

Inconvénients: temps de requête augmente avec le nombre de lignes. (non évolutif)

Si vous interrogez une fois par mois, pourquoi vous souciez-vous de la performance, accordez quelque chose qui a un impact important sur vos opérations (très légèrement facétieux).

Si vous n'insérez pas un paquet (selon votre matériel) de lignes par seconde, est-ce que passer ce temps à l'avance va faire une grande différence?