web-dev-qa-db-fra.com

Alias ​​de référence (calculé dans SELECT) dans la clause WHERE

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error

La valeur calculée 'BalanceDue' définie comme variable dans la liste des colonnes sélectionnées ne peut pas être utilisée dans la clause WHERE.

Y a-t-il un moyen possible? Dans cette question connexe ( --- (Utilisation d’une variable dans MySQL Select Statment dans une clause Where ), il semble que la réponse serait, en fait, non, il vous suffirait d’écrire le calcul ( et effectuent ce calcul dans la requête) deux fois, dont aucun n'est satisfaisant.

115
Nicholas Petersen

Vous ne pouvez pas référencer un alias sauf dans ORDER BY car SELECT est l'avant-dernière clause évaluée. Deux solutions de contournement:

SELECT BalanceDue FROM (
  SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
  FROM Invoices
) AS x
WHERE BalanceDue > 0;

Ou répétez simplement l'expression:

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE  (InvoiceTotal - PaymentTotal - CreditTotal)  > 0;

Je préfère ce dernier. Si l'expression est extrêmement complexe (ou coûteuse à calculer), vous devriez probablement envisager une colonne calculée (et peut-être même persistée), en particulier si de nombreuses requêtes font référence à cette même expression.

PS tes peurs semblent sans fondement. Dans cet exemple simple au moins, SQL Server est suffisamment intelligent pour effectuer le calcul une seule fois, même si vous l'avez référencé deux fois. Allez-y et comparez les plans; vous verrez qu'ils sont identiques. Si vous avez un cas plus complexe dans lequel l'expression est évaluée plusieurs fois, envoyez la requête plus complexe et les plans.

Voici 5 exemples de requêtes qui donnent toutes exactement le même plan d'exécution:

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE LEN(name) + column_id > 30;

SELECT x FROM (
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE column_id + LEN(name) > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE LEN(name) + column_id > 30;

Plan résultant pour les cinq requêtes:

enter image description here

215
Aaron Bertrand

Vous pouvez le faire en utilisant cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;
6
Manoj

En réalité, il est possible de définir efficacement une variable pouvant être utilisée à la fois dans les clauses SELECT, WHERE et autres.

Une jointure croisée ne permet pas nécessairement une liaison appropriée aux colonnes de la table référencée, cependant OUTER APPLY le permet - et traite les valeurs NULL de manière plus transparente.

SELECT
    vars.BalanceDue
FROM
    Entity e
OUTER APPLY (
    SELECT
        -- variables   
        BalanceDue = e.EntityTypeId,
        Variable2 = ...some..long..complex..expression..etc...
    ) vars
WHERE
    vars.BalanceDue > 0

Félicitations à Syed Mehroz Alam .

3
Peter Aylett