【问题标题】:Removing SQL Duplicates via SELECT通过 SELECT 删除 SQL 重复项
【发布时间】:2014-08-05 12:14:38
【问题描述】:

我还有一个快速的 SQL 问题。 考虑下表:

Value1   |   Value2   |   Value3   
------------------------------------
Peter    |   Blue     |    Red
Peter    |   Null     |    Null
Martin   |   Blue     |    Null
Martin   |   Null     |    Null
Boris    |   Null     |    Null
Sergej   |   Null     |    Green
Sergej   |   Null     |    Null

你看这是一个特殊的案例。有些情况下,Value2 和 Value3 都设置在一个条目中,有些情况下设置了 Value2 和 Value3,有些情况下没有设置(没有重复)。

所以问题是:如何删除重复项(通过 Value1)并获取信息最多的条目? IE。如果设置了 Value2 和/或 Value3,则使用这些条目。显然,我不能只使用“非空”,因为有些情况(开始时不重复的情况)我需要涵盖两者都在 Null 上的情况。

第二个问题是我只有读取权限,所以它必须发生在 Select 语句中。非常感谢。

【问题讨论】:

  • 您使用的是什么 RDMS?
  • 该样本中根本没有duplicates。但抛开语义不谈,使用的是什么 dbms 类型(供应商)?例如,您的 dbms 平台是否支持 dense_rank() 或 row_number()?
  • 你期望结果是什么,就像user2067753说没有重复记录。
  • 感谢您的 cmets。抱歉,我可能选错词了。当然,它们不是完全重复的。考虑与上面相同的表,但在 Value1 和 Value2 之间还有 10 个列。认为它们对于具有相同名称的每个条目都是相同的。因此,除了最后 2 列(Value2 和 Value3)之外,条目是“重复的”。我对上表的预期输出:Peter-Blue-Red Martin-Blue-Null Boris-Null-Null Sergej-Null-Green 我使用 MS-SQL。

标签: sql


【解决方案1】:

对 MS SQL 来说可以,这样就可以了

DECLARE @T TABLE (V1 VARCHAR(50), V2 VARCHAR(50), V3 VARCHAR(50))
INSERT INTO @T VALUES ('Peter', 'Blue', 'Red'), ('Peter', Null, Null), ('Martin', 'Blue',Null), 
    ('Martin', Null, Null), ('Boris', Null, Null), ('Sergej', Null, 'Green'), ('Sergej', Null, Null)
SElECT V1, V2, V3 
FROM (SELECT V1, V2, V3, ROW_NUMBER () 
          OVER (PARTITION BY V1 ORDER BY CASE WHEN V2 IS NULL THEN 1 ELSE 0 END 
                    + CASE WHEN V3 IS NULL THEN 1 ELSE 0 END) as Quality 
      FROM @T) as T
WHERE Quality = 1

结果

V1       V2      V3  
Boris   NULL    NULL  
Martin  Blue    NULL  
Peter   Blue    Red  
Sergej  NULL    Green  

编辑:注意:这将为每个名称提供一个条目,即使有多个行包含相同数量的信息。也就是说,如果 Peter 在 V2 和 V3 中有 2 行都具有非空值,则系统将随机选择一个。

如果您希望所有行的信息量最大,可以将 ROW_NUMBER 替换为 RANK。

这也认为 V2 和 V3 具有相同的权重,因此只有 V2 NULL 的行和只有 V3 NULL 的行是相等的。您可以通过更改 CASE 语句为 2 个字段返回不同的值来更改该行为。即对于两者都使 NULL 值 3 和 NON-NULL 一个值 0 另一个值 1,所以 NON-NULL NON-NULL 是 Q=1,NULL NON-NULL 是 Q = 3,NON-NULL NULL 是 Q= 4,NULL NULL是Q=6。

编辑 2:在上面的解释中错误地有两次 NULL NULL :-(

编辑 3:cmets 中要求的扩展解释
好没问题。 “ROW_NUMBER”(和 RANK)函数通常会为您的数据集生成一系列数字。要知道订单应该基于什么,你必须告诉它。所以这两个函数都需要一个“OVER (ORDER BY Col1[,Col2 ...])”子句。 OVER() 中的 ORDER BY 就像查询末尾的 ORDER BY 子句一样工作。

在这种情况下,我没有在您的数据中使用真正的列,我正在派生一个列(这是匿名的,为了清楚起见,我想给它一个别名,但 SQL-Server 2008 R2 没有支持)。作为参考,我们将派生列称为 Q,即使 SQL 不允许我们实际命名它。我的派生列是 2 个 CASE 语句的总和,因此它是一个整数值,表示该行中 NULL 的数量。由于 ORDER BY 指令默认为 ASCENDING 顺序,因此数据最多(NULL 最少)的行将具有最低的“Q”并排序到顶部。

我给整个 ROW_NUMBER 函数输出一个别名 - “质量”。它与我上面描述的“Q”不一样,但它与之相关。质量将是一个整数序列,从 1 开始,每行增加 1,而 Q 将是 0、1 或 2,具体取决于行中有多少 NULL。最低 Q 行将获得最低质量数字,但对于具有相同 Q 值的行,SQL 将随机排序。

解决方案的最后一点是 PARTITION BY 子句,它告诉 ROW_NUMBER(或 RANK)函数将数据分成集合(就像查询中的 GROUP BY 子句一样,它也可以占用 1 列或更多列) 并在每个组中重新开始编号。这样,Peter 获得了自己的 1,2,3,4,... 质量值,Martin 获得了自己的 1,2,3,4,... 值,等等。

因此,当我将“WHERE Quality = 1”子句放在查询末尾时,我是说“对于每个人,选择具有最少 NULL 的行”

我希望这是你问的问题,我不确定我是否理解你的“如果我只是按 1 排序”位。

【讨论】:

  • 非常感谢您的解决方案。你能向我解释一下(PARTITION BY V1 ORDER BY CASE WHEN V2 IS NULL THEN 1 ELSE 0 END + CASE WHEN V3 IS NULL THEN 1 ELSE 0 END)到底是做什么的?如果我只是按 1 排序,我会得到一个错误。这可能是一个非常微不足道的误解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 2015-09-01
  • 2018-04-26
  • 2021-10-25
相关资源
最近更新 更多