如何在某些条件下计算所选行中的值-Potgresql

时间:2019-10-22 06:18:09

标签: sql postgresql aggregate countif postgresql-9.6

我正在寻找一些内置的Postresql功能,该功能可以在某些条件下计算行值(而不是列)。

的类似物
=countif(a:a;"Yes")

在Excel中

我在stackoverflow上看到了许多类似问题的答案,但是当您过滤表列而不是行中的数据时,提供了所有解决方案。 但是我需要解析行中的数据。 我不喜欢使用交叉表的解决方案,因为原始选择的列超过60列,并且不希望两次执行相同的查询(但是如果这是唯一的解决方案,我会这样做的。)

一些测试示例,最后一行“ num_of_yes”应该在其中,该行显示连续的“是”答案的数量。 测试数据

CREATE TABLE main_data (
    id serial PRIMARY KEY,
    name VARCHAR(255) default NULL,
    lastname VARCHAR(255) default NULL,
    username VARCHAR(255) default NULL,
    job VARCHAR(255) default NULL,
    age integer  default NULL,
    licenseid integer  default NULL,
    account integer  default NULL
);

INSERT INTO main_data VALUES(1,'Jhon', 'Brown', 'jbrown', 'some job', 35, 11112333, 3333455);
INSERT INTO main_data VALUES(2,'Bob', NULL, 'bob', 'another job', 64, 1000500, 5555252);
INSERT INTO main_data VALUES(3,'Mike', 'McDonald', 'mike', NULL, 8, NULL, NULL);

选择查询:

select id, name,
case when lastname notnull then 'Yes'::text else 'No'::text end as "has_lastname",
case when username notnull then 'Yes'::text else 'No'::text end as "has_username",
case when job notnull then 'Yes'::text else 'No'::text end as "has_job",
case when age < 16 then 'Yes'::text else 'No'::text end as "is_child",
case when licenseid notnull then 'Yes'::text else 'No'::text end as "has_licenseid",
case when account notnull then 'Yes'::text else 'No'::text end as "has_account"
from main_data
order by id;

对于选择查询,我具有以下输出:

| id | name | has_lastname | has_username | has_job | is_child | has_licenseid | has_account |
|----|------|--------------|--------------|---------|----------|---------------|-------------|
|  1 | Jhon | Yes          | Yes          | Yes     | No       | Yes           | Yes         |
|  2 | Bob  | No           | Yes          | Yes     | No       | Yes           | Yes         |
|  3 | Mike | Yes          | Yes          | No      | Yes      | No            | No          |

我需要在最后一列中添加“是”答案。

所需的输出应如下所示:

| id | name | has_lastname | has_username | has_job | is_child | has_licenseid | has_account | num_of_yes |
|----|------|--------------|--------------|---------|----------|---------------|-------------|------------|
|  1 | Jhon | Yes          | Yes          | Yes     | No       | Yes           | Yes         |          5 |
|  2 | Bob  | No           | Yes          | Yes     | No       | Yes           | Yes         |          4 |
|  3 | Mike | Yes          | Yes          | No      | Yes      | No            | No          |          3 |

我正在使用Postgresql 9.6.5

2 个答案:

答案 0 :(得分:3)

您可以将行转换为JSONB值,然后计算Yes的值:

select *, 
       (select count(*) 
        from jsonb_each_text(to_jsonb(t) - 'id' - 'name') as x(k,v)
        where v = 'Yes') as num_of_yes
from (
  select id, name,
         case when lastname is not null then 'Yes' else 'No' end as "has_lastname",
         case when username is not null then 'Yes' else 'No' end as "has_username",
         case when job is not null then 'Yes' else 'No' end as "has_job",
         case when age < 16 then 'Yes' else 'No' end as "is_child",
         case when licenseid is not null then 'Yes' else 'No' end as "has_licenseid",
         case when account is not null then 'Yes' else 'No' end as "has_account"
  from main_data
) t  
order by id;

表达式to_jsonb(t) - 'id' - 'name'将整行转换为JSON值,并从中删除idname键。然后jsonb_each_text()遍历所有键/值对,where v = 'Yes'然后使子查询计算Yes

在线示例:https://rextester.com/PLJA96007


另一种选择是使用num_nonnulls()函数:

select id, name,
       case when lastname is not null then 'Yes' else 'No' end as "has_lastname",
       case when username is not null then 'Yes' else 'No' end as "has_username",
       case when job is not null then 'Yes' else 'No' end as "has_job",
       case when age < 16 then 'Yes' else 'No' end as "is_child",
       case when licenseid is not null then 'Yes' else 'No' end as "has_licenseid",
       case when account is not null then 'Yes' else 'No' end as "has_account",
       num_nonnulls(lastname, username, job, licenseid, account, nullif(age < 16, false)) as num_of_yes
from main_data
order by id;

这很可能会比JSONB解决方案更快。


请注意,如果您想使用真实的boolean列,则case表达式可以简化为: lastname is not null as "has_lastname"age < 16 as "is_child"

答案 1 :(得分:1)

您可以考虑使用array_lengthstring_to_array

select *
        , array_length(string_to_array(replace(t1::text,t1.name,''), ',Yes'), 1) - 1  
from 
  (select id, name,
  case when lastname notnull then 'Yes'::text else 'No'::text end as "has_lastname",
  case when username notnull then 'Yes'::text else 'No'::text end as "has_username",
  case when job notnull then 'Yes'::text else 'No'::text end as "has_job",
  case when age < 16 then 'Yes'::text else 'No'::text end as "is_child",
  case when licenseid notnull then 'Yes'::text else 'No'::text end as "has_licenseid",
  case when account notnull then 'Yes'::text else 'No'::text end as "has_account"
  from main_data) t1
order by id;
相关问题