2014-11-06 2 views
2

Я пытаюсь перечислить и сгруппировать некоторые потенциальные дубликаты из таблицы Person.SQL Server: Найти и группировать дубликаты по «слабым» критериям

Схема выглядит следующим образом:

Id LastName  OriginalName FirstName 
--------------------------------------------- 
1  Nolte   Huber   Silvia 
2  Nolte       Johann 
3  Huber       Milan 
4  Huber       Silvia 
5  Abacherli      Adrian 
6  Abächerli      Adrian  
7  Meier       Hans 
8  Meier       Urs 
9  Meyer       Hans 
10 Meier       Urs 
11 Hermann      Marco 
12 Huber       Milan 
13 Meyer       Hans  

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

GroupNumber Id LastName  OriginalName FirstName 
----------------------------------------------------------- 
1    5  Abacherli      Adrian 
1    6  Abächerli      Adrian 
2    3  Huber       Milan 
2    12 Huber       Milan 
3    4  Huber       Silvia 
3    1  Nolte   Huber   Silvia 
4    7  Meier       Hans 
4    9  Meyer       Hans 
4    13 Meyer       Hans 
5    8  Meier       Urs 
5    10 Meier       Urs 

Объяснение:

Я хочу сгруппировать строки, которые рядом матчей и перечислить их в сетке веб-приложение (ASP.NET MVC). Взвешенный дубликат должен иметь по крайней мере:

  • же LastName и тот же FirstName ИЛИ
  • LastName как OrginalName и тот же FirstName

Чтобы сделать вещи более сложными, «же» означает, что фонетическое совпадение (т.е. через SOUNDEX или аналогичную функцию): Meyer == Meier == meier.

технологии в использовании:

  • Microsoft SQL Server 2012
  • Telerik DataAccess ORM
  • .NET Framework 4.5, C#

Ожидаемый ответ:

  • Чистый SQL-запрос OR
  • Хранимая процедура ИЛИ
  • Комбинация SQL запросов/SP и LINQ запрос для ОРМ в C#

Все подходы, которые я выработал до сих пор отсутствует GroupNumber. Это такой (нерабочий) запрос:

SELECT 
    Id, LastName, FirstName 
FROM 
    Person p1, 
    (SELECT 
    p1.Id AS Id1 
    FROM Person p1 
    INNER JOIN Person p2 
    ON (p1.LastName LIKE p2.LastName OR p1.LastName LIKE p2.OriginalName) AND p1.FirstName LIKE p2.FirstName AND p1.Id <> p2.Id 
    GROUP BY p1.Id 
    HAVING COUNT(*) > 1) AS p2 
WHERE 
    p1.Id IN (SELECT Id1) 
ORDER BY 
    p1.LastName, FirstName, Id 
+1

[Плохие привычки пинают: использование старого стиля JOIN и] (http://sqlblog.com/ блоги/aaron_bertrand/archive/2009/10/08/bad-привычки-к-kick-use-old-style-joins.aspx) - этот стиль * разделенных запятыми таблиц * в стиле старого стиля ** не должен быть используемый** и вместо этого рекомендуется использовать ** соответствующий ANSI JOIN ** синтаксис, введенный с ANSI - ** 92 ** SQL Standard (более ** 20 лет ** назад) –

+0

Думали ли вы об использовании служб качества данных Microsoft (часть SQL Server) для идентификации дубликатов и близких совпадений? Это может выводиться в таблицу, которая может отображаться в веб-форме? –

+0

@SteveFord: Я быстро посмотрел на DQS. С точки зрения дублирования поиска и группировки он будет выполнять требования. Но похоже, что API отсутствует (см. [Link] (http://stackoverflow.com/questions/15293671/use-of-dqs-apis)), поэтому я не могу интегрировать результаты в существующее веб-приложение. – flo

ответ

1

Как об этом:

SQL Fiddle

Сервер установки 2012 Схема MS SQL:

CREATE TABLE Person 
(ID Int, 
    LastName Varchar(50), 
    OriginalName Varchar(50), 
    FirstName varchar(50) 
) 

INSERT INTO Person 
VALUES 
    (1, 'Nolte', 'Huber','Silvia'), 
    (2,'Nolte', '', 'Johann'), 
    (3,'Huber', '', 'Milan'), 
    (4,'Huber', '', 'Silvia'), 
    (5,'Abacherli', '', 'Adrian'), 
    (6,'Abacherli', '', 'Adrian'), 
    (7,'Meier', '', 'Hans'), 
    (8,'Meier', '', 'Urs'), 
    (9,'Meyer', '', 'Hans'), 
    (10,'Meier', '', 'Urs'), 
    (11,'Hermann', '', 'Marco'), 
    (12,'Huber', '', 'Milan'), 
    (13,'Meyer', '', 'Hans') 

Запрос 1:

;WITH PersonCTE 
AS 
(
    SELECT ID, SOUNDEX(LastName) AS LastNameSDX, LastName, OriginalName, SOUNDEX(FirstName) FirstNameSDX, FirstName 
    FROM Person 
    UNION ALL 
    SELECT ID, SOUNDEX(OriginalName) AS LastNameSDX, LastName, OriginalName, SOUNDEX(FirstName) FirstNameSDX, FirstName 
    FROM Person 
    WHERE OriginalName <> '' 
), 
PersonRankCTE 
AS 
(
    SELECT DENSE_RANK() OVER (ORDER BY LastNameSDX, FirstNameSdx) AS Grp, * 
    FROM PersonCTE 
) 
SELECT DENSE_RANK() OVER(ORDER BY grp) AS Grp, ID, LastName, OriginalName, FirstName 
FROM PersonRankCTE P1 
WHERE (SELECT COUNT(*) FROM PersonRankCTE P2 WHERE P1.grp = P2.grp) > 1 

Results:

| GRP | ID | LASTNAME | ORIGINALNAME | FIRSTNAME | 
|-----|----|-----------|--------------|-----------| 
| 1 | 5 | Abacherli |    | Adrian | 
| 1 | 6 | Abacherli |    | Adrian | 
| 2 | 3 |  Huber |    |  Milan | 
| 2 | 12 |  Huber |    |  Milan | 
| 3 | 1 |  Nolte |  Huber | Silvia | 
| 3 | 4 |  Huber |    | Silvia | 
| 4 | 13 |  Meyer |    |  Hans | 
| 4 | 9 |  Meyer |    |  Hans | 
| 4 | 7 |  Meier |    |  Hans | 
| 5 | 8 |  Meier |    |  Urs | 
| 5 | 10 |  Meier |    |  Urs | 
0

Может быть (наверное?) Более сложным, но ...

Я делаю два CTE

1, чтобы получить все поля Person с соответствующим Soundex LastName и OriginalName

1 чтобы создать группу и получить GroupNumber.Создание союза все, чтобы получить на колонке "" 1, "soundexed" LastName и ORIGINALNAME (и принимать только дубликатами)

так

with cte as (select 
        id, 
        LastName, 
        OriginalName, 
        soundex(LastName) as sdxLastName, 
        soundex(OriginalName) as sdxOriginalName, 
        FirstName 
      from Person), 

    grp as (select lname, FirstName, row_number() over(order by lname) rn 
      from (
        select 
        sdxOriginalName as lname, 
        FirstName from cte 
        where sdxOriginalName is not null 
        union all 
        select 
         sdxLastName as lname, 
         FirstName from cte) s 
       group by lname, FirstName 
       having count(*) > 1) 
select 
    g.rn as GroupNumber, 
    p.Id, 
    p.LastName, 
    p.OriginalName, 
    p.FirstName 
from grp g 
join cte p on p.firstName = g.FirstName and 
    (sdxLastName = g.lname or sdxOriginalName = g.lname) 
order by rn 

см Sqlfiddle