arrays - 将字符变化列导入 PSQL 中的字符变化数组列

标签 arrays postgresql

使用 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;

假设键是独占的单个字符。
这将返回一个纯字符串,没有转义。你可以选择任何一种方式......

SQL Fiddle.

密切相关:

关于arrays - 将字符变化列导入 PSQL 中的字符变化数组列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34193699/

相关文章:

arrays - 将数据从设备复制到主机时出现无效的参数错误

c++ - 将空数组传递给函数,填充数组并获取值

postgresql - 在 Postgres 中对非常大的结果集正确使用游标

postgresql - Ubuntu - 无法停止 postgres.service : Unit postgres. 服务未加载

python - 生成的 Django 查询集有效,但在 Django 中运行失败

c++ - 如何放入 addStaff(const Staff&) 数组

java - 如何对集合中的子字符串进行排序?

java - 从日期减去一小时 SQL

postgresql - 使用不正确的日期值将 csv 导入 Postgres 数据库

java - Random.nextInt() 产生非常相似的数字?