我正在尝试清理具有非常杂乱的varchar列的表,其中包含以下各项:
<u><font color="#0000FF"><a href="http://virginialidar.com/index-3.html#.VgLbFPm6e73" target="_blank">VA Lidar</a></font></u> OR <u><font color="#0000FF"><a href="https://inport.nmfs.noaa.gov/inport/item/50122" target="_blank">InPort Metadata</a></font></u>
我想通过仅保留html链接来更新该列,如果有多个,则用逗号分隔。理想情况下,我会这样做:
UPDATE mytable
SET column = array_to_string(regexp_matches(column,'(?<=href=").+?(?=\")','g') , ',');
但是不幸的是,这在Postgres 10中返回了一个错误:
ERROR: set-returning functions are not allowed in UPDATE
我假设regexp_matches()
是所说的集合返回函数。关于如何实现此目标的任何想法?
答案 0 :(得分:3)
1。
您不需要将相关子查询建立在基表的单独实例上(就像到目前为止提出的两个答案一样)。那将无所事事地做更多的工作。
2。
在简单情况下, ARRAY构造函数比array_agg()
便宜。参见:
3。
我使用不带lookahead and lookbehind constraints的正则表达式,而使用括号:href="([^"]+)
请参见 查询1 。
之所以可行,是因为带括号的子表达式是由regexp_matches()
(和其他几个Postgres regexp函数)捕获的。因此,我们可以用普通括号代替更复杂的约束。 The manual on regexp_match()
:
如果找到匹配项,并且
pattern
不包含括号 子表达式,则结果是一个单元素文本数组 包含与整个模式匹配的子字符串。如果匹配 找到,并且*pattern*
包含带括号的子表达式,然后 结果是一个文本数组,其第n
个元素是匹配的子字符串 模式的第n
个带括号的子表达式
如果没有匹配项,则此函数不返回任何行;如果不存在,则不返回任何行 是一个匹配项,并且不给出g标志;如果存在N,则
N
行 匹配并给出g标志。返回的每一行都是一个文本数组 包含整个匹配的子字符串或匹配的子字符串 括号内的子表达式,如上所述 为regexp_match
。
4。
regexp_matches()
返回一组数组( setof text[]
)是有原因的:正则表达式不仅可以在单个字符串中匹配多次(因此, set ),它还可以为每个匹配项生成多个字符串,并带有多个捕获括号(因此, array )。 this 正则表达式不会发生,结果中的每个数组都包含一个元素。但是将来的读者不会陷入陷阱:
当将生成的一维数组馈送到产生二维数组的array_agg()
(或ARRAY构造函数)时,这甚至是可行的,因为Postgres 9.5添加了array_agg()
接受数组的变体输入。参见:
输入必须全部具有相同的维数,并且不能为空或NULL
我认为这永远不会失败,因为同一个正则表达式总是产生相同数量的数组元素。我们总是产生 one 元素。但这可能与其他正则表达式不同。如果是这样,则有多种选择:
仅使用(regexp_matches(...))[1]
作为第一个元素。请参见 查询2 。
取消嵌套数组,并在基础元素上使用string_agg()
。请参见 查询3 。
每种方法都可以在这里使用。
UPDATE tbl t
SET col = (
SELECT array_to_string(ARRAY(SELECT regexp_matches(col, 'href="([^"]+)', 'g')), ',')
);
不匹配的列设置为''
(空字符串)。
UPDATE tbl
SET col = (
SELECT string_agg(t.arr[1], ',')
FROM regexp_matches(col, 'href="([^"]+)', 'g') t(arr)
);
不匹配的列设置为NULL
。
UPDATE tbl
SET col = (
SELECT string_agg(elem, ',')
FROM regexp_matches(col, 'href="([^"]+)', 'g') t(arr)
, unnest(t.arr) elem
);
不匹配的列设置为NULL
。
db <>小提琴here (带有扩展测试用例)
答案 1 :(得分:1)
您可以使用相关子查询来处理有问题的集合返回函数(即regexp_matches
)。像这样:
update mytable
set column = (
select array_to_string(array_agg(x), ',')
from (
select regexp_matches(t2.c, '(?<=href=").+?(?=\")', 'g')
from t t2
where t2.id = t.id
) dt(x)
)
您仍然对“ CSV in a column”一栏感到困惑,但这是一个单独的问题,可能对您来说不是问题。
答案 2 :(得分:0)
在mu方法的基础上构建的正则表达式太短,并且使用COALESCE函数来保留不包含href链接的值:
UPDATE a
SET bad_data = COALESCE(
(SELECT Array_to_string(Array_agg(x), ',')
FROM (SELECT Regexp_matches(a.bad_data,
'(?<=href=")[^"]+', 'g'
) AS x
FROM a a2
WHERE a2.id = a.id) AS sub), bad_data
);