PostgreSQL加入子查询

时间:2018-02-26 20:00:40

标签: sql postgresql

我有以下表格:

  

svc_sms:

     

sms_pk | sms_title

  

svc_sms_msg:

     

msg_pk | sms_text | msg_status | sms_pk(FK到svc_sms.sms_pk)

我想通过msg_status查询和分组来自const express = require('express'); const app = express(); var blog = require('./routes/blog'); app.use('/blogs', blog); 的所有行,所以我这样做:

svc_sms_msg

现在认为msg.msg_status范围仅在0到4之间,所以我需要得到每个带有sms_status ='x'的sms出现在sms_pk分组中的次数。我需要我的结果是这样的:

    SELECT sms.sms_pk, msg.msg_status, COUNT(msg.msg_status) as num
        FROM svc_sms_msg as msg
        INNER JOIN svc_sms as sms ON sms.sms_pk = msg.sms_pk
        GROUP BY sms.sms_pk, msg.msg_status
        ORDER BY sms.sms_pk, msg.msg_status

但是当没有'msg_status = x'的行(msg中)时,连接什么也没给我,我需要显示“状态”的大小,即使它们是0的 在我这样做之前:

> sms.sms_pk  |  msg.msg_status  |  num 
> 
> 1                   0               1
> 
> 1                   1               5
> 
> 1                   2               4
> 
> 1                   3               20
> 
> 1                   4               18
> 
> 2                   0                5
> 
> 2                   1                0
> 
> 2                   2                3
> 
> 2                   3                23
> 
> 2                   4                0
等等......但事实证明,在我的情况下,它太慢而且不实用。我怎样才能以更有效的方式做到这一点?谢谢

4 个答案:

答案 0 :(得分:2)

您可以创建所有状态(0到4)的派生表(下面的All_Status),并将其连接到您的svc_sms,然后将其连接到svc_sms_msg

交叉连接确保每个SVC_SMS都具有所有状态,即使没有消息存在,因此这些记录的计数将为零而不是丢失。通过在sms_pk和" status"上加入svc_sms_msg。我们保留所有交叉连接记录,确保每个状态都分配给每个SMS_PK;因此当msg.msg_status不存在时,计数将为0(为空)

SELECT sms.sms_pk, msg.msg_status, COUNT(msg.msg_status) as num
FROM svc_sms as sms 
CROSS JOIN (SELECT unnest(array[0,1,2,3,4]) status) All_Status
LEFT JOIN svc_sms_msg as msg
  ON sms.sms_pk = msg.sms_pk
 AND All_Status.Status = msg.msg_Status
GROUP BY sms.sms_pk, msg.msg_status
ORDER BY sms.sms_pk, msg.msg_status

仅取决于您是希望为每个状态添加更多列还是为每个状态和sms_pk添加一行。

给定此方法,总行数应等于SMS_Pk的5 *#。

答案 1 :(得分:1)

由于很酷的postgresql功能FILTER

,它可以变得更简单
SELECT
    msg_pk
   ,COUNT() FILTER (WHERE msg_status = 0) as cnt_initial
   ,COUNT() FILTER (WHERE msg_status = 1) as cnt_pending
   ,COUNT() FILTER (WHERE msg_status = 2) as cnt_sended
FROM svc_sms_msg
GROUP BY sms_pk;

答案 2 :(得分:0)

也许条件聚合可行:

SELECT SUM(CASE WHEN (msg_status = 0) THEN 1 ELSE 0 END) as cnt_initial,
       SUM(CASE WHEN (msg_status = 1) THEN 1 ELSE 0 END) as cnt_pending;
       SUM(CASE WHEN (msg_status = 2) THEN 1 ELSE 0 END) ) as cnt_sended
FROM svc_sms_msg;

您可以按特定列进行汇总。 。 。所以你可能想要:

SELECT sms_pk, SUM(CASE WHEN (msg_status = 0) THEN 1 ELSE 0 END) as cnt_initial,
       SUM(CASE WHEN (msg_status = 1) THEN 1 ELSE 0 END) as cnt_pending;
       SUM(CASE WHEN (msg_status = 2) THEN 1 ELSE 0 END) ) as cnt_sended
FROM svc_sms_msg
GROUP BY sms_pk;

我发现在查询中遵循逻辑有点困难,但我认为这就是你要做的事情。

答案 3 :(得分:0)

希望,我理解你的问题。

请查看以下查询。

SELECT sms.sms_pk, msg.msg_status,
COUNT(CASE WHEN msg.msg_status='0' THEN 1 END) as num_0
COUNT(CASE WHEN msg.msg_status='1' THEN 1 END) as num_1
COUNT(CASE WHEN msg.msg_status='2' THEN 1 END) as num_2
COUNT(CASE WHEN msg.msg_status='3' THEN 1 END) as num_3
COUNT(CASE WHEN msg.msg_status='4' THEN 1 END) as num_4
            FROM svc_sms as sms 
            LEFT OUTER JOIN svc_sms_msg as msg ON sms.sms_pk = msg.sms_pk
            GROUP BY sms.sms_pk, msg.msg_status
            ORDER BY sms.sms_pk, msg.msg_status;