使用PostgreSQL 9.4。
我有一个名为&#39; Appliances&#39;的数据栏。 &#39;电器&#39;是类型字符变化的,并且充满了遵循这种模式的值:&#39; A | B | E | H&#39;或者&C; D | E&#39;等我还有一个文本文件,解释了定义大写字母含义的映射,即A =洗碗机,B =炉灶,C =微波... < / p>
我需要将此数据转换为适合新的字符变化数组列,以使值变为:&#39; {洗碗机,炉灶,风扇,电视}&#39;
我尝试的第一件事就是将一大堆替换呼叫叠加在一起并与&#39; {&#39;和&#39;}&#39;:
select
'{' ||
replace(replace(replace(
replace(replace(replace(
replace(replace(replace(
replace('A|B|C|D|E|F|G|J|I', '|', ','),
'G', 'Refrigerator'),
'D', 'Garbage Disposal'),
'A', 'Dishwasher') ,
'B', 'Double Oven'),
'C', 'Dryer'),
'E', 'Microwave'),
'F', 'Range/Oven'),
'I', 'Trash Compactor'),
'J', 'Washer')
|| '}'
这不仅看起来很粗糙,而且当你切换冰箱和垃圾处理时它会中断。因为&#39; G&#39;在垃圾处理&#39;。另一个问题是我可以想象以后添加一个设备会进行循环替换,这会阻止此方法形式起作用。
那么,有没有更好的方法来处理这种情况?
答案 0 :(得分:1)
你可以试试这个。
create table appliances (
code text primary key,
dscr text not null);
insert into appliances (code, dscr)
values ('G', 'Refrigerator'),
('D', 'Garbage Disposal'),
('A', 'Dishwasher') ,
('B', 'Double Oven'),
('C', 'Dryer'),
('E', 'Microwave'),
('F', 'Range/Oven'),
('I', 'Trash Compactor'),
('J', 'Washer');
select array_agg(appliances.dscr)
from regexp_split_to_table('A|E|G', '\|')
join appliances on code=regexp_split_to_table;
结果:
{Dishwasher,Microwave,Refrigerator}
如果您希望将未知代码显示为NULL,则可以离开连接,例如
select array_agg(appliances.dscr)
from regexp_split_to_table('A|E|G|?', '\|')
left join appliances on code=regexp_split_to_table;
结果:
{Dishwasher,Microwave,Refrigerator,NULL}
老实说,如果你将'A | E | G'字符串拆分成各自的代码并且每个条目都有一行,那么从长远来看,你可能会更好。
答案 1 :(得分:1)
与@Bill already hinted类似,执行此操作的正确方法是设备和房屋之间的多对多关系(或任何持有您的设备集合)的规范化架构。您可以使用三个表来实现它:
house
appliance
house_appliance
详细说明:
虽然坚持使用您当前的架构,但仍有多种解决方案可供选择 - 具体取决于您的Postgres版本以及 精确 对您的定义你有什么需要。
以此架构为基础:
CREATE TABLE appliance (
appliance_id "char" PRIMARY KEY
, appliance text NOT NULL
);
INSERT INTO appliance VALUES
('G', 'Refrigerator')
, ('D', 'Garbage Disposal')
, ('A', 'Dishwasher')
, ('B', 'Double Oven')
, ('C', 'Dryer')
, ('E', 'Microwave')
, ('F', 'Range/Oven')
, ('I', 'Trash Compactor')
, ('J', 'Washer')
;
CREATE TABLE house (
house_id serial PRIMARY KEY
, appliances text
);
INSERT INTO house(appliances) VALUES
('A|B|C|D|E|F|G|J|I')
, ('G|A|F')
, ('B|Z|A') -- special case: invalid reference
, ('B|F|') -- special case: empty after separator
, ('') -- special case: empty string
, (NULL) -- special case: NULL
;
(其中很多。)
要返回实际的数组 - 所以文本表示会自动包含在'{}'
中,并且会转义任何特殊字符。
对于Postgres 9.4 + :
SELECT *
FROM house h
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT a.appliance
FROM unnest(string_to_array(h.appliances, '|'))
WITH ORDINALITY ha(appliance_id, ord)
LEFT JOIN appliance a USING (appliance_id)
ORDER BY ha.ord
) AS appl_arr
) a ON TRUE;
Postgres 9.4引入了 WITH ORDINALITY
。详细说明:
对于Postgres 9.3 :
SELECT *
FROM (SELECT house_id, string_to_array(appliances, '|') AS arr FROM house) h
LEFT JOIN LATERAL (
SELECT ARRAY (
SELECT a.appliance
FROM generate_subscripts(h.arr, 1) i
LEFT JOIN appliance a ON a.appliance_id = arr[i]
ORDER BY i
) AS appl_arr
) a ON TRUE;
LATERAL
要求Postgres 9.3
这两个版本都包含无效或缺少键的结果中的NULL值。将内部LEFT JOIN
替换为JOIN
以忽略无效或缺失的键。由于外部LEFT JOIN
,结果仍包含所有行。
对于Postgres 9.2或更早:
SELECT *
FROM house h
LEFT JOIN LATERAL (
SELECT '{' || string_agg(appliance, ', ') || '}' AS appl_string
FROM (
SELECT a.appliance
FROM generate_series(1, (length (h.appliances) + 1)/ 2) i
LEFT JOIN appliance a ON a.appliance_id = split_part(h.appliances, '|', i)
ORDER BY i
) sub
) a ON TRUE;
假设密钥只是一个字符。
这将返回一个纯字符串,没有转义。你可以用任何一种方式......
密切相关: