Postgresql CASE语法的问题

时间:2012-08-09 06:53:19

标签: sql postgresql datetime aggregate-functions case

这是我的SQL查询:


SELECT (CASE (elapsed_time_from_first_login IS NULL) 
                        WHEN true THEN 0 
                        ELSE elapsed_time_from_first_login END) 
FROM (
   SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4) 
   FROM radacct
   WHERE UserName = 'test156') AS elapsed_time_from_first_login;

当我执行上述查询时,我收到此错误:

  

错误:CASE类型记录和整数无法匹配

从错误消息我明白PostgreSQL分别将第二个选择elapsed_time_from_first_login作为一行,即使它总是一个值(因为min()函数)。< / p>

问题:您对如何处理此查询有什么建议吗?

4 个答案:

答案 0 :(得分:3)

我想,你实际上要做的事情应该是这样的:

SELECT COALESCE((
    SELECT now() - min(AcctStartTime)
    FROM   radacct
    WHERE  userName = 'test156'), interval '0s')

如果子选择(别名elapsed_time_from_first_login)没有找到行,它将返回NULL值,但没有行 < / p> <击>

要抓住这个,你可以将whole sub-select包装在COALESCE语句中 - 这比CASE更简单。

我首先忽略了汇总函数min(),它将“无行”转换为NULL。因此,只要您在子选择中有一个聚合函数,下面的简单形式就可以了。这是一个快速demo on sqlfiddle to show the effect

已经指出了查询的其他问题。但这是更简单的解决方案。它会返回interval而不是integer


转换为整数

在@artaxerxe评论之后简化。
简单的表单无需检查“无行”即可完成工作:

SELECT COALESCE(EXTRACT(epoch FROM now() - min(AcctStartTime))::int, 0)
FROM   radacct
WHERE  userName = 'test156';

有关EXTRACT(epoch FROM INTERVAL) in the manual

的详细信息

聚合函数和NULL

还有一句话:如果您使用了最初在问题中使用的汇总函数count() - 而不是sum() - 结果会有所不同。 count()是标准聚合函数中的一个特例,因为它永远不会返回NULL。如果未找到任何值(或行),则返回0

excerpt from the manual on aggregate functions似乎涵盖了它:

  

应该注意除了count之外,这些函数返回a   没有选择行时为null。特别是,没有行的总和   返回null,而不是正如人们所期望的那样,并且array_agg返回null   没有输入行时,而不是空数组。合并   function可用于将零或空数组替换为null   必要时。

答案 1 :(得分:2)

Postgres抱怨0elapsed_time_from_first_login的类型不同。

试试这个(也简化你的选择):

select
    coalesce(elapsed_time_from_first_login::INT4, 0)
from ...

答案 2 :(得分:1)

以下是我格式化SQL的方法,现在正在运行:

SELECT coalesce(result, 0) 
     FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4) as result 
                FROM radacct WHERE UserName = 'test156') as elapsed_time_from_first_login;

答案 3 :(得分:1)

第二个SELECT返回一个名为elapsed_time_from_first_login的表,其中包含一列和一行。您必须为该列添加别名并在CASE子句中使用它。你不能把整个表(即使只有一列,只有一行)放在一个预期的值。

SELECT (CASE (elapsed_time IS NULL) 
                        WHEN true THEN 0 
                        ELSE elapsed_time end) 
   FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4) 
                AS elapsed_time                -- column alias
         FROM radacct 
         WHERE UserName = 'test156'
        ) as elapsed_time_from_first_login;    -- table alias

您可以使用CASE函数缩短COALESCE()(并可选择添加该列的别名以显示在结果中):

SELECT COALESCE(elapsed_time, 0) 
         AS elapsed_time 
   FROM (SELECT (now()::ABSTIME::INT4 - min(AcctStartTime)::ABSTIME::INT4) 
                AS elapsed_time            
         FROM radacct 
         WHERE UserName = 'test156'
        ) as elapsed_time_from_first_login;    -- table alias