【问题标题】:Select rows where elements of two arrays don't match a pattern选择两个数组的元素与模式不匹配的行
【发布时间】:2020-06-01 09:16:55
【问题描述】:

我尝试编写一个查询,在该查询中,我选择基于 LIKE 模式的行,该模式必须与数组的任何条目都不匹配。我有的是现在的

SELECT * FROM mytable WHERE 
NOT (EXISTS (SELECT * from unnest(a) WHERE  unnest LIKE '%XYZ%' ))) 

其实我想要的是pairs——所以更像

SELECT * FROM mytable WHERE 
NOT (
  (EXISTS (select * from unnest(a) as A, unnest(b) as B  WHERE  A||B LIKE '%xyz%abc%' )) OR
  (EXISTS (select * from unnest(a) as A, unnest(b) as B  WHERE  A||B LIKE '%abc%xyz%' ))
)

这对我有用,但看起来相当笨拙。有没有更漂亮/更有效的解决方案?我发现最讨厌的是 SELECT 作为 WHERE 子句的一部分来展开数组。

几点:

  • 在我的情况下,xyzabc 不会作为子字符串一起出现在单个数组条目中(例如,没有像 xyzblaabcexists 这样的东西)
  • 两个数组的元素数量完全相同(它们实际上是从另一个查询派生的)
  • 没有空元素(但即使这样仍然可以工作,因为我没有一对abcxyz

编辑:

澄清:不应返回包含 A 为 {rxyz,foo, bar} 和 B 为 {other, abc, this} 的行,因为它在一个数组中包含 rxyz,在另一个数组中包含 abc

例子:

SELECT * FROM(
 SELECT *
    FROM   (select 1 as ID, '{rxyz,foo,bar}'::varchar[] as a, '{abc,other,this}'::varchar[] as b) row1  UNION
 SELECT *
    FROM   (select 2 as ID, '{rxyz,foo,bar}'::varchar[] as a, '{other,rabc,this}'::varchar[] as b) row1  UNION
 SELECT *
    FROM   (select 3 as ID, '{else,foo,bar}'::varchar[] as a, '{abc,other,this}'::varchar[] as b) row2

    ) mytable
WHERE   
NOT (
  (EXISTS (select * from unnest(a) as A, unnest(b) as B  WHERE  A||'-|-'||B LIKE '%xyz%-|-%abc%' OR B||'-|-'||A LIKE '%xyz%-|-%abc%' )))

仅返回第 3 行。在我的用例中,我可以保证 -|- 不是这两个列表的一部分,因此可以将它们分开。

【问题讨论】:

  • *“这行得通……” - 是吗?见答案。
  • 那么在this fiddle 中,只有 1-3 个符合条件?
  • @ErwinBrandstetter 在那一个中​​,除了 4 之外的所有内容都应该返回。
  • 对不起,但这并没有加起来。您应用于小提琴的查询返回不同的集合:dbfiddle here 请提供您想要实现的准确定义.
  • @ErwinBrandstetter 正如提到的abcxyz,在我的情况下,它们不会属于同一个字符串。在您的示例中,它们位于 7 和 8 中-对不起,我错过了。所以 4、7 和 8 被“正确”过滤掉了。这些数组实际上是 array_agg() 对其他数据的结果。所以我知道它们的大小总是一样的。

标签: sql arrays postgresql unnest


【解决方案1】:

您可以将两者取消嵌套到一个派生表中:

select *
from mytable
where not exists (select *
                  from unnest(a,b) as x(a,b)
                  where x.a like '%xyz%'
                     or x.b like '%xyz%'
                     or x.a like '%abc%'
                     or x.b like '%abc%')

【讨论】:

  • 谢谢 - 我不知道unest(a,b)
【解决方案2】:

更新后的任务描述为:

删除文本数组 a 的任何元素包含 ('abc', 'xyz') 之一且文本数组 b 的任何元素包含另一个的行。

您的第二个查询实现了这一目标,但代价高昂。 unnest(a) as A, unnest(b) 将数组 a 的每个元素与数组 b 的每个元素组合在一起 - 笛卡尔积 O(N²) 会在数组越长时性能迅速下降。

尝试以下方法之一:

使用CROSS JOIN 对串联字符串进行测试:

SELECT t.*
FROM   tbl t, array_to_string(a, '|') AS a_string, array_to_string(b, '|') AS b_string
WHERE (a_string LIKE '%abc%' AND b_string LIKE '%xyz%'
   OR  a_string LIKE '%xyz%' AND b_string LIKE '%abc%') IS NOT TRUE;

确保选择不会产生错误匹配的分隔符。示例中为'|'

使用NOT EXISTS 对串联字符串进行测试:

SELECT *
FROM   tbl t
WHERE  NOT EXISTS (
   SELECT FROM array_to_string(a, '|') a_string
             , array_to_string(b, '|') b_string
   WHERE  a_string LIKE '%abc%' AND b_string LIKE '%xyz%'
      OR  a_string LIKE '%xyz%' AND b_string LIKE '%abc%'
   );

使用LATERAL 子查询测试未嵌套的元素;

SELECT t.*
FROM   tbl t
JOIN   LATERAL (
   SELECT bool_or(a_elem LIKE '%abc%') AND bool_or(b_elem LIKE '%xyz%') 
       OR bool_or(a_elem LIKE '%xyz%') AND bool_or(b_elem LIKE '%abc%') AS exclude
   FROM   unnest(t.a,t.b) e(a_elem, b_elem)
   ) x ON exclude IS NOT TRUE;

使用NOT EXISTS测试未嵌套的元素

SELECT *
FROM   tbl t
WHERE  NOT EXISTS (
   SELECT FROM unnest(t.a,t.b) AS e(a_elem, b_elem)
   HAVING bool_or(a_elem LIKE '%abc%') AND bool_or(b_elem LIKE '%xyz%') 
       OR bool_or(a_elem LIKE '%xyz%') AND bool_or(b_elem LIKE '%abc%')
   );

db小提琴here

最后一个是我最喜欢的。 N不确定哪一个表现最好。所有这些都应该比您现在的执行速度快得多。

关于unnest(a,b)

关于你的观点的一些 cmets

在我的情况下,xyzabc 不会作为子字符串一起出现在单个数组条目中(例如,没有像 xyzblaabc 这样的东西)。

这是一个很大的假设。这可能有充分的理由,但你确定没有极端案例,现在没有,永远也没有?否则,您的代码稍后会静默中断,并且很难确定原因。

两个数组的元素数量完全相同(它们实际上是从另一个查询派生的)

最有效的查询通常是在源中应用过滤器,在其他查询中 - 如果可能的话。甚至可以使用适当的索引。比如,一个三元索引:

没有空元素(但即使这样仍然可以工作,因为那时我没有一对abcxyz

很公平。但是,如果有 一些,请注意 NULL 值做你想做的事情。

【讨论】:

  • 我对我想要的内容进行了一些澄清 - 抱歉
  • @Lutz:我写了一个新的答案来解决这个问题。
  • 实际上我希望的是用更紧凑和可读的东西来替换整个NOT EXISTS ( SELECT FROM unnest WHERE )(比如x like any (array of conditions)的倒数)。我想我必须坚持下去。至于在源头申请 - 在这里不起作用。
  • 有点像not ((any a like '%xyz%' and any b like '%abc%') or (any b like '%xyz%' and any a like '%abc%')) 但我能想到的只是not exists in sub-querry
  • @Lutz:请在stackoverflow.com/questions/60262472/…回复我的请求。我的答案顶部的假设是否正确?当问题不完全清楚时,很难讨论细节。
猜你喜欢
  • 2017-07-09
  • 1970-01-01
  • 2014-06-12
  • 2020-10-03
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多