错误:OR的参数不能返回一组

时间:2018-04-25 23:22:22

标签: sql regex postgresql non-ascii-characters

在这里运行PostgreSQL 9.2.8 ......

我一直在尝试获取表格中包含非ASCII字符的所有行的列表 - 超出<space>~范围内的任何行,以及反引号字符`。如果任何行包含任何无效字符,则将显示包含所有地址值的行。但由于某种原因,我收到以下错误:

ERROR:  argument of OR must not return a set (10586)
LINE 9: (CAST(regexp_matches(a.address_line_1,'([^ !-~]|`)') AS VARCHAR)...
         ^

********** Error **********

ERROR: argument of OR must not return a set (10586)
SQL state: 42804
Character: 252

我一直试图使用的查询如下:

select a.address_id, a.address_line_1, 
    a.address_line_2, 
    a.address_line_3, 
regexp_matches(a.address_line_1,'([^ !-~]|`)'),
regexp_matches(a.address_line_2,'([^ !-~]|`)'),
regexp_matches(a.address_line_3,'([^ !-~]|`)')
    FROM public.address a 
WHERE 
(CAST(regexp_matches(a.address_line_1,'([^ !-~]|`)') AS VARCHAR) <> '') OR
(CAST(regexp_matches(a.address_line_2,'([^ !-~]|`)') AS VARCHAR) <> '') OR
(CAST(regexp_matches(a.address_line_3,'([^ !-~]|`)') AS VARCHAR) <> '')
LIMIT 1000

我不确定我可能会遗漏什么,因为这似乎是一个有效的查询。

我正在尝试获取三个地址字段中任何一个中存在无效字符的行,而不仅仅是三个地址字段中的无效字符。

2 个答案:

答案 0 :(得分:1)

regexp_matches()返回SETOF text,无法像您尝试的那样使用(正如错误消息所示)。您可以使用regular expression operator ~代替。

但是你的正则表达式似乎没有涵盖你所描述的内容:

  

地址中的非ASCII字符

此外,括号表达式!-~中的范围[^ !-~]取决于您的COLLATION设置。 The manual warns:

  

范围非常依赖于整理顺序,因此便携式程序应该避免依赖它们。

考虑:

SELECT g, chr(g), chr(g) ~ '([^ !-~]|`)'
FROM   generate_series (1,300) g;  -- ASCII range plus some

假设服务器编码UTF8,找到3列中包含任何非ASCII字符的行:

...
WHERE octet_length(concat(a.address_line_1, a.address_line_2, a.address_line_3))
         <> length(concat(a.address_line_1, a.address_line_2, a.address_line_3))

这是有效的,因为所有非ASCII字符在UTF8中编码的字节数超过1个,因此octet_length()报告的数字高于length()(别名:char_length())。与concat()的连接可以防止可能的NULL值。

要测试反引号,请添加:

...
OR  concat(a.address_line_1, a.address_line_2, a.address_line_3) LIKE '%`%'

答案 1 :(得分:0)

一种方法使用exists

where exists (select 1 from regexp_matches(a.address_line_1, '[^ !-~]')) or
      exists (select 1 from regexp_matches(a.address_line_2, '[^ !-~]')) or
      exists (select 1 from regexp_matches(a.address_line_3, '[^ !-~]')) 

或者更简单:

where a.address_line_1 ~ '[^ !-~]' or
      a.address_line_2 ~ '[^ !-~]' or
      a.address_line_3 ~ '[^ !-~]'