【问题标题】:Postgres - only return results of query if two sub-queries produce resultsPostgres - 如果两个子查询产生结果,则仅返回查询结果
【发布时间】:2019-06-19 13:25:11
【问题描述】:

这是一个名为fruits的表:

id  fruit_name  quantity   date
1 | 'apple'    | 10       | '2019-01-01'
2 | 'peach'    | 15       | '2019-01-02'
3 | 'plum'     | 5        | '2019-01-05'
4 | 'banana'   | 10       | '2019-01-06'
5 | 'orange'   | 20       | '2019-01-06'
6 | 'grape'    | 25       | '2019-01-09'

我正在尝试从两个子查询创建一个查询。只有both两个子查询都返回任何结果时,主查询才应返回结果。因此,如果两个子查询中的任何一个都没有任何结果,那么整个查询应该什么都不返回。

以下是我处理过的查询。它有点像我想要创造的东西:

WITH r AS (
    SELECT *
    FROM fruits
    WHERE fruit_name = ('pear')
)
SELECT *
FROM r
UNION ALL
SELECT *
FROM fruits
WHERE fruit_name = ('banana')
  AND EXISTS (
    SELECT *
    FROM r
  );

所以这个查询不会返回任何结果,因为第一个子查询要求一个'pear',而fruits 表中没有'pear'。这按预期工作。

但是,如果我在查询中交换“梨”和“香蕉”,那么它将返回一行:“香蕉”行。这不是我想要发生的——在这种情况下,我也希望它返回 nothing。

所以基本上,我怎样才能修改这个查询(或使用更好的查询),以便它只返回结果如果两个子查询中的每一个每个都有结果?

SQL 小提琴:http://www.sqlfiddle.com/#!15/7ecb1/5

【问题讨论】:

  • 我不明白你想用这个实现什么。但是,您在第二个示例中得到了“香蕉”输出,因为您使用了 SELECT * FROM r UNION ALL ...!这每次都会为您提供来自公用表表达式(您的第一个子查询)的所有结果。所以就不要管它了!?

标签: sql postgresql


【解决方案1】:

在 where 子句中使用两个检查:

select *
from fruits
where fruit_name in ('banana', 'pear')
and exists (
    select from fruits
    where fruit_name = 'banana')
and exists (
    select from fruits
    where fruit_name = 'pear');

请注意,在 Postgres 中,选择列表可能为空。您可以选择一列或一个常量

...
and exists (
    select id from fruits
    where fruit_name = 'banana')
...

但这不影响结果。

或者,您可以检查结果是否包含带有banana 和pear 的行:

with results as (
    select *
    from fruits
    where fruit_name in ('banana', 'pear')
)
select *
from results
where (
    select count(distinct fruit_name)
    from results
    ) = 2

【讨论】:

  • 虽然我同意在这种情况下这比我的更有效和简洁,但我不确定这是否是我们想要的。查找在一张表上的事实是我理解的最小可重复示例的结果。
【解决方案2】:

您可以使用单个子查询来表达:

select f.*
from fruits f
where f.fruit_name in ('banana', 'pear') and
      exists (select 1
              from fruits f2
              where f2.fruit_name in ('banana', 'pear') and
                    f2.fruit_name <> f.fruit_name
             );

但是,假设名称是唯一的,我倾向于这样做:

select f.*
from (select f.*, count(*) over () as cnt
      from fruits f
      where f.fruit_name in ('banana', 'pear')
     ) f
where cnt = 2;

如果您确实有重复,则可以轻松调整此逻辑,但您的样本数据没有任何重复。

【讨论】:

    【解决方案3】:

    您可以使用两个子查询而不是一个

    WITH sub1 AS (
        SELECT *
        FROM fruits
        WHERE fruit_name = ('pear')
    ),
    sub2 AS (
        SELECT *
        FROM fruits
        WHERE fruit_name = ('banana')
    )
    SELECT *
    FROM sub1
    UNION ALL
    SELECT *
    FROM sub2
    WHERE
    EXISTS (
        SELECT *
        FROM sub1
      )
    AND EXISTS (
        SELECT *
        FROM sub2
      );
    
    

    【讨论】:

    • 这被否决的任何原因,问题明确提到它有两个子查询,only return results of query if two sub-queries produce results
    • "但是,如果我在查询中交换 'pear' 和 'banana',那么它将返回一行:'banana' 行。这不是我想要发生的——在这种情况下,我也希望它不返回任何东西。”这正是您的示例中发生的情况!
    • 是的,这正是它的行为方式。 xD
    猜你喜欢
    • 1970-01-01
    • 2020-02-03
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多