我有一个表,其中列出了客户在商店中由客户购买的所有水果:
| 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
答案 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;