No puede hacer referencia a un alias excepto en ORDER BY porque SELECT es la segunda última cláusula que se evalúa. Dos soluciones alternativas:
SELECT BalanceDue FROM (
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
) AS x
WHERE BalanceDue > 0;
O simplemente repita la expresión:
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE (InvoiceTotal - PaymentTotal - CreditTotal) > 0;
Prefiero el último. Si la expresión es extremadamente compleja (o costosa de calcular), probablemente debería considerar una columna calculada (y tal vez persistente), especialmente si muchas consultas se refieren a esta misma expresión.
PD: tus miedos parecen infundados. Al menos en este simple ejemplo, SQL Server es lo suficientemente inteligente como para realizar el cálculo solo una vez, aunque lo haya referenciado dos veces. Anímate y compara los planes; Verás que son idénticos. Si tiene un caso más complejo en el que ve la expresión evaluada varias veces, publique la consulta más compleja y los planes.
Aquí hay 5 consultas de ejemplo que arrojan exactamente el mismo plan de ejecución:
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 resultante para las cinco consultas: