Группировка SQL Server считает ноль равным всем значениям - PullRequest
0 голосов
/ 24 февраля 2019

В SQL Server я пытаюсь сгруппировать совпадающие строки по идентификаторам.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');

Таблица, например

enter image description here

Это ожидаемый результат:

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

эти строки не могут объединиться в одну группу: {1,2,3}.

Пример ожидаемого результата в таблице:

enter image description here

Я попробовал этот ответ:

   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

таблица результатов: enter image description here

Итак, я вижу, что строка 1 соответствуетстроки 2, 3, 5 - но я не вижу, чтобы строки 2 и 3/5 не могли объединиться в одну группу.То, что я хочу, это результат, в котором я вижу, что строки 1, 3, 5 могут быть в одной группе, потому что они все совпадают, но соответствие между строками 1 и 2 должно быть в другой группе, потому что строка 2 не совпадает со строками3 и 5.

Ответы [ 2 ]

0 голосов
/ 31 июля 2019

Это проще, чем кажется на самом деле.

Дайте мне знать, если оно не работает с другими образцами данных.

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: Пожалуйста, киньте реальную таблицуStructure.Total количество записей и сколько строк требуется в наборе результатов?

Есть ли какие-либо ограничения, что column1, column2, column3 будет содержать только определенное значение. Или они могут содержать что-нибудь?

0 голосов
/ 24 февраля 2019

Вы можете попробовать следующий подход:

  • Найти все различные значения из column1, column2 и column3.Эти значения являются возможными кандидатами для 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
...