在Postgresql的where子句中使用Alias列


76

我有这样的查询:

SELECT
    jobs.*, 
    (
        CASE
            WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
            ELSE 'NEW'
        END
    ) AS lead_state
FROM
    jobs
    LEFT JOIN lead_informations ON
        lead_informations.job_id = jobs.id
        AND
        lead_informations.mechanic_id = 3
WHERE
    lead_state = 'NEW'

出现以下错误:

PGError: ERROR:  column "lead_state" does not exist
LINE 1: ...s.id AND lead_informations.mechanic_id = 3 WHERE (lead_state...

在MySql中这是有效的,但显然在Postgresql中无效。据我所知,原因是SELECT查询部分的评估晚于该WHERE部分。有没有解决此问题的常用方法?


这是一个很好的问题,但一个奇怪的示例查询。您永远不想为该列选择非NULL值,因此整个CASE语句完全没有必要。
phils 2012年

@phils你是对的。该查询是动态生成的,因此该where子句可能包含其他内容,但该select部分将保持不变。
troelskn 2012年

Answers:


17

正如您所经历的那样,MySQL的支持是非标准的。正确的方法是重新打印SELECT子句中使用的相同表达式:

SELECT
    jobs.*, 
    CASE 
         WHEN lead_informations.state IS NOT NULL THEN lead_informations.state 
         ELSE 'NEW' 
    END AS lead_state
FROM
    jobs
    LEFT JOIN lead_informations ON
        lead_informations.job_id = jobs.id
        AND
        lead_informations.mechanic_id = 3
WHERE
    lead_informations.state IS NULL

5
另外,您可以用COALESCE:代替CASE语句COALESCE(lead_informations.state, 'NEW') AS lead_state
OMG小马

3
不得不重复这样的逻辑似乎有点尴尬,但是我想我会那样做。不知道COALESCE-感谢您的提示。
troelskn

79

我在同一个问题上苦苦挣扎,我认为“ mysql语法是非标准的”不是有效的参数。PostgreSQL还添加了方便的非标准扩展名,例如“ INSERT ... RETURNING ...”,以便在插入后获得自动ID。同样,重复大型查询也不是一个很好的解决方案。

但是,我发现WITH语句非常有帮助。它在查询中创建了一个临时视图,您可以像使用普通表一样使用它。我不确定我是否正确重写了JOIN,但总的来说,它应该像这样工作:

WITH jobs_refined AS (
    SELECT
        jobs.*,
        (SELECT CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) AS lead_state
    FROM jobs
    LEFT JOIN lead_informations
        ON lead_informations.job_id = jobs.id
        AND lead_informations.mechanic_id = 3
)
SELECT *
FROM jobs_refined
WHERE lead_state = 'NEW'

15
当您要在两种不同的产品上做某事时,说某事是“非标准”是完全正确的。它们都实现了SQL标准的一部分,并且都具有非标准扩展。不要指望非标准扩展名会转换。不要期望SQL标准部分能够翻译。话虽如此-感谢WITH的例子。
凌乱

1
当标准说一件事时,您做了其他事情,而对标准不作声的产品进行了扩展,这又有所不同。
柯克·罗伊巴尔 Kirk Roybal)2014年

1
这是对问题的正确答案。接受的答案起作用的唯一原因是因为WHERE子句所在的列实际上存在于联接表上,该表不解决查询创建的列的别名,例如子查询的别名。
花cap

23

您可能需要在where子句中复制case语句,或者我的偏好是执行以下操作:

SELECT *
FROM (
SELECT 
    jobs.*, 
    (CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) as lead_state
FROM 
    "jobs"
    LEFT JOIN lead_informations ON lead_informations.job_id = jobs.id
    AND lead_informations.mechanic_id = 3
) q1
WHERE (lead_state = 'NEW')

2

我相信常见的解决方案是使用内部SELECT进行计算(在这种情况下为CASE语句),以便在执行到该外部查询时,整个外部查询都可以使用内部SELECT的结果。否则,将首先评估WHERE子句,并且对SELECT子句一无所知。


0

我在这样的地方使用别名。(内部查询)。

Select "Vendors"."VendorId", "Vendors"."Name","Result"."Total" 
From (Select "Trans"."VendorId", ("Trans"."A"+"Trans"."B"+"Trans"."C")    AS "Total"
        FROM "Trans"
    WHERE "Trans"."Year"=2014                                                
    ) As "Result"
JOIN "Vendors" ON "Result"."VendorId"="Vendors"."VendorId" 
WHERE "Vendors"."Class"='I' AND "Result"."Total" > 200

0
SELECT "tab_1"."BirthDate", "tab_1"."col_1" FROM (
   SELECT BirthDate, DATEADD(year, 18, BirthDate) AS "col_1" FROM Employees
) AS "tab_1"
WHERE "tab_1"."col_1" >= '2000-12-31';

7
带代码的答案通常最好附有一个解释,说明您发布的内容为何起作用以及它如何专门解决该问题
C.Nivs

@ C.Nivs该评论不言自明
jan4co

@GianGomen可能不适合新用户,特别是如果错过了如何解答的指南。它确实可以帮助其他人,以后可能会遇到此答案。这就像您可能会认为有关如何提出问题的准则是不言自明的,但是坏问题经常被问到,因此提供一些有用的建议可以使人朝正确的方向前进
C.Nivs
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.