Как найти повторяющиеся значения в SQL Server - PullRequest
9 голосов
/ 20 мая 2010

Я использую SQL Server 2008. У меня есть таблица

Customers

customer_number int

field1 varchar

field2 varchar

field3 varchar

field4 varchar

... и много других столбцов, которые не имеют значения для моих запросов.

Столбец customer_number это pk. Я пытаюсь найти повторяющиеся значения и некоторые различия между ними.

Пожалуйста, помогите мне найти все строки, которые имеют одинаковые

1) поле1, поле2, поле3, поле4

2) только 3 столбца равны и один из них не равен (кроме строк из списка 1)

3) только 2 столбца равны и два из них не равны (кроме строк из списка 1 и списка 2)

В итоге у меня будет 3 таблицы с этими результатами и дополнительным идентификатором groupId, который будет одинаковым для группы похожих (например, для трех равных столбцов строки с равными тремя одинаковыми столбцами будут отдельной группой )

Спасибо.

Ответы [ 5 ]

56 голосов
/ 10 июня 2011

Вот удобный запрос для поиска дубликатов в таблице. Предположим, вы хотите найти все адреса электронной почты в таблице, которые существуют более одного раза:

SELECT email, COUNT(email) AS NumOccurrences
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )

Вы также можете использовать эту технику, чтобы найти строки, которые встречаются ровно один раз:

SELECT email
FROM users
GROUP BY email
HAVING ( COUNT(email) = 1 )
4 голосов
/ 20 мая 2010

Возможно, проще всего было бы написать хранимую процедуру для перебора каждой группы клиентов с дубликатами и вставить соответствующие числа для номера группы соответственно.

Тем не менее, я думал об этом, и вы, вероятно, можете сделать это с помощью подзапроса. Надеюсь, я не сделал это более сложным, чем следовало бы, но это должно дать вам то, что вы ищете для первой таблицы дубликатов (все четыре поля). Обратите внимание, что это не проверено, поэтому может потребоваться небольшая настройка.

По сути, он получает каждую группу полей, где есть дубликаты, номер группы для каждого, затем получает всех клиентов с этими полями и назначает один и тот же номер группы.

INSERT INTO FourFieldsDuplicates(group_no, customer_no)
SELECT Groups.group_no, custs.customer_no
FROM (SELECT ROW_NUMBER() OVER(ORDER BY c.field1) AS group_no,
             c.field1, c.field2, c.field3, c.field4
      FROM Customers c
      GROUP BY c.field1, c.field2, c.field3, c.field4
      HAVING COUNT(*) > 1) Groups
INNER JOIN Customers custs ON custs.field1 = Groups.field1
                           AND custs.field2 = Groups.field2
                           AND custs.field3 = Groups.field3
                           AND custs.field4 = Groups.field4

Остальные немного сложнее, однако вам нужно будет расширить возможности. Тогда группы с тремя полями будут:

INSERT INTO ThreeFieldsDuplicates(group_no, customer_no)
SELECT Groups.group_no, custs.customer_no
FROM (SELECT ROW_NUMBER() OVER(ORDER BY GroupsInner.field1) AS group_no,
             GroupsInner.field1, GroupsInner.field2, 
             GroupsInner.field3, GroupsInner.field4
      FROM (SELECT c.field1, c.field2, c.field3, NULL AS field4
            FROM Customers c
            WHERE NOT EXISTS(SELECT d.customer_no
                       FROM FourFieldsDuplicates d
                       WHERE d.customer_no = c.customer_no)
            GROUP BY c.field1, c.field2, c.field3
            UNION ALL
            SELECT c.field1, c.field2, NULL AS field3, c.field4
            FROM Customers c
            WHERE NOT EXISTS(SELECT d.customer_no
                             FROM FourFieldsDuplicates d
                             WHERE d.customer_no = c.customer_no)
            GROUP BY c.field1, c.field2, c.field4
            UNION ALL
            SELECT c.field1, NULL AS field2, c.field3, c.field4
            FROM Customers c
            WHERE NOT EXISTS(SELECT d.customer_no
                             FROM FourFieldsDuplicates d
                             WHERE d.customer_no = c.customer_no)
            GROUP BY c.field1, c.field3, c.field4
            UNION ALL
            SELECT NULL AS field1, c.field2, c.field3, c.field4
            FROM Customers c
            WHERE NOT EXISTS(SELECT d.customer_no
                             FROM FourFieldsDuplicates d
                             WHERE d.customer_no = c.customer_no)
            GROUP BY c.field2, c.field3, c.field4) GroupsInner
      GROUP BY GroupsInner.field1, GroupsInner.field2, 
               GroupsInner.field3, GroupsInner.field4
      HAVING COUNT(*) > 1) Groups
INNER JOIN Customers custs ON (Groups.field1 IS NULL OR custs.field1 = Groups.field1)
                           AND (Groups.field2 IS NULL OR custs.field2 = Groups.field2)
                           AND (Groups.field3 IS NULL OR custs.field3 = Groups.field3)
                           AND (Groups.field4 IS NULL OR custs.field4 = Groups.field4)

Надеюсь, это даст правильные результаты, и я оставлю последний в качестве упражнения. : -D

2 голосов
/ 20 мая 2010

Я не уверен, требуется ли вам проверка равенства в разных полях (например, field1 = field2).
В противном случае этого может быть достаточно.

Редактировать

Не стесняйтесь корректировать тестовые данные, чтобы предоставить нам входные данные, которые дают неправильный вывод в соответствии с вашими требованиями.

Данные испытаний

DECLARE @Customers TABLE (
  customer_number INTEGER IDENTITY(1, 1)
  , field1 INTEGER
  , field2 INTEGER
  , field3 INTEGER
  , field4 INTEGER)

INSERT INTO @Customers
          SELECT 1, 1, 1, 1
UNION ALL SELECT 1, 1, 1, 1
UNION ALL SELECT 1, 1, 1, NULL
UNION ALL SELECT 1, 1, 1, 2
UNION ALL SELECT 1, 1, 1, 3
UNION ALL SELECT 2, 1, 1, 1

Все равно

SELECT  ROW_NUMBER() OVER (ORDER BY c1.customer_number)
        , c1.field1
        , c1.field2
        , c1.field3
        , c1.field4
FROM    @Customers c1 
        INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number  
                                    AND ISNULL(c2.field1, 0) = ISNULL(c1.field1, 0) 
                                    AND ISNULL(c2.field2, 0) = ISNULL(c1.field2, 0)
                                    AND ISNULL(c2.field3, 0) = ISNULL(c1.field3, 0)
                                    AND ISNULL(c2.field4, 0) = ISNULL(c1.field4, 0)

Одно поле отличается

SELECT  ROW_NUMBER() OVER (ORDER BY field1, field2, field3, field4)
        , field1
        , field2
        , field3
        , field4
FROM    (
          SELECT  DISTINCT c1.field1
                  , c1.field2
                  , c1.field3
                  , field4 = NULL
          FROM    @Customers c1 
                  INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number  
                                             AND c2.field1 = c1.field1 
                                             AND c2.field2 = c1.field2 
                                             AND c2.field3 = c1.field3 
                                             AND ISNULL(c2.field4, 0) <> ISNULL(c1.field4, 0) 
          UNION ALL
          SELECT  DISTINCT c1.field1
                  , c1.field2
                  , NULL
                  , c1.field4
          FROM    @Customers c1 
                  INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number  
                                             AND c2.field1 = c1.field1 
                                             AND c2.field2 = c1.field2 
                                             AND ISNULL(c2.field3, 0) <> ISNULL(c1.field3, 0) 
                                             AND c2.field4 = c1.field4 
          UNION ALL
          SELECT  DISTINCT c1.field1
                  , NULL
                  , c1.field3
                  , c1.field4
          FROM    @Customers c1 
                  INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number  
                                             AND c2.field1 = c1.field1 
                                             AND ISNULL(c2.field2, 0) <> ISNULL(c1.field2, 0) 
                                             AND c2.field3 = c1.field3 
                                             AND c2.field4 = c1.field4 
          UNION ALL
          SELECT  DISTINCT NULL
                  , c1.field2
                  , c1.field3
                  , c1.field4
          FROM    @Customers c1 
                  INNER JOIN @Customers c2 ON c2.customer_number > c1.customer_number  
                                             AND ISNULL(c2.field1, 0) <> ISNULL(c1.field1, 0)
                                             AND c2.field2 = c1.field2 
                                             AND c2.field3 = c1.field3 
                                             AND c2.field4 = c1.field4 
      ) c
0 голосов
/ 27 сентября 2014

Существует чистый способ сделать это с CUBE(), который агрегирует по все возможные комбинации столбцов

SELECT
  field1,field2,field3,field4
 ,duplicate_row_count = COUNT(*)
 ,grp_id = GROUPING_ID(field1,field2,field3,field4)
INTO #duplicate_rows
FROM table_name
GROUP BY CUBE(field1,field2,field3,field4)
HAVING COUNT(*) > 1
  AND GROUPING_ID(field1,field2,field3,field4) IN (0,1,2,4,8,3,5,6,9,10,12)

Числа (0,1,2,4,8,3,5,6,9,10,12) - это только битовые маски (0000,0001,0010,0100, ..., 1010,1100) группирующие наборы, которые нас интересуют - наборы с 4, 3 или 2 совпадениями.

Затем присоедините это обратно к исходной таблице, используя технику, которая обрабатывает NULL в #duplicate_rows как подстановочные знаки

SELECT a.*
FROM table_name a
INNER JOIN #duplicate_rows b
  ON  NULLIF(b.field1,a.field1) IS NULL
  AND NULLIF(b.field2,a.field2) IS NULL
  AND NULLIF(b.field3,a.field3) IS NULL
  AND NULLIF(b.field4,a.field4) IS NULL
--WHERE grp_id IN (0)             --Use this for 4 matches
--WHERE grp_id IN (1,2,4,8)       --Use this for 3 matches
--WHERE grp_id IN (3,5,6,9,10,12) --Use this for 2 matches
0 голосов
/ 25 февраля 2013

Вы можете написать что-то подобное, чтобы подсчитать дубликаты записей, я думаю, что это работает:

use *DATABASE_NAME*
go
SELECT     *YOUR_FIELD*, COUNT(*) AS dupes  
FROM         *YOUR_TABLE_NAME*
GROUP BY *YOUR_FIELD* 
HAVING      (COUNT(*) > 1)

Наслаждайтесь

...