Sql 仅选择“最完整”记录

Sql 仅选择“最完整”记录,sql,postgresql,Sql,Postgresql,我需要解决以下问题 假设我有一个表,有4个字段,叫做a,b,c,d 我有以下记录: ------------------------------------- a | b | c | d ------------------------------------- 1 | 2 | | row 1 1 | 2 | 3 | 4 row 2

我需要解决以下问题

假设我有一个表,有4个字段,叫做a,b,c,d

我有以下记录:

-------------------------------------
   a   |    b    |    c    |    d  
-------------------------------------
   1   |    2    |         |             row 1 
   1   |    2    |    3    |    4        row 2 
   1   |    2    |         |    4        row 3
   1   |    2    |    3    |             row 4  
可以观察到,第1、3、4行是第2行的子记录

我想做的是,只提取第二行

你能帮我吗

提前谢谢你的回答

编辑:我需要更具体一些

我还可以将这些案例:

-------------------------------------
   a   |    b    |    c    |    d  
-------------------------------------
   1   |    2    |         |             row 1 
   1   |    2    |         |    4        row 2 
   1   |         |         |    4        row 3
在这里我需要提取第二行

-------------------------------------
   a   |    b    |    c    |    d  
-------------------------------------
   1   |    2    |         |             row 1 
   1   |    2    |    3    |             row 2 
   1   |         |    3    |             row 3
我需要再次提取第二行

夫妻也一样

其他例子也是如此


当然,现在总是第二行

根据您的定义,最完整的行是空列最少的行:

SELECT * FROM tablename
WHERE (
    (CASE WHEN a IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN b IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN c IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN d IS NULL THEN 0 ELSE 1 END)
) =
(SELECT MAX(
    (CASE WHEN a IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN b IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN c IS NULL THEN 0 ELSE 1 END) + 
    (CASE WHEN d IS NULL THEN 0 ELSE 1 END)) 
FROM tablename)

嗯。我想你可以用不存在的:

逻辑是不存在值匹配的更具体的行


是一个dbfiddle。

您需要为每一行计算一个完成索引。在您提供的示例中,您可以使用以下内容:

   (CASE WHEN a IS NULL THEN 0 ELSE 1) +
   (CASE WHEN b IS NULL THEN 0 ELSE 1) +
   (CASE WHEN c IS NULL THEN 0 ELSE 1) +
   (CASE WHEN d IS NULL THEN 0 ELSE 1) AS CompletionIndex
然后选择按CompletionIndex按降序排序的前1个


显然,这在大量列中的可伸缩性不是很强。但是,如果有大量稀疏的列,则可以考虑基于行而不是基于列的结构。这种设计将使计算每个实体的非空值的数量变得更加容易。

正如Gordon Linoff所提到的,我们也必须使用类似not exists的东西

使用“帮助”之外的内容进行编辑

这可能有用

SELECT * from table1 
EXCEPT
(
SELECT t1.*
FROM table1 t1
JOIN table1 t2
ON  COALESCE(t1.a, t2.a, -1) = COALESCE(t2.a, -1)
AND COALESCE(t1.b, t2.b, -1) = COALESCE(t2.b, -1)
AND COALESCE(t1.c, t2.c, -1) = COALESCE(t2.c, -1)
AND COALESCE(t1.d, t2.d, -1) = COALESCE(t2.d, -1)
)
这里,t1是每个子集行


注意:我们假设值-1为sentinel值,它不会出现在任何列中。

使用not EXISTS,可以筛选出具有更好副本的记录


dbfiddle

如果有多行具有相同的子记录,该怎么办?不应该考虑这种情况,但是我仍然只需要最完整的记录,或者更简单地说,因为您正在检查所有列:WHERE NOT tablename在哪里NULL@eurotrash这是什么?这是一条有效的语句吗?@forpas Yes,假设表名为tablename,通过说nottablename为NULL,它将检查整行,并且只有当所有字段均为NULL时,该语句才会返回TRUE。因此,这比指定每个字段都要短。@eurotrash您有关于它的文档吗?@forpas请参见,特别是如果表达式是行值的,那么当行表达式本身为NULL或所有行的字段为NULL时,则为NULL为真谢谢您的回答!我对我的问题进行了更具体的编辑,因为这只是一种可以happen@M.Orlandi . . . 问题中的额外澄清帮助我完善了逻辑。这正是我想要的!我只在a、b、c、d上添加了一个select distinct,而不是select*,它工作得非常好!感谢you@M.Orlandi代表的Thx。不过只是一个警告。这不会过滤掉元组4、null、6、null、4、5、null、null、null、4、6、null等特殊情况。但是这种方法的优点是,您可以通过简单地删除NOT来选择lesses DUP。例如,如果要删除不太完整的DUP。
   (CASE WHEN a IS NULL THEN 0 ELSE 1) +
   (CASE WHEN b IS NULL THEN 0 ELSE 1) +
   (CASE WHEN c IS NULL THEN 0 ELSE 1) +
   (CASE WHEN d IS NULL THEN 0 ELSE 1) AS CompletionIndex
SELECT * from table1 
EXCEPT
(
SELECT t1.*
FROM table1 t1
JOIN table1 t2
ON  COALESCE(t1.a, t2.a, -1) = COALESCE(t2.a, -1)
AND COALESCE(t1.b, t2.b, -1) = COALESCE(t2.b, -1)
AND COALESCE(t1.c, t2.c, -1) = COALESCE(t2.c, -1)
AND COALESCE(t1.d, t2.d, -1) = COALESCE(t2.d, -1)
)
create table abcd (
 a int,
 b int,
 c int,
 d int
);
insert into abcd (a, b, c, d) values
 (1, 2, null, null)
,(1, 2, 3, 4)
,(1, 2, null, 4)
,(1, 2, 3, null)

,(2, 3, null,null)
,(2, 3, null, 5)
,(2, null, null, 5) 

,(3, null, null, null)
,(3, null, 5, null)
,(null, null, 5, null)
SELECT *  
FROM abcd AS t 
WHERE NOT EXISTS 
(
   select 1
   from abcd as d  
   where (t.a is null or d.a = t.a)
     and (t.b is null or d.b = t.b)
     and (t.c is null or d.c = t.c)
     and (t.d is null or d.d = t.d)
     and (case when t.a is null then 0 else 1 end +
          case when t.b is null then 0 else 1 end +
          case when t.c is null then 0 else 1 end +
          case when t.d is null then 0 else 1 end) < 
         (case when d.a is null then 0 else 1 end +
          case when d.b is null then 0 else 1 end +
          case when d.c is null then 0 else 1 end +
          case when d.d is null then 0 else 1 end)
);
a | b | c | d -: | ---: | ---: | ---: 1 | 2 | 3 | 4 2 | 3 | null | 5 3 | null | 5 | null