POSTGRES:如何仅在另一个值不存在时选择具有某个值的行,在这种情况下选择另一个值?

Posted

技术标签:

【中文标题】POSTGRES:如何仅在另一个值不存在时选择具有某个值的行,在这种情况下选择另一个值?【英文标题】:POSTGRES: How to select rows with a certain value only if another value doesn't exist, and in that case select the other value? 【发布时间】:2022-01-24 03:48:33 【问题描述】:

这是我正在使用的表的示例。我想要实现的是选择类型不是“NONE”的最新行,除非“NONE”是该id唯一可用的类型。

id date type
123 01-01-2021 NONE
123 12-31-2021 NONE
123 01-01-2021 METAL
123 12-31-2021 METAL

从上面的示例表中,我希望查询返回这个

id date type
123 12-31-2021 METAL

如果表格只包含“NONE”类型,例如这个例子......

id date type
123 01-01-2021 NONE
123 12-31-2021 NONE
123 01-01-2021 NONE
123 12-31-2021 NONE

那么我希望结果集是..

id date type
123 12-31-2021 NONE

我已经尝试了很多不同的方法来做到这一点,但我目前的尝试看起来像这样。它在表中只有一个 ID 时有效,但不适用于我尝试为表中的每个特定 ID 选择一行时。


SELECT DISTINCT ON (id),
       date,
       type
FROM 
    example_table
WHERE
    CASE
        WHEN 
            (   SELECT 
                    COUNT(*) 
                FROM 
                    example_table t 
                WHERE 
                    t.type <> 'NONE'
                AND t.id = example_table.id) 
            <> 0
        THEN type <> 'NONE'
        ELSE 1=1
    END
ORDER BY 
    id, date DESC

 

【问题讨论】:

【参考方案1】:

您可以将 Row_number() 函数与 case 语句一起使用来确定要选择的行。

with cte AS
(
  select id,
       date, 
       type,
       row_number() over(partition by id 
                          order by case when type <> 'NONE' THEN 1 ELSE 2 END, date desc
                          ) as RN
       
  from test
 )
 select *
 from cte
 where rn = 1

SQL Fiddle

【讨论】:

谢谢我喜欢这个。如果您能帮助我理解 case 语句的工作原理,特别是 order by 子句中的 THEN 1 ELSE 2 部分,我将不胜感激。按 1 订购 2 订购?这是在做什么? 使用case语句,你基本上是为类型分配权重,然后使用权重对它们进行排序。 您正在为“无”以外的值分配较小的数字,以便在您订购它们时它们会排在第一位。 啊,谢谢。我从不知道您可以使用 order by 并分配这样的权重。【参考方案2】:

不使用分析函数: SQL:

with cte
as
(
 select id,type,flag, max(date) date from (select id , date , type, case when type='NONE' then 'flag_none' else 'flag_not_none' end as flag from test) x group by  id,type,flag)
select id,type,date from cte where flag='flag_not_none'  and  (id,date) in (select id,max(date) from cte group by id)
union
select id,type,date from cte where id not in (select id from cte where flag='flag_not_none'  and  (id,date) in (select id,max(date) from cte group by id)) and flag='flag_none';

完全执行:

CREATE TABLE test 
(

  id int,
  date date,
  type text
);

insert into test
values
(123,  '01-01-2021', 'NONE'),
(123, '12-31-2021', 'NONE'),
(123, '01-01-2021', 'METAL'),
(123, '12-31-2021', 'PLASTIC'),
(124,  '01-01-2021', 'NONE'),
(124, '12-31-2021', 'NONE'),
(124, '01-01-2021', 'NONE'),
(124, '12-31-2021', 'NONE'),
(125, '12-25-2021', 'NONE'),
(125, '12-25-2021', 'RUBBER'),
(125, '12-31-2021', 'STEEL');

postgres=# with cte
postgres-# as
postgres-# (
postgres(#  select id,type,flag, max(date) date from (select id , date , type, case when type='NONE' then 'flag_none' else 'flag_not_none' end as flag from test) x group by  id,type,flag)
postgres-# select id,type,date from cte where flag='flag_not_none'  and  (id,date) in (select id,max(date) from cte group by id)
postgres-# union
postgres-# select id,type,date from cte where id not in (select id from cte where flag='flag_not_none'  and  (id,date) in (select id,max(date) from cte group by id)) and flag='flag_none';
 id  |  type   |    date
-----+---------+------------
 123 | PLASTIC | 2021-12-31
 124 | NONE    | 2021-12-31
 125 | STEEL   | 2021-12-31
(3 rows)

【讨论】:

以上是关于POSTGRES:如何仅在另一个值不存在时选择具有某个值的行,在这种情况下选择另一个值?的主要内容,如果未能解决你的问题,请参考以下文章

大查询 - 仅在列值不存在时插入

postgres仅在不存在时创建用户[重复]

Sql Server:如何仅在存在时才选择列?

如何使用窗口函数仅在 POSTGRES 中选择不超过某个值的行

XQuery sql 仅在存在时选择节点

基本布尔逻辑——如何仅在另一个条件为真时测试条件