Postgres-在表中的各列中搜索特定值

时间:2019-04-04 12:56:01

标签: sql postgresql

我有一个表,其中列出了客户在商店中由客户购买的所有水果:

| cust_name | fruit1 | fruit2  | fruit3 |
|-----------|--------|---------|--------|
| cust_a    | apples | oranges | pears  |
| cust_b    | pears  |         |        |
| cust_c    |        |         | apples |

我正在尝试创建一个输出,其中显示上表中被标记为apples的客户购买了哪个水果(fruit1 / fruit2,fruit3)。我知道case语句只能应用于单个列,所以我想知道是否有办法吸引购买apples的客户。

预期输出:

cust_a,fruit1
cust_b,
cust_c,fruit3

3 个答案:

答案 0 :(得分:3)

一种无需编写复杂的WHERE子句并且很容易扩展到更多列的方法是将行转换为JSON并遍历结果JSON值的键:

select t.cust_name, string_agg(r.field, ',')
from the_table t
  left join lateral jsonb_each_text(to_jsonb(t) - 'cust_name') as r(field, fruit) 
                 on r.fruit = 'apples'
group by t.cust_name;

to_jsonb(t) - 'cust_name'使用行中的所有列创建一个JSON值,并删除cust_name。不必严格地从JSON中删除cust_name,因为它不太可能包含水果名称,因此无论如何都不会返回它。

jsonb_each_text()然后在所有列上“迭代”,只保留包含值apples的那些列,然后将结果汇总回到逗号分隔的列表中,以防万一。

具有以下示例数据:

create table the_table (cust_name text, fruit1 text, fruit2 text, fruit3 text)
insert into the_table
values 
  ('cust_a', 'apples', 'oranges', 'pears'),
  ('cust_b', 'pears', null, null),
  ('cust_c', null,  null, 'apples'),
  ('cust_d', 'apples',  null, 'apples');

上面的查询返回:

cust_name | string_agg   
----------+--------------
cust_a    | fruit1       
cust_b    |              
cust_c    | fruit3       
cust_d    | fruit1,fruit3

正确规范数据模型将是一个更好的解决方案。

答案 1 :(得分:0)

CASE与多个WHEN配合使用可以得到预期的结果:

DECLARE FruitName VARCHAR(50) := 'apples';

SELECT cust_name,
       CASE WHEN fruit1 = FruitName THEN 'fruit1'
            WHEN fruit2 = FruitName THEN 'fruit2'
            WHEN fruit3 = FruitName THEN 'fruit3'
       ELSE '' END AS fruit

答案 2 :(得分:0)

您可以创建存储水果及其编号的类型:

CREATE TYPE num_fruit AS (
    num integer,
    fruit text
);

鉴于这种类型,您可以使用unnest将列扩展为行(我不确定表达式是否正确):

CREATE TABLE customer_fruits (cust_name text, fruit1 text, fruit2 text, fruit3 text);
INSERT INTO customer_fruits VALUES 
  ('cust_a', 'apples', 'oranges', 'pears'),
  ('cust_b', 'pears', NULL, NULL),
  ('cust_c', NULL, NULL, 'apples'),
  ('cust_d', 'apples', NULL, 'apples');


SELECT cust_name, unnest(ARRAY[ROW(1, fruit1), ROW(2, fruit2), ROW(3, fruit3)]::num_fruit[]) as nf FROM customer_fruits;

输出:

cust_name | nf
-----------------------
cust_a    | (1,apples)
cust_a    | (2,oranges)
cust_a    | (3,pears)
cust_b    | (1,pears)
cust_b    | (2,)
cust_b    | (3,)
cust_c    | (1,)
cust_c    | (2,)
cust_c    | (3,apples)
cust_d    | (1,apples)
cust_d    | (2,)
cust_d    | (3,apples)

现在,您只需在SELECT上的fruit行中返回num

WITH t AS (
    SELECT cust_name, unnest(ARRAY[ROW(1, fruit1), ROW(2, fruit2), ROW(3, fruit3)]::num_fruit[]) as nf FROM customer_fruits
) SELECT cust_name, 'fruit' || num(nf) as 'fruit' FROM t WHERE fruit(nf) = 'apples';

cust_name | fruit
------------------
cust_a    | fruit1
cust_c    | fruit3
cust_d    | fruit1
cust_d    | fruit3

或者:

WITH t AS (
    SELECT cust_name, unnest(ARRAY[ROW(1, fruit1), ROW(2, fruit2), ROW(3, fruit3)]::num_fruit[]) as nf FROM customer_fruits
) SELECT cust_name, array_agg('fruit' || num(nf)) as 'fruits' FROM t WHERE fruit(nf) = 'apples' GROUP BY 1;

cust_name | fruits
------------------
cust_c    | {fruit3}
cust_a    | {fruit1}
cust_d    | {fruit1, fruit3}

您甚至可以规范化您的表(PK =客户名称+数字):

WITH t AS (
    SELECT cust_name, unnest(ARRAY[ROW(1, fruit1), ROW(2, fruit2), ROW(3, fruit3)]::num_fruit[]) as nf FROM the_table
) SELECT cust_name, num(nf), fruit(nf) FROM t WHERE fruit(nf) IS NOT NULL;