过滤二维数组

时间:2013-06-26 11:46:15

标签: sql postgresql database-design database-normalization

我有这个数组:

1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0,11:0,12:0,13:0,14:0,15:0,16:0
17:0,18:0,19:0,20:0,21:0,22:0,23:0,24:0,25:0,26:0,27:0,28:0,29:0,30:0,31:0,32:0,
49:0,33:0,34:0,35:0,36:0,37:0,38:0,39:0,40:0,41:0,42:0,43:0,44:0,45:0,46:0,47:0,
48:0,50:0,51:0,52:0,53:0,54:0,55:0,56:0,57:0,58:0,59:0,60:0,61:0,62:0,63:9,64:0,
65:0,66:0,67:0,68:0,69:0,70:0,71:0,72:0,73:0,74:0,75:0,76:0,77:0,78:0,79:0,80:0,
81:0,82:0,83:0,84:0,85:0,86:0,87:0,88:0,89:0,90:0,91:0,92:0,93:0,94:0,95:0,96:0,
97:0,98:0,99:0,100:0

我想过滤所有条目*:0,以便我只得到这个结果:

63:9

我想我必须更好地描述它:

我有一个表users,其中包含字段user_skill 在此字段中是一个字符串1:0, 2:0, 3:0, 4:3, 5:8, 6:9, 7:0, 8:0, 9:0,其语法为skill_id:prio,skill_id:prio,skill_id:prio,skill_id:prio,...

现在我想加入users表和skills表,如下所示:

SELECT skill_name
FROM users
inner join skills on skills.skill_id = ANY (string_to_array(regexp_replace(user_skill,':[0-9]*','','g'),',')::int[])
where user_id = 16
order by skill_name

效果不错,但我只想查看用户skill_name所在的prio <> 0

1 个答案:

答案 0 :(得分:1)

正确的解决方案

您可能希望熟悉normalization并将其实现为表usersskills之间的正确n:m关系,其中包含额外的属性prio user_skill表。这是一个完整的食谱:
How to implement a many-to-many relationship in PostgreSQL?

然后您的查询可以非常简单:

SELECT s.skill_name
FROM   user_skill uk
JOIN   skills s USING (skill_id)
WHERE  uk.user_id = 16
AND    uk.prio <> 0
ORDER  BY s.skill_name;

它可以(并且应该)使用索引进行备份,并且比现在的速度快几个数量级。
它需要更多的磁盘空间。

黑暗面的解决方案

虽然被锁定在这种不幸的情况下,你可以帮助自己解决这个问题。但是,这至少假设Postgres版本

SELECT s.skill_name
FROM  (
   SELECT split_part(us_item, ':', 1) AS skill_id
   FROM  (
      SELECT trim(unnest(string_to_array(user_skill, ','))) AS us_item
      FROM   users
      WHERE  user_id = 16      -- enter user_id here
      ) x
   WHERE  split_part(us_item, ':', 2) <> '0'
   ) u
JOIN   skills s USING (skill_id)
ORDER  BY 1;

演示示例:

SELECT split_part(us_item, ':', 1) AS skill_id
FROM  (
   SELECT  trim(unnest(string_to_array(
'1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0,11:0,12:0,13:0,14:0,15:0,16:0,'
'17:0,18:0,19:0,20:0,21:0,22:0,23:0,24:0,25:0,26:0,27:0,28:0,29:0,30:0,31:0,32:0,'
'49:0,33:0,34:0,35:0,36:0,37:0,38:0,39:0,40:0,41:0,42:0,43:0,44:0,45:0,46:0,47:0,'
'48:0,50:0,51:0,52:0,53:0,54:0,55:0,56:0,57:0,58:0,59:0,60:0,61:0,62:0,63:9,64:0,'
'65:0,66:0,67:0,68:0,69:0,70:0,71:0,72:0,73:0,74:0,75:0,76:0,77:0,78:0,79:0,80:0,'
'81:0,82:0,83:0,84:0,85:0,86:0,87:0,88:0,89:0,90:0,91:0,92:0,93:0,94:0,95:0,96:0,'
'97:0,98:0,99:0,100:0', ','))) AS item
   ) x
WHERE  split_part(us_item, ':', 2) <> '0';

trim()处理前导和尾随空格,就像您在示例中所做的那样。但这些可能只是在草率问题上的文物。

我修复了丢失的, 顺便说一下,SQL标准允许像我演示的那样输入字符串文字。很奇怪,但有时很有用。