【问题标题】:SQL Server grouping consider null as equals to all valuesSQL Server 分组将 null 视为等于所有值
【发布时间】:2019-07-17 22:18:27
【问题描述】:

在 SQL Server 中,我尝试按 id 对匹配的行进行分组。 Null 被视为通配符。

解释:匹配行是什么意思?

匹配行意味着——只要两行的所有列都匹配。

匹配列均值 - 相同的值 ('A' = 'A') 或每个值的空值 ('A'/'B'/'C'/... = NULL)。

在我的例子中:

第 1 行匹配第 2 行 - 因为:

First column: 'A' = 'A'
Second column: 'B' = NULL
Third column: NULL = 'C'

第 1 行与第 4 行不匹配:

First column: 'A' = 'A'
Second column: 'B' != 'D'
Third column: NULL = NULL.

比较失败,因为第二列中的值不匹配。

任何人都可以帮助我处理 SQL 吗?

例如:

用于创建测试表:

create table test_table 
(
    id int,
    column1 varchar(20),
    column2 varchar(20),
    column3 varchar(20)
);

insert into test_table (id, column1, column2, column3) values 
(1, 'A', 'B', NULL),
(2, 'A',NULL, 'C'),
(3, 'A', 'B', 'D'),
(4, NULL, 'D', NULL),
(5, 'A', 'B', 'D');

以表格为例

这是预期的结果:

group id 1: {1,2}
group id 2: {1,3,5}
group id 3: {2,4}

这些行不能加入一个组:{1,2,3}。

表格中预期结果的示例:

我试过这个答案:

   SELECT
    T1.id as row_id,
    T2.id as row_id
FROM
    test_table AS T1
    INNER JOIN test_table AS T2 ON
        (T1.column1 = T2.column1 OR T1.column1 IS NULL OR T2.column1 IS NULL) AND
        (T1.column2 = T2.column2 OR T1.column2 IS NULL OR T2.column2 IS NULL) AND
        (T1.column3 = T2.column3 OR T1.column3 IS NULL OR T2.column3 IS NULL)
WHERE
    T1.id < T2.id

结果表:

所以我可以看到第 1 行与第 2、3、5 行匹配 - 但我看不到第 2 行和第 3/5 行不能加入同一个组。 我想要的是一个结果,我可以看到第 1,3,5 行可以在同一个组中,因为它们都匹配,但是第 1 行和第 2 行之间的匹配需要在其他组中,因为第 2 行不匹配行3 和 5。

【问题讨论】:

  • 我几乎觉得你有一个规范化问题,你可能应该每对列有一个联结表。我不确定你在这里得到的任何答案是否能很好地扩展。
  • 您在解释什么是匹配方面做得很好,但从不解释什么是组。您可以进行 2 次匹配,但 3 次匹配是什么意思?我不明白表格样本上的 group_id 是什么以及与文本样本的关系如何
  • 我测试了您的数据和查询,看起来不错rextester.com/BDI28000,结果与您的期望输出不匹配,但符合您的描述。所以不知道你有什么问题。
  • 对不起,我更新了问题中的结果表。谢谢。
  • 再次,您有一个条件T1.id &lt; T2.id,因此与您的期望输出不匹配。,您必须再次扩展匹配说明,因为我的测试符合您的描述

标签: sql-server null grouping


【解决方案1】:

您可以尝试以下方法:

  • column1column2column3 中查找所有不同的值。这些值是您列中NULL 值的可能候选值。
  • 使用生成的不同值生成 NULL 值的所有可能组合
  • 只选择重复的行
  • 使用DENSE_RANK() 生成组号码

声明:

;WITH ValuesCTE ([column]) AS (
   SELECT column1 FROM #test_table WHERE column1 IS NOT NULL
   UNION 
   SELECT column2 FROM #test_table WHERE column2 IS NOT NULL
   UNION 
   SELECT column3 FROM #test_table WHERE column3 IS NOT NULL
), ReplaceCTE AS (
   SELECT 
      t.id, 
      CASE WHEN t.column1 IS NULL THEN c1.[column] ELSE t.column1 END AS column1, 
      CASE WHEN t.column2 IS NULL THEN c2.[column] ELSE t.column2 END AS column2, 
      CASE WHEN t.column3 IS NULL THEN c3.[column] ELSE t.column3 END AS column3
   FROM #test_table t
   LEFT JOIN ValuesCTE c1 ON t.column1 IS NULL
   LEFT JOIN ValuesCTE c2 ON t.column2 IS NULL
   LEFT JOIN ValuesCTE c3 ON t.column3 IS NULL
), DuplicatesCTE AS (
   SELECT column1, column2, column3 
   FROM ReplaceCTE
   GROUP BY column1, column2, column3
   HAVING COUNT(*) > 1
)
SELECT
   r.id,
   DENSE_RANK() OVER (ORDER BY r.column1, r.column2, r.column3) AS grp
FROM ReplaceCTE r
RIGHT JOIN DuplicatesCTE d ON (r.column1 = d.column1) AND (r.column2 = d.column2) AND (r.column3 = d.column3)

输出:

id  grp
1   1
2   1
1   2
3   2
5   2
2   3
4   3

【讨论】:

    【解决方案2】:

    这个比它看起来更容易。

    如果它不适用于任何其他示例数据,请告诉我。

    drop table if exists #test_table
    create table #test_table 
    (
        id int,
        column1 varchar(20),
        column2 varchar(20),
        column3 varchar(20)
    );
    
    insert into #test_table (id, column1, column2, column3) 
    values 
    (1, 'A', 'B', NULL),
    (2, 'A',NULL, 'C'),
    (3, 'A', 'B', 'D'),
    (4, NULL, 'D', NULL),
    (5, 'A', 'B', 'D');
    
    WITH CTE
         AS (SELECT t.id, 
                    ISNULL(t.column1, t1.column1) col1, 
                    ISNULL(t.column2, t1.column2) col2, 
                    ISNULL(t.column3, t1.column3) col3, 
                    t1.id AS tid, 
                    ISNULL(t1.column1, t.column1) col4, 
                    ISNULL(t1.column2, t.column2) col5, 
                    ISNULL(t1.column3, t.column3) col6
             FROM #test_table t
                  INNER JOIN #test_table t1 ON ISNULL(t.column1, t1.column1) = ISNULL(t1.column1, t.column1)
                                               AND ISNULL(t.column2, t1.column2) = ISNULL(t1.column2, t.column2)
                                               AND ISNULL(t.column3, t1.column3) = ISNULL(t1.column3, t.column3)
             WHERE t1.id > t.id),
         CTE1
         AS (SELECT *, 
                    DENSE_RANK() OVER(
                    ORDER BY col4, 
                             col5, 
                             col6) rn
             FROM CTE)
         SELECT DISTINCT 
                t.id, 
                ca.rn
         FROM #test_table t
              CROSS APPLY
         (
             SELECT c.id, 
                    c.tid, 
                    rn
             FROM cte1 c
             WHERE t.id = c.id
                   OR t.id = c.tid
         ) ca
         ORDER BY rn;
    

    For optimize query:请抛出真实的表结构。记录的总数和结果集中需要多少行?

    column1,column2,column3 是否有任何限制只能包含某些值。或者它们可以包含任何内容?

    【讨论】:

      猜你喜欢
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      • 2016-01-21
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 1970-01-01
      • 2013-06-25
      相关资源
      最近更新 更多