在PSQL中将字符变化列导入到字符变化数组列

时间:2015-12-10 04:29:22

标签: arrays postgresql

使用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;。另一个问题是我可以想象以后添加一个设备会进行循环替换,这会阻止此方法形式起作用。

那么,有没有更好的方法来处理这种情况?

2 个答案:

答案 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;

假设密钥只是一个字符。
这将返回一个纯字符串,没有转义。你可以用任何一种方式......

SQL Fiddle.

密切相关: