【问题标题】:Select row with most non-key information for each group为每个组选择具有最多非关键信息的行
【发布时间】:2013-01-05 06:32:27
【问题描述】:

好的,我有一个包含一些垃圾数据且没有唯一标识符列的表。让我举一个我正在使用的表的例子:

     A    |   B   |  C   |        D         |   E  |
  --------------------------------------------------
1.  Fiona | Smith | NULL | 2152 Cherry Lane | CA   |
2.  Fiona | Smith | NULL | NULL             | NULL |
3.  Bill  | NULL  | ACME | 2903 Center Road | WA   |
4.  Bill  | NULL  | ACME | NULL             | NULL |
5.  NULL  | NULL  | ABC  | 2300 Water St    | PA   |
6.  NULL  | NULL  | ABC  | 2300 Water St    | PA   |
7.  NULL  | NULL  | NULL | 3455 B Street    | CO   |

我需要编写一个只抓取不同行的 SELECT 语句。例如,以第 1 行和第 2 行为例。它们显然都指的是同一个人,但它们只是部分重复。在这两个中,我希望我的 SELECT 语句中包含第 1 行,因为它在每列中包含最多的数据。第 3 行和第 4 行也是如此。第 3 行是我想要包含的行。对于第 5 行和第 6 行,选择哪一个并不重要,因为它们都是完全重复的。第 7 行将默认包括在内,因为它是不同的(意味着 A、B 和 C,而不仅仅是 A 和 B)。

这是我尝试过的:

SELECT A, B, C = MAX(D), MAX(E), 
FROM dbo.Data
GROUP BY A, B, C;

这似乎抓住了我想要的唯一行,但是数据以某种方式放置到了错误的列中。

【问题讨论】:

    标签: sql select sql-server-2012


    【解决方案1】:

    这种方法将 D 和 E 视为平等:

    DECLARE @x TABLE
    (
      A VARCHAR(32), 
      B VARCHAR(32), 
      C VARCHAR(32), 
      D VARCHAR(32), 
      E VARCHAR(32)
    );
    
    INSERT @x VALUES
    ('Fiona', 'Smith', NULL,   '2152 Cherry Lane',  'CA'),
    ('Fiona', 'Smith', NULL,   NULL,                NULL),
    ('Bill',  NULL,    'ACME', '2903 Center Road',  'WA'),
    ('Bill',  NULL,    'ACME', NULL,                NULL),
    (NULL  ,  NULL,    'ABC',  '2300 Water St',     'PA'),
    (NULL  ,  NULL,    'ABC',  '2300 Water St',     'PA'),
    (NULL  ,  NULL,    NULL,   '3455 B Street',     'CO'),
    ('Bob',   'Barker',NULL,   NULL,                NULL),
    ('Bob',   'Barker',NULL,   NULL,                'NY');
    
    ;WITH x AS
    (
      SELECT A,B,C,D,E, rn = ROW_NUMBER() OVER 
      (
        PARTITION BY A,B,C
        ORDER BY COALESCE(LEN(LEFT(D,1)),0) + COALESCE(LEN(LEFT(E,1)),0) DESC
      )
      FROM @x
    )
    SELECT A,B,C,D,E
    FROM x WHERE rn = 1;
    

    【讨论】:

    • 嗨亚伦,谢谢你的回答,它成功了!我肯定需要做一些研究来弄清楚这段代码到底在做什么以及为什么。
    • ROW_NUMBER 允许您根据您定义的标准对行(以及相关行的组,称为分区)应用排名。在这种情况下,我知道您认为 A,B,C 是您的密钥,所以这就是我们分区的依​​据。然后是确定如何定义每个组中最重要的行的问题。 COALESCE 表达式确保我们为 D 和 E 获得 0(对于 NULL 或空字符串)或 1(对于填充的值)。最高值获胜。如果你想要一个决胜局,你可以添加,D DESC, E DESC 或相反,具体取决于...
    • ...在一行只有 D 而另一行只有 E 的情况下,您是否更喜欢 D 或 E。我试图演示此解决方案如何更喜欢具有 E 值而不是 E 值的行D 在没有任何值的行上。
    • 太棒了,感谢您的解释。我可以再问一件事吗?如何将此查询的结果插入到另一个现有表中?
    • 在倒数第二行之前插入一个插入。 ) INSERT dbo.othertable(cols) SELECT A,B,C,D,E ...
    【解决方案2】:
    WITH recordsList
    AS
    (
        SELECT  A, B, C, D, E,
                ROW_NUMBER() OVER (PARTITION BY A,B,C
                                   ORDER BY 
                                     CASE WHEN D IS NULL 
                                          THEN 0
                                          ELSE 1 END DESC) x
       FROM table1
    )
    SELECT A,B,C,D,E
    FROM recordsList
    WHERE x = 1
    

    【讨论】:

    • 在 D 为空但 E 不为空的情况下,这可能会选择数据较少的行(请参阅我的答案中的其他示例行)。
    • @user1994209 不客气。查询仅基于您的结果。我假设如果D 为空,E 也是如此。无论如何,我很高兴:D
    • @JW - 我有一个简单的问题。如果我添加 CASE WHEN E IS NULL THEN 0 ELSE 1 END DESC,这个查询会将 D 和 E 视为相等吗?
    • 如果你的意思是CASE WHEN D IS NULL THEN 0 ELSE 1 END DESC, CASE WHEN E IS NULL THEN 0 ELSE 1 END DESC。是的,但它先订购D,然后订购E。
    猜你喜欢
    • 2021-07-30
    • 2013-06-24
    • 2011-01-10
    • 2019-10-03
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 2013-10-26
    • 2022-09-28
    相关资源
    最近更新 更多