【问题标题】:PostgreSQL multiple tuples selection in WHERE clauseWHERE子句中的PostgreSQL多元组选择
【发布时间】:2015-03-31 09:13:57
【问题描述】:

我有这张桌子:

create table myTable (keyword text, category text, result text
                    , primary key (keyword,category));

insert into myTable values
    ('foo',       'A', '10'),
    ('bar',       'A', '200'),
    ('baz',       'A', '10'),
    ('Superman',  'B', '200'),
    ('Yoda',      'B', '10'),
    ('foo',       'C', '10');

我想根据元组(keyword,category)检索结果。所以基本上,通过一个简单的元组,我有以下查询:

SELECT result FROM myTable WHERE keyword LIKE '%a%' AND category = 'A';
-- returns 10,200 as expected

但我可以拥有任意数量的元组。将此查询扩展到几个元组会返回错误的结果:

SELECT result FROM myTable
    WHERE ( keyword LIKE '%a%' AND category = 'A')
    AND   ( keyword LIKE '%Superman%' AND category = 'B');
-- expected 200; but returned no rows...

SELECT distinct result FROM myTable
    WHERE ( keyword LIKE '%a%' AND category = 'A')
    OR   ( NOT(keyword LIKE '%Superman%') AND category = 'B');
-- expected 10; but returned 10,200...

这很合乎逻辑,因为 PostgreSQL 不遵循运算符顺序和括号。

只有OR 子句有效。如果我只有 OR 子句,我会使用这样的:

SELECT result FROM myTable
    INNER JOIN (VALUES
      ('foo','C'),
      ('Superman', 'B')
    ) t(keyword,category) USING (keyword,category); -- 10,200 as expected

但它仅适用于OR 和严格相等。在我的情况下,我想使用 LIKE 相等,并且我想在不同的元组之间使用 ANDORAND NOTOR NOT

更准确地说,当我写作时:

SELECT result FROM myTable
    WHERE ( keyword LIKE '%a%' AND category = 'A')
    AND   ( keyword LIKE '%Superman%' AND category = 'B');
 -- expected 200; but returned no row

我的意思是我想要两个子句获得的结果的 INTERSECTION。 第一个元组返回 10,200,第二个返回 200。在这种情况下,我只想返回 200。

像这样在 cmets 中使用 OR 作为建议:

SELECT distinct result FROM myTable
    WHERE ( keyword LIKE '%a%' AND category = 'A')
    OR   ( keyword LIKE '%Superman%' AND category = 'B');

返回 10,200,但这不是我想要的 ...

【问题讨论】:

  • 可能是代码错误?也许您的意思是:SELECT result FROM myTable WHERE ( keyword LIKE '%a%' AND category = 'A') OR ( keyword LIKE '%Superman%' AND category = 'B');。如果您有 A 类 AND B 类,那么结果显然什么都没有。
  • 至于keyword LIKE '%a%' AND category = 'A' => 按预期返回 bar 和 baz 而 NOT(keyword LIKE '%Superman%') AND category = 'B' => 按预期返回 yoda
  • @SimoKivistö 对于第一个错误查询,我不想要 OR,因为我不想获得第一个元组的每个结果加上第二个元组的每个结果。事实上,这是我需要的一个 INTERSECTION。
  • @pidupuis for 预期 200;但没有返回任何行 > 试试this

标签: sql postgresql where multiple-columns relational-division


【解决方案1】:

您似乎正在寻找的东西称为关系划分。该任务可以表述为:

查找至少有一行符合这些条件的结果:
keyword LIKE '%a%' AND category = 'A'
并且至少有一行匹配这些其他条件:
keyword LIKE '%Superman%' AND category = 'B'

返回DISTINCT结果的条件的快速解决方案:

SELECT DISTINCT result
FROM   tbl t1
JOIN   tbl t2 USING (result)
WHERE  t1.keyword LIKE '%a%' AND t1.category = 'A'
AND    t2.keyword LIKE '%Superman%' AND t2.category = 'B';

但由于您的过滤器可以为每个结果返回多行,因此其中一个会更快

SELECT result
FROM  (
     SELECT DISTINCT result
     FROM   tbl
     WHERE  keyword LIKE '%a%' AND category = 'A'
     ) t1
JOIN  (
     SELECT DISTINCT result
     FROM   tbl
     WHERE  keyword LIKE '%Superman%' AND category = 'B'
     ) t2 USING (result);

或者:

SELECT result
FROM  (
     SELECT DISTINCT result
     FROM   tbl
     WHERE  keyword LIKE '%a%' AND category = 'A'
     ) t
WHERE  EXISTS (
     SELECT 1
     FROM   tbl
     WHERE  result = t.result
     AND    keyword LIKE '%Superman%' AND category = 'B'
     );

SQL 小提琴。

我们在这个相关问题下组装了一个查询技术库:

【讨论】:

  • 使用EXISTS 的解决方案似乎更好,因为它允许我使用NOT。您如何建议我将它与更多交替使用 ANDOR 的元组一起使用? (我可以结合几个EXISTSUNION 没有无限深度的嵌入式选择吗?)
  • @pidupuis:你已经知道如何实现OR,将EXISTS/NOT EXISTS添加到AND/AND NOT的WHERE子句中……
【解决方案2】:

你快到了:

SELECT distinct result FROM myTable
WHERE ( keyword LIKE '%a%' AND category = 'A')
OR   (keyword LIKE '%Superman%' AND category = 'B'); 

它的作用是:如果关键字类似于 '%a%' 且类别 = 'A' 或关键字类似于 '%supaerman%' 且类别 = 'B',则返回行

您的查询执行了以下操作

SELECT result FROM myTable
WHERE ( keyword LIKE '%a%' AND category = 'A')
AND   ( keyword LIKE '%Superman%' AND category = 'B'); -- expected 200;  but returned no rows

要在此处返回一行(除其他事项外,该行中的类别必须为“A”和“B”。由于不能同时为两者,因此未返回任何行。

SELECT distinct result FROM myTable
WHERE ( keyword LIKE '%a%' AND category = 'A')
OR   ( NOT(keyword LIKE '%Superman%') AND category = 'B'); -- expected 10; but returned 10,200..

在这种情况下,NOT(...) 使查询返回类别等于“B”的所有行,其中关键字不包含“超人”(当然还有他之前条件的结果)。 ;)

【讨论】:

  • 谢谢,但这不是 OR。 AND我的意思是INTERSECTION。查看我的编辑。
【解决方案3】:

我想你也可以看看文档SIMILAR TO

你可以这样做

SELECT * from myTable where keyword SIMILAR TO '%(oo|ba)%' and category SIMILAR TO '(A)';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    相关资源
    最近更新 更多