Sql 选择两个数组的元素与模式不匹配的行
我尝试编写一个查询,其中我基于一个相似的模式选择行,该模式必须与数组的任何条目都不匹配。我所拥有的是现在的Sql 选择两个数组的元素与模式不匹配的行,sql,arrays,postgresql,unnest,Sql,Arrays,Postgresql,Unnest,我尝试编写一个查询,其中我基于一个相似的模式选择行,该模式必须与数组的任何条目都不匹配。我所拥有的是现在的 SELECT * FROM mytable WHERE NOT (EXISTS (SELECT * from unnest(a) WHERE unnest LIKE '%XYZ%' ))) 实际上,我想要的是成对的,所以它更像 SELECT * FROM mytable WHERE NOT ( (EXISTS (select * from unnest(a) as A, un
SELECT * FROM mytable WHERE
NOT (EXISTS (SELECT * from unnest(a) WHERE unnest LIKE '%XYZ%' )))
实际上,我想要的是成对的,所以它更像
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%' ))
)
这对我来说很有效,但看起来相当笨拙。是否有更漂亮/更有效的解决方案?我发现最令人讨厌的是WHERE子句中用于展开数组的SELECT as部分
有几点:
在我的情况下,xyz和abc不会作为子字符串一起出现在单个数组条目中,例如,与xyzblaabcexists不同
两个数组的元素数完全相同,它们实际上是从另一个查询派生的
没有空元素,但即使它仍然可以工作,因为我没有一对abc和xyz
编辑:
澄清:一行包含a as{rxyz,foo,bar}和B as{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行。在我的用例中,我可以保证-|-不属于这两个列表的一部分,因此可以将它们分开。您可以将这两个列表都取消到一个派生表中:
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%')
更新后的任务描述为: 消除文本数组a的任何元素包含“abc”、“xyz”和文本数组b的任何元素包含另一个元素的行 您的第二个查询实现了这一点,但代价很高。unnesta作为A,unnestb将数组A的每个元素与数组b的每个元素组合在一起-笛卡尔积在²上会使较长数组的性能迅速恶化 请尝试以下方法之一: 针对具有交叉联接的连接字符串进行测试: 确保选择的分隔符不能产生错误匹配。“|”在这个例子中 针对不存在的连接字符串进行测试: 使用横向子查询测试未列出的元素
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;
测试不存在的未列出元素
小提琴
最后一个是我最喜欢的。我不知道哪一个表现最好。所有这些都应该比你现在的表现快很多
关于unnesta,b:
对你的观点有一些评论
在我的情况下,xyz和abc不会作为子字符串一起出现在单个数组条目中,例如,与xyzblaabcexists不同
这是一个很大的假设。这可能有很好的理由,但你确定没有角落案件,现在没有,永远没有?否则,您的代码将在稍后悄无声息地中断,并且很难确定原因
两个数组的元素数完全相同,它们实际上是从另一个查询派生的
最有效的查询通常是在源位置应用过滤器,如果可能的话,在另一个查询中应用过滤器。甚至可以使用适当的索引。例如,三元索引:
没有空元素,但即使它仍然可以工作,因为我没有一对abc和xyz
很公平。但是,如果有空值,请注意空值会做您想做的事情。*这是有效的…-还是这样?请看答案。所以,在中,只有1-3个符合条件?@ErwinBrandstetter在中,除4之外的所有内容都应返回。抱歉,这不符合要求。应用于fiddle的查询返回一个不同的集合:dbfiddle请提供您想要实现的目标的精确定义。@ErwinBrandstetter如我所述,abc和xyz将不属于同一个字符串。在你的例子中,它们出现在7和8中——对不起,我错过了。因此,4、7和8被正确地过滤掉。这些数组实际上是其他数据上的数组_agg的结果。所以我知道它们的尺寸总是一样的。我对我想要的做了一些澄清-sorry@Lutz:我写了一个新的答案来解决这个问题。实际上,我希望的是用一些更简洁易读的东西来取代整个不存在的东西,比如x的倒数,比如任何条件数组。我想我必须坚持下去。至于在源代码处应用-在这里不起作用。因此,类似于“%xyz%”和“%abc%”的任何a和任何b,或者类似“%xyz%”的任何b和类似“%abc%”的任何a,我所能想到的只是子代码中不存在-querry@Lutz当前位置请在中答复我的请求。我答案顶部的假设正确吗?当问题还不完全清楚时,很难讨论细节。谢谢-我不知道unesta,b。
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%'
);
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;
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%')
);