WHERE 절의 참조 별명 (SELECT에서 계산 됨)


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

선택한 열 목록에서 변수로 설정된 계산 된 값 'BalanceDue'는 WHERE 절에서 사용할 수 없습니다.

할 수있는 방법이 있습니까? 이 관련된 질문에서 ( WHERE 절에서 MySQL을 선택 한 Statment의 변수를 사용하여 ), 답이 될 것 같은, 실제로, 아니, 당신은 단지 계산을 쓸 것 (보인다 , 전혀 쿼리에 그 계산을 수행)를 두 번 만족합니다.

답변:


237

SELECT는 평가 된 마지막 두 번째 절이므로 ORDER BY를 제외하고 별칭을 참조 할 수 없습니다. 두 가지 해결 방법 :

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

또는 표현을 반복하십시오.

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

나는 후자를 선호합니다. 표현식이 매우 복잡하거나 계산 비용이 많이 드는 경우, 특히 많은 쿼리가이 동일한 표현식을 참조하는 경우 계산 열 (또는 지속)을 고려해야합니다.

추신 : 당신의 두려움은 근거가없는 것 같습니다. 이 간단한 예에서, SQL Server는 계산을 두 번 참조했지만 계산을 한 번만 수행 할 수있을 정도로 똑똑합니다. 계속해서 계획을 비교하십시오. 당신은 그들이 동일한 것을 볼 수 있습니다. 표현식이 여러 번 평가되는 더 복잡한 경우가있는 경우 더 복잡한 쿼리 및 계획을 게시하십시오.

다음은 모두 정확히 동일한 실행 계획을 생성하는 5 가지 예제 쿼리입니다.

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;

다섯 가지 쿼리 모두에 대한 결과 계획 :

여기에 이미지 설명을 입력하십시오


11
와. SQL 서버는 한 번만 계산을 수행 할 수있는 스마트 충분하다
alternatefaraz

5
와우이 매우 높은 품질의 답변입니다!
Siddhartha

MERGE 문에 몇 가지 추가 조건이 필요했으며 이것이 유일한 작동 방법이었습니다. 감사!
Eric Burdo

1
@EricBurdo를 사용 MERGE하는 경우이 모든 MERGE사항을주의 깊게 고려했는지 확인하십시오 .
Aaron Bertrand

11

당신은 이것을 사용하여 이것을 할 수 있습니다 cross apply

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

4

실제로 SELECT, WHERE 및 기타 절에서 사용할 수있는 변수를 효과적으로 정의 할 수 있습니다.

교차 조인은 참조 된 테이블 열에 대한 적절한 바인딩을 반드시 허용하지는 않지만 OUTER APPLY는 허용하며 널을보다 투명하게 처리합니다.

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

Syed Mehroz Alam 에게 찬사를 보냅니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.