PostgreSQL:用于确定不同行的自定义逻辑?

时间:2010-08-29 01:27:53

标签: postgresql distinct

这是我的问题。假设我有一个名为persons的表,其中包含人名和国家识别号的字段,后者是可选的。每个真人都可以有多行。

现在假设我想为每个真人选择一行。出于应用的目的,如果a)它们的ID号匹配,或者b)它们的名称匹配并且一个或两个的ID号为NULL,则认为两行指向同一个人。 SELECT DISTINCT在这里不是很好:我不能做DISTINCT ON (name, id)因为那么两个具有相同名称的行(其中一个的ID是NULL)将不匹配(这是不正确的,它们应该被认为是相同的)。我无法执行DISTINCT ON (name),因为具有相同名称但不同ID的行将匹配(再次不正确,它们应被视为不同)。我不能做DISTINCT ON (id),因为ID为NULL的所有行都被认为是相同的(显然不正确)。

有没有办法重新定义PostgreSQL比较行的方式,以确定它们是否相同?我想DISTINCT ON (name, id)的默认行为类似于IF a.name = b.name AND a.id = b.id THEN IDENTICAL ELSE DISTINCT。我想将它重新定义为IF a.id = b.id OR (a.name = b.name AND (a.id IS NULL OR b.id IS NULL)) THEN IDENTICAL ELSE DISTINCT

现在已经很晚了,我可能已经错过了一些明显的东西,所以关于如何达到我想要的东西的其他建议也会受到欢迎。能让我根据比简单的列列表更复杂的标准选择不同行的任何东西。提前谢谢。

2 个答案:

答案 0 :(得分:1)

使用窗口函数

--
-- First, SELECT those names with NULL national IDs not shadowed by the same
-- name with a national ID.  Each one is a unique person.
--
SELECT name, id
FROM   persons
WHERE  NOT EXISTS (SELECT 1
                     FROM persons p
                    WHERE p.name = persons.name AND p.id IS NOT NULL)
--
-- Second, collapse each national ID into the "first" row with that ID,
-- whatever the name.  Each ID is a unique person.
--
UNION ALL
SELECT name, id
  FROM (SELECT name, id, ROW_NUMBER() OVER (PARTITION BY id)
          FROM persons
         WHERE id IS NOT NULL) d
 WHERE d.row_number = 1;

没有窗口函数

将上述UNION替换为GROUP BY每个非空ID的第一个(MIN())名称:

...
UNION ALL
  SELECT MIN(name) AS name, id
    FROM persons
   WHERE id IS NOT NULL
GROUP BY id

答案 1 :(得分:0)

似乎主要问题是数据库的布局。我不知道您的具体应用程序的详细信息,但同一个人拥有多行和空ID通常是一个坏主意。如果可能,您可能需要考虑为需要多行的任何信息创建单独的表,persons每行只包含一行,每行包含唯一标识符。

但是,如果你不能这样做......我不认为只是一个明显的解决这个问题。

有什么问题:

select distinct name, id
from persons
where id is not null

您是否有一些人有姓名,但没有身份证?或者您是否需要来自其他行的某些特定数据?

这是另一个问题:如果有两个具有相同名称和空ID的行,以及多个具有相同名称和不同ID的人,您如何知道空行匹配哪个人?