PostgreSQL数据库中DISTINCT关键字的四种用法详解

 更新时间:2024年04月18日 10:01:12   作者:不剪发的Tony老师  
PostgreSQL 不但高度兼容 SQL 标准,同时还对很多语法进行了扩展,可以用于实现一些特殊的功能,今天我们就来介绍一下 PostgreSQL 数据库中 DISTINCT 关键字的 4 种不同用法,需要的朋友可以参考下

前言

PostgreSQL 不但高度兼容 SQL 标准,同时还对很多语法进行了扩展,可以用于实现一些特殊的功能。今天我们就来介绍一下 PostgreSQL 数据库中 DISTINCT 关键字的 4 种不同用法。

DISTINCT

按照 SQL 标准,SELECT DISTINCT可以在返回查询结果之前去除重复的记录,每个重复的数据组中只保留一条记录。例如:

SELECT DISTINCT dept_id, sex
FROM employee;

dept_id|sex|
-------|---|
      4|男  |
      1|男  |
      4|女  |
      5|男  |
      3|女  |
      2|男  |

以上语句中的 DISTINCT 表示返回不同部门 id 和性别的组合值。我们也可以使用 GROUP BY 实现相同的结果:

SELECT dept_id, sex
FROM employee
GROUP BY dept_id, sex;

按照 SQL 标准,多个 NULL 值对于 DISTINCT 而言属于相同的分组。

DISTINCT ON

考虑一个问题:每个部门中月薪最高的员工都是谁?这个问题可以使用多种实现方法:

-- 子查询
SELECT dept_id, emp_name,salary
FROM employee
WHERE (dept_id, salary) IN ( SELECT dept_id, MAX(salary)
                             FROM employee
                             GROUP BY dept_id );

dept_id|emp_name|salary  |
-------|--------|--------|
      1|刘备     |30000.00|
      2|诸葛亮    |24000.00|
      3|孙尚香    |12000.00|
      4|赵云     |15000.00|
      5|法正     |10000.00|

-- 窗口函数
WITH ranked_employee AS (
  SELECT dept_id, emp_name, salary, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) rn
  FROM employee
)
SELECT *
FROM ranked_employee
WHERE rn = 1;

dept_id|emp_name|salary  |rn|
-------|--------|--------|--|
      1|刘备     |30000.00| 1|
      2|诸葛亮    |24000.00| 1|
      3|孙尚香    |12000.00| 1|
      4|赵云     |15000.00| 1|
      5|法正     |10000.00| 1|

其中,第一个语句使用了子查询;第二个语句使用了窗口函数,除了 ROW_NUMBER 之外,也可以使用 RANK 或者 DENSE_RANK 等函数。这两者都是 SQL 标准实现。

除此之外,PostgreSQL 提供了扩展的 DISTINCT ON 子句,可以更加方便地实现以上结果:

SELECT DISTINCT ON (dept_id) dept_id, emp_name, salary
FROM employee
ORDER BY dept_id, salary DESC;

dept_id|emp_name|salary  |rn|
-------|--------|--------|--|
      1|刘备     |30000.00| 1|
      2|诸葛亮    |24000.00| 1|
      3|孙尚香    |12000.00| 1|
      4|赵云     |15000.00| 1|
      5|法正     |10000.00| 1|

其中,DISTINCT ON (dept_id) 表示部门 id 相同的数据组,返回其中的第一条记录;ORDER BY 子句确保了返回的是每个部分中月薪最高的记录。DISTINCT ON 中的字段或表达式(可能多个)必须和 ORDER BY 最左侧的几个字段或表达式相同。

IS DISTINCT FROM

空值(NULL)是数据库中的一个特殊值,通常用于表示缺失值或者不适用的值。空值的比较是一个比较容易出错的问题。例如:

WITH t AS ( 
  SELECT 1 AS a, 1 AS b
  UNION ALL 
  SELECT 1, 2
  UNION ALL 
  SELECT NULL, 1
  UNION ALL 
  SELECT NULL, NULL )
SELECT a, b, a = b "a=b"
FROM t;

a   |b   |a=b  |
----|----|-----|
1   |1   |true |
1   |2   |false|
NULL|1   |NULL |
NULL|NULL|NULL |

当我们使用比较运算符(=、<>、<、> 等)与 NULL 进行比较时,结果既不是真也不是假,而是未知;因为 NULL 表示未知,也就意味着可能是任何值;我们不能说两个未知的值相同,也不能说它们不相同。

为了比较 NULL 值,SQL 定义了两个专用的运算符:IS NULL和IS NOT NULL。例如:

SELECT 1 IS NULL "1 IS NULL",
       1 IS NOT NULL "1 IS NOT NULL",
       NULL IS NULL "NULL IS NULL",
       NULL IS NOT NULL "NULL IS NOT NULL";

1 IS NULL|1 IS NOT NULL|NULL IS NULL|NULL IS NOT NULL|
---------|-------------|------------|----------------|
false    |true         |true        |false           |

因此,对于两个可能为空的字段进行比较的完整方法如下:

WITH t AS ( 
  SELECT 1 AS a, 1 AS b
  UNION ALL 
  SELECT 1, 2
  UNION ALL 
  SELECT NULL, 1
  UNION ALL 
  SELECT NULL, NULL )
SELECT a, b,
       (a IS NULL AND b IS NULL)
       OR 
       (a IS NOT NULL AND b IS NOT NULL AND a = b) "a=b"
FROM t;

a   |b   |a=b  |
----|----|-----|
1   |1   |true |
1   |2   |false|
NULL|1   |false|
NULL|NULL|true |

以上语句返回了我们期望的结果,但是读写都很不方便;为此,PostgreSQL 提供了扩展的 IS [NOT] DISTINCT FROM 运算符,支持 NULL 值的比较。例如:

WITH t AS ( 
  SELECT 1 AS a, 1 AS b
  UNION ALL 
  SELECT 1, 2
  UNION ALL 
  SELECT NULL, 1
  UNION ALL 
  SELECT NULL, NULL )
SELECT a, b, a IS NOT DISTINCT FROM b "a=b"
FROM t;

a   |b   |a=b  |
----|----|-----|
1   |1   |true |
1   |2   |false|
NULL|1   |false|
NULL|NULL|true |

注意,IS NOT DISTINCT FROM 表示判断两个数据是否相同,IS DISTINCT FROM 表示判断两个数据是否不同;它们都将 NULL 看作已知的一个特殊值,而不是 SQL 标准中的未知值。显然这种语法更加言简意赅。

另外,PostgreSQL 还提供了一个配置变量 transform_null_equals,该参数默认为 off;如果设置为 on,PostgreSQL 会自动执行 convert x = NULL 到 x IS NULL 的转换。建议不要依赖这个参数的设置,而是应该修改应用程序

聚合函数与 DISTINCT

聚合函数(aggregate function)针对一组数据行进行运算,并且返回一条结果。PostgreSQL 支持的聚合函数包括 avg、COUNT、MAX/MIN、SUM、STRING_AGG、ARRAY_AGG 等。例如:

SELECT dept_id, count(*), avg(salary), string_agg(emp_name, ',' ORDER BY salary DESC)
FROM employee
GROUP BY dept_id
ORDER BY dept_id;

dept_id|count|avg                   |string_agg                            |
-------|-----|----------------------|--------------------------------------|
      1|    3|    26666.666666666667|刘备,关羽,张飞                          |
      2|    3|13166.6666666666666667|诸葛亮,黄忠,魏延                        |
      3|    2| 9000.0000000000000000|孙尚香,孙丫鬟                           |
      4|    9| 7577.7777777777777778|赵云,周仓,关兴,关平,赵氏,廖化,张苞,赵统,马岱|
      5|    8| 5012.5000000000000000|法正,简雍,孙乾,糜竺,黄权,庞统,邓芝,蒋琬    |

以上语句返回了每个部门的员工人数、平均月薪以及所有员工姓名的连接字符串(按照月薪从高到低)。

PostgreSQL 不仅实现了分组聚合操作,还支持聚合函数中的 DISTINCT 选项,可以在进行汇总之前去除每个分组中的重复记录。例如:

SELECT dept_id, string_agg(sex, ','), string_agg(DISTINCT sex, ',') string_agg_distinct
FROM employee
GROUP BY dept_id
ORDER BY dept_id;

dept_id|string_agg             |string_agg_distinct|
-------|-----------------------|-------------------|
      1|男,男,男                |男                 |
      2|男,男,男                |男                 |
      3|女,女                   |女                 |
      4|男,女,男,男,男,男,男,男,男|女,男               |
      5|男,男,男,男,男,男,男,男   |男                 |

到此这篇关于PostgreSQL数据库中DISTINCT关键字的四种用法详解的文章就介绍到这了,更多相关PostgreSQL DISTINCT用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • PgSQl临时表创建及应用实例解析

    PgSQl临时表创建及应用实例解析

    这篇文章主要介绍了PgSQl临时表创建及应用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • PostgreSQL教程(七):函数和操作符详解(3)

    PostgreSQL教程(七):函数和操作符详解(3)

    这篇文章主要介绍了PostgreSQL教程(七):函数和操作符详解(3),本文讲解了序列操作函数、条件表达式、数组函数和操作符、系统信息函数、系统管理函数等内容,需要的朋友可以参考下
    2015-05-05
  • PostgreSQL表操作之表的创建及表基础语法总结

    PostgreSQL表操作之表的创建及表基础语法总结

    在PostgreSQL中创建表命令用于在任何给定的数据库中创建新表,下面这篇文章主要给大家介绍了关于PostgreSQL表操作之表的创建及表基础语法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05
  • PostgreSQL中ON CONFLICT的使用及一些扩展用法

    PostgreSQL中ON CONFLICT的使用及一些扩展用法

    Postgres ON CONFLICT是PostgreSQL数据库中的一个功能,用于处理插入或更新数据时的冲突情况,下面这篇文章主要给大家介绍了关于PostgreSQL中ON CONFLICT的使用及一些扩展用法的相关资料,需要的朋友可以参考下
    2024-06-06
  • Debian中PostgreSQL数据库安装配置实例

    Debian中PostgreSQL数据库安装配置实例

    这篇文章主要介绍了Debian中PostgreSQL数据库安装配置实例,一个简明教程,需要的朋友可以参考下
    2014-06-06
  • postgresql insert into select无法使用并行查询的解决

    postgresql insert into select无法使用并行查询的解决

    这篇文章主要介绍了postgresql insert into select无法使用并行查询的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Navicat设置PostgreSQL数据库的表主键ID自增的方法

    Navicat设置PostgreSQL数据库的表主键ID自增的方法

    这篇文章主要介绍了Navicat设置PostgreSQL数据库的表主键ID自增的方法,文章通过图文结合的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-06-06
  • 使用PostgreSQL为表或视图创建备注的操作

    使用PostgreSQL为表或视图创建备注的操作

    这篇文章主要介绍了使用PostgreSQL为表或视图创建备注的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL慢SQL的定位排查方法

    PostgreSQL慢SQL的定位排查方法

    所谓慢SQL 是指在数据库中执行时间超过指定阈值的语句,慢查询太多,对于业务而言,是有很大风险的,可能随时都会因为某种原因而被触发,本篇文章将介绍 PostgreSQL 慢 SQL 如何定位排查,需要的朋友可以参考下
    2024-07-07
  • PostgreSQL+GeoHash地图点位聚合实现代码

    PostgreSQL+GeoHash地图点位聚合实现代码

    这篇文章主要介绍了PostgreSQL+GeoHash地图点位聚合,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07

最新评论