使用 PostgreSQL 9.4。
我有一个名为“设备”的数据列。 'Appliances' 的类型是字符变化的,并且充满了遵循这种模式的值:'A|B|E|H' 或 'C|D|E' 等。我还有一个文本文件来解释定义的映射大写字母的含义,即 A=Dishwasher,B=Stove,C=Microwave...
我需要将此数据转换为适合新的字符可变数组列,以便值变为:'{Dishwasher, Stove, Fan, Television}'
我尝试的第一件事是将一大堆替换调用堆叠在一起并用“{”和“}”连接起来:
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')
|| '}'
这不仅看起来很恶心,而且当你切换冰箱和垃圾处理器时它也会坏掉。因为“垃圾处理”中的“G”。另一个问题是我可以想象稍后添加一个设备,它会进行循环替换,从而完全阻止这种方法的工作。
那么,有没有更好的方法来处理这种情况?
最佳答案
赞@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;
WITH ORDINALITY
是在 Postgres 9.4 中引入的。详情:
对于 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;
假设键是独占的单个字符。
这将返回一个纯字符串,没有转义。你可以选择任何一种方式......
密切相关:
关于arrays - 将字符变化列导入 PSQL 中的字符变化数组列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34193699/