Как оптимизировать группировку в таблице с огромным количеством записей - PullRequest
1 голос
/ 10 февраля 2011

У меня есть таблица Person с огромным количеством записей (около 16 миллионов), и я должен найти всех людей с одинаковыми lastname, первой буквой firstname и birthyear в других мирах, которые яхочу показать, что в пользовательском интерфейсе пользователя есть дубликаты, чтобы пользователи могли проанализировать и решить, есть ли один и тот же человек или нет.

Вот запрос, который я пишу

SELECT * 
FROM Person INNER JOIN
(
    SELECT SUBSTRING(firstName, 1, 1) firstNameF,lastName,YEAR(birthDate) birthYear
    FROM Person
    GROUP BY SUBSTRING(firstName, 1,1),lastName,YEAR(birthDate)
    HAVING count(*) > 1
) as dupPersons 
ON SUBSTRING(Person.firstName,1,1) = dupPersons.firstNameF and Person.lastName = dupPersons.lastName and YEAR(Person.birthDate) = dupPersons.birthYear
order by Person.lastName,Person.firstName

, но я не эксперт по SQLхочу тоже знать, это хороший способ сделать это?Есть ли более оптимизированный способ?

РЕДАКТИРОВАТЬ

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

, например, если я хочусократить данные на 2 может вернуть два человека

Johan Smith |
Jane Smith  | have same lastname and first name inita 
Jack Smith  |

Mark Tween  | have same lastname and first name inita 
Mac Tween   |

Ответы [ 3 ]

2 голосов
/ 10 февраля 2011

Если производительность с использованием GROUP BY недостаточна, вы можете попробовать использовать INNER JOIN

SELECT  *
FROM    Person p1
        INNER JOIN Person p2 ON p2.PersonID > p1.PersonID
WHERE   SUBSTRING(p2.Firstname, 1, 1) = SUBSTRING(p1.Firstname, 1, 1) 
        AND p2.LastName = p1.LastName
        AND YEAR(p2.BirthDate) = YEAR(p1.BirthDate)
ORDER BY
        p1.LastName, p1.FirstName        
2 голосов
/ 10 февраля 2011

Что ж, если вы не эксперт, написанный вами запрос говорит мне, что вы по крайней мере довольно компетентны.Когда мы смотрим, является ли запрос «оптимизированным», есть две непосредственные части этого: 1. Запрос сам по себе имеет что-то явно неправильное - плохое объединение, неправильное использование ключевого слова, взрывной размер набора результатов, допущения о NOT IN и т. Д. 2. Контекст, в котором работает запрос - специфика БД, специфика задачи и т. Д.

Ваш запрос проходит # 1, нет проблем.Я написал бы это по-другому - присвоил псевдоним таблице Person, использовал LEFT(P.FirstName, 1) вместо SUBSTRING и использовал бы CTE (WITH -clause) вместо подзапроса.Но это не проблемы оптимизации.Возможно, я бы использовал WITH(READUNCOMMITTED), если результаты не были чувствительны к грязному чтению.Вне зависимости от контекста, ваш запрос не похож на бомбу, ожидающую взрыва.

Что касается # 2 - вам, вероятно, следует перейти к конкретике.Например, «Я должен запускать это каждую неделю. Это занимает 17 минут. Как я могу получить это менее чем за минуту?»Тогда люди спросят вас, как выглядит ваш план, какие у вас индексы и т. Д.

Вещи, которые я бы хотел знать:

  • Сколько времени уже занимает выполнение?
  • Какое у вас окно выполнения?(Допуск пользователя и приложения для времени запроса.)
  • Этот запуск выполняется один раз в день?Неделю?Месяц?Квартал?
  • У вас есть разрешение на создание таблиц, изменение текущих таблиц или изменение индексов?
  • Может быть, исходя из того, что вы его запустили, какое соотношение дубликатов вы ожидаете найти?5%?90%?
  • Насколько стабилен критерий соответствия?

Пример сценария: Если это была функция запуска по команде, она будет в моем приложении бесконечно долго, она будетзапускаться еженедельно, с ожидаемым количеством дубликатов на 10% или менее, с возможностью изменять БД так, как мне хотелось бы, если критерий соответствия дубликатов тверд (не колеблется), и я хочу сократить его с 90 до 5 с,Я бы создал выделенный столбец BirthYear (возможно, постоянный вычисляемый столбец с BirthDate) и индекс на LastName ASC, BirthYear ASC, FirstName ASC.Если слишком многие из этих условий изменятся, я мог бы пойти в другом направлении.

1 голос
/ 10 февраля 2011

Вы можете попробовать что-то вроде этого и увидеть разницу в планах выполнения или сравнить результаты по производительности:

;WITH DupPersons AS
(
    SELECT *, COUNT(1) OVER(PARTITION BY SUBSTRING(firstName, 1, 1), lastName, YEAR(birthDate)) Quant
    FROM Person
)

SELECT *
FROM DupPersons
WHERE Quant > 1

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...