【问题标题】:Multiple DISTINCT ON clauses in PostgreSQLPostgreSQL 中的多个 DISTINCT ON 子句
【发布时间】:2017-02-28 09:58:40
【问题描述】:

是否可以选择 DISTINCT ON 一些单独的、独立的列集的行?

假设我想要所有符合以下条件的行:

  • 区别于(name, birth)
  • 区别于(name, height)

因此,在下表中,标有红叉的行不会是不同的(带有失败子句的指示):

name      birth    height
--------------------------
William    1976      1.82
James      1981      1.68
Mike       1976      1.68
Tom        1967      1.79
William    1976      1.74   ❌ (name, birth)
William    1981      1.82   ❌ (name, height)
Tom        1978      1.92
Mike       1963      1.68   ❌ (name, height)
Tom        1971      1.86
James      1981      1.77   ❌ (name, birth)
Tom        1971      1.89   ❌ (name, birth)

在上面的示例中,如果 DISTINCT ON 子句刚刚是 DISTINCT ON (name, birth, height),那么所有行都将被视为不同的。

试过了还是不行:

  • SELECT DISTINCT ON (name, birth) (name, height) ...
  • SELECT DISTINCT ON (name, birth), (name, height) ...
  • SELECT DISTINCT ON ((name, birth), (name, height)) ...
  • SELECT DISTINCT ON (name, birth) AND (name, height) ...
  • SELECT DISTINCT ON (name, birth) AND ON (name, height) ...
  • SELECT DISTINCT ON (name, birth) DISTINCT ON (name, height) ...
  • SELECT DISTINCT ON (name, birth), DISTINCT ON (name, height) ...

【问题讨论】:

  • Postgres 允许在表达式上使用DISTINCT ON 。也许你想要SELECT DISTINCT ON (name||birth, name||height) 之类的东西?
  • @Eugene 非常好,似乎可以解决问题并且没有明显的性能损失。你为什么不把它变成一个答案?
  • @Jivan 你有订购标准吗?如果没有,您可能会得到随机行,例如您可能会得到William | 1976 | 1.82 或者可能是William | 1981| 1.82
  • @OtoShavadze 精度不错——我猜这个简单的技巧不适用于排序标准
  • 您的问题的定义是不确定性。结果可能包括William 1976 1.82William 1976 1.74 William 1981 1.82 两者都符合您的标准。您需要更仔细地定义目标。 (例如,通过明确定义从每组欺骗中选择哪一行。)并且总是提供您的 Postgres 版本。

标签: sql postgresql duplicates distinct-on


【解决方案1】:

As commented,这个问题有歧义。每次调用的结果行数可能不同。如果您对任意结果感到满意,@klin's solution 就足够了。否则,您需要更紧密地定义需求。喜欢:
区别(name, birth),先选择最小的高度,然后选择最小的ID作为决胜局

或者:
(name, height) 上不同,先选择最早的出生,然后选择最小的 ID 作为决胜局

您的表应该有一个主键(或一些唯一标识行的方法):

CREATE TEMP TABLE tbl (
  tbl_id serial PRIMARY KEY
, name text
, birth int
, height numeric);

INSERT INTO tbl (name, birth, height)
VALUES
  ('William', 1976, 1.82)
, ('James',   1981, 1.68)
, ('Mike',    1976, 1.68)
, ('Tom',     1967, 1.79)
, ('William', 1976, 1.74)
, ('William', 1981, 1.82)
, ('Tom',     1978, 1.92)
, ('Mike',    1963, 1.68)
, ('Tom',     1971, 1.86)
, ('James',   1981, 1.77)
, ('Tom',     1971, 1.89);

查询:

SELECT DISTINCT ON (name, height) *
FROM  (
   SELECT DISTINCT ON (name, birth) *
   FROM   tbl
   ORDER  BY name, birth, height, tbl_id  -- pick smallest height, ID as tiebreaker
   ) sub
ORDER  BY name, height, birth, tbl_id;    -- pick earliest birth, ID as tiebreaker
 tbl_id |  name   | birth | height
--------+---------+-------+--------
      2 | James   |  1981 |   1.68
      8 | Mike    |  1963 |   1.68
      4 | Tom     |  1967 |   1.79
      9 | Tom     |  1971 |   1.86
      7 | Tom     |  1978 |   1.92
      5 | William |  1976 |   1.74
      6 | William |  1981 |   1.82
(7 rows)    -- !!!

没有确定性ORDER BYDISTINCT ON 查询可以从每组欺骗中返回任意行。应用一次,您仍然可以获得确定的行数(任意选择)。重复应用,得到的行数也是任意的。相关:

【讨论】:

    【解决方案2】:

    使用派生表:

    with my_table(name, birth, height) as (
    values
    ('William',    1976,      1.82),
    ('James',      1981,      1.68),
    ('Mike',       1976,      1.68),
    ('Tom',        1967,      1.79),
    ('William',    1976,      1.74),  -- ? (name, birth)
    ('William',    1981,      1.82),  -- ? (name, height)
    ('Tom',        1978,      1.92),
    ('Mike',       1963,      1.68),  -- ? (name, height)
    ('Tom',        1971,      1.86),
    ('James',      1981,      1.77),  -- ? (name, birth)
    ('Tom',        1971,      1.89)   -- ? (name, birth)
    )
    select distinct on (name, height) *
    from (
        select distinct on (name, birth) *
        from my_table
        ) s
    
      name   | birth | height 
    ---------+-------+--------
     James   |  1981 |   1.68
     Mike    |  1963 |   1.68
     Tom     |  1967 |   1.79
     Tom     |  1971 |   1.89
     Tom     |  1978 |   1.92
     William |  1976 |   1.82
    (6 rows)        
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-05
      • 2016-06-01
      • 2017-11-18
      • 2020-11-05
      • 2012-04-05
      • 2019-01-08
      • 2020-01-06
      • 2015-08-23
      相关资源
      最近更新 更多