← Все курсы · Курс SQL · Все руководства
Как найти повторяющиеся строки в SQL (и решить, что считать одной)
В конце этой страницы вы можете проверить любую таблицу на наличие повторяющихся строк, составить список всех копий и отметить, какую из них следует сохранить, и все это с помощью понятных вам запросов. Вы также будете знать шаг, который следует выполнить перед любым запросом: решить, что означает «дубликат» для этой таблицы, потому что две строки могут совпадать во всем или в одном столбце, и это разные проблемы с разными исправлениями. Это около двадцати минут.
Вот что на самом деле с этим делать. В следующей таблице, которую вы получите, запустите одно сравнение прежде всего: COUNT(*) против COUNT(DISTINCT key). Если эти два числа различаются, в таблице есть дубликаты, и теперь вы узнаете об этом еще до того, как это произойдет в первом отчете.
Краткая версия: группируйте по столбцам, которые определяют дубликат, сохраняйте группы там, где COUNT(*) больше единицы, и отметьте дополнительные функции значком ROW_NUMBER вместо того, чтобы удалять их.
Одна картинка несет в себе весь метод. Строки с общим ключом сворачиваются в сегменты, а сегменты, содержащие более одной строки, являются вашими дубликатами.
- Решите, что считать дубликатом, прежде чем писать что-либо.
- Десятисекундный тест на любом столе
- Перечислите дубликаты с помощью GROUP BY и HAVING.
- Просматривайте полные повторяющиеся строки, а не только сводку.
- Отметьте хранителей и статистов с помощью ROW_NUMBER
- Шаблон удаления, указанный один раз, с ограждениями
- Полное до и после
- Крайние случаи, из-за которых поиск дубликатов идет не так
- Почему это работает
- Использование этого в вашем собственном проекте
- Все на одном экране
GROUP BY сам новый, читайте GROUP BY и HAVING сначала и возвращайся.Вот таблица. Четырнадцать строк, и система регистрации дала сбой тремя разными способами.
| customer_id | электронная почта | полное имя | Город | Дата_регистрации |
|---|---|---|---|---|
| 101 | ana@keller.com | Ана Келлер | Остин | 2026-01-04 |
| 102 | ben.ortiz@mail.com | Бен Ортис | Даллас | 2026-01-09 |
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 104 | dev.p@mail.com | Dev Patel | Хьюстон | 2026-01-20 |
| 105 | ella.r@mail.com | Элла Рейес | Даллас | 2026-02-02 |
| 106 | finn.w@mail.com | Найти Уолша | Остин | 2026-02-11 |
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 107 | gus.m@mail.com | Гас Моран | Хьюстон | 2026-02-19 |
| 108 | hana.s@mail.com | Хана Сато | Даллас | 2026-02-25 |
| 105 | ella.r@mail.com | Элла Рейес | Форт-Уэрт | 2026-03-01 |
| 109 | ivan.k@mail.com | Иван Кова | Остин | 2026-03-06 |
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 110 | jo.b@mail.com | Да Брандт | Хьюстон | 2026-03-14 |
| 111 | ben.ortiz@mail.com | Бенджамин Ортис | Даллас | 2026-03-20 |
1. Решите, что считать дубликатом, прежде чем писать что-либо.
Перед пояснением: клиент 103 появляется три раза, причем все столбцы идентичны. Клиент 105 появляется дважды с двумя разными городами. Электронная почта Бена Ортиса отображается под двумя разными идентификаторами. Какие из этих трех являются дубликатами?
На этот вопрос нет однозначного ответа, и это настоящий первый шаг в любой охоте за дубликатами. «Дубликат» не является свойством данных. Вы принимаете решение о том, какие столбцы должны совпадать, прежде чем две строки будут означать одно и то же. Есть три общих ответа, и каждый из них ведет к разным запросам и разным процедурам очистки.
- Весь ряд одинаковый. Каждый столбец совпадает, как клиент 103. Почти всегда это случайная загрузка: один и тот же файл импортирован дважды, повторная вставка, копипаста. Дополнительные копии не несут никакой информации, и их можно безопасно удалить после пометки.
- Тот же естественный ключ, остальное отличается. Естественный ключ — это столбец, который идентифицирует вещь в реальном мире, например
customer_id. У клиента 105 один идентификатор и два города, поэтому одна строка устарела, а другая — текущая. Удаление неправильной копии уничтожает реальную информацию, поэтому здесь решается, какую копию оставить, а не сколько удалить. - Тот же человек, другое написание. Бен Ортис и Бенджамин Ортис обмениваются электронными письмами, но ничего
GROUP BYможет точно совпасть. Ни один запрос на этой странице не обнаружит эту пару как совпадение имени. Эта проблема называется разрешением сущностей: она решает, когда две записи, написанные по-разному, являются одной и той же реальной вещью. Свой собственный гид.
Итак, развилка: какие столбцы определяют «то же самое»? Все это означает, что вы охотитесь за несчастными случаями при погрузке. Естественный ключ означает, что вы ищете противоречивые версии. Ни то, ни другое не означает, что вы можете охотиться на людей, и это другой инструмент. Все, что ниже, работает для первых двух, а запросы отличаются только тем, что вы ставите после GROUP BY.
2. Десятисекундный тест на любом столе
Прежде чем объяснить: не оглядываясь на таблицу, как бы вы получили одно число, которое говорит о том, появляется ли какой-либо идентификатор дважды?
Посчитайте ряды двумя способами. COUNT(*) считает каждую строку. COUNT(DISTINCT customer_id) подсчитывает, сколько существует различных значений идентификаторов. Если каждый идентификатор появляется один раз, два числа совпадают. Любой разрыв между ними — это количество дополнительных копий.
SELECT COUNT(*) AS total_rows,
COUNT(DISTINCT customer_id) AS distinct_ids
FROM customers;
| всего_строк | различные_иды |
|---|---|
| 14 | 11 |
Четырнадцать строк, одиннадцать различных идентификаторов, поэтому три строки — это дополнительные копии чего-то. Это запрос, который я выполняю для каждой таблицы, которую мне передают, перед любым соединением и перед любым отчетом, потому что он занимает около десяти секунд и меняет то, чему я доверяю. Он не сообщает вам, какие строки являются копиями. Оно говорит вам, нужна ли охота вообще, а 14 против 11 говорят, что да.
customer_id и в этой таблице 14 строк для 11 идентификаторов, соединение будет умножать строки, и каждый последующий счетчик будет совершенно неправильным. Десять секунд здесь экономят день там.3. Перечислите дубликаты с помощью GROUP BY и HAVING.
До пояснения: тест говорит, что существуют три лишние строки. Что бы вы сгруппировали, чтобы узнать, каким клиентам они принадлежат?
Сгруппируйте столбцы из вашего первого шага, подсчитайте каждый сегмент и оставьте только сегменты с более чем одной строкой. HAVING — это предложение, которое фильтрует группы после их формирования, то есть именно в тот момент, когда существует счетчик.
SELECT customer_id, COUNT(*) AS copies
FROM customers
GROUP BY customer_id
HAVING COUNT(*) > 1
ORDER BY copies DESC;
| customer_id | копии |
|---|---|
| 103 | 3 |
| 105 | 2 |
Два покупателя, пять рядов между ними, трое из них статисты. Это как раз и объясняет пробел на втором этапе: 14 строк минус 11 идентификаторов — это 3, и вот они, две дополнительные копии 103 плюс одна дополнительная копия 105.
Чтобы вместо этого искать дубликаты всей строки, поместите каждый столбец в GROUP BY. Теперь сегмент формируется только тогда, когда строки совпадают во всем.
SELECT customer_id, email, full_name, city, signup_date,
COUNT(*) AS copies
FROM customers
GROUP BY customer_id, email, full_name, city, signup_date
HAVING COUNT(*) > 1;
| customer_id | электронная почта | полное имя | Город | Дата_регистрации | копии |
|---|---|---|---|---|---|
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 | 3 |
Обратите внимание, что изменилось. Клиент 105 исчез из этого результата, поскольку ее две строки различаются по городам, поэтому они никогда не попадают в одну и ту же корзину. Прежде чем читать дальше, скажите своими словами, почему эти два запроса расходятся по поводу 105. Если вы можете это объяснить, у вас есть весь метод: GROUP BY список — это ваше определение дубликата, записанное в виде кода.
4. Просматривайте полные повторяющиеся строки, а не только сводку.
Перед пояснением: в сводке говорится, что у клиента 105 есть две копии, но чтобы решить, какую оставить, вам нужно увидеть обе строки рядом. В сгруппированном результате их невозможно отобразить, поскольку группировка их свернула. Как их вернуть?
Используйте сводку в качестве списка покупок. Сначала найдите идентификаторы с дубликатами, затем извлеките каждую строку, идентификатор которой есть в этом списке. IN это самый простой способ сказать это.
SELECT *
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM customers
GROUP BY customer_id
HAVING COUNT(*) > 1
)
ORDER BY customer_id, signup_date;
| customer_id | электронная почта | полное имя | Город | Дата_регистрации |
|---|---|---|---|---|
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 103 | cara.li@mail.com | Дорогая Ли | Остин | 2026-01-15 |
| 105 | ella.r@mail.com | Элла Рейес | Даллас | 2026-02-02 |
| 105 | ella.r@mail.com | Элла Рейес | Форт-Уэрт | 2026-03-01 |
Пять рядов, и теперь две проблемы выглядят совершенно разными. Клиент 103 — три одинаковые строки: авария при погрузке, нечего решать. Клиент 105 — это две версии одного человека: Даллас в феврале, Форт-Уэрт в марте, и кто-то должен сказать, какой город настоящий. Обратное соединение с той же сводкой дает идентичный результат, и я запустил оба варианта, чтобы убедиться: в любом случае одни и те же пять строк. Используйте то, что вам больше нравится; SQL-СОЕДИНЕНИЯ охватывает форму присоединения.
5. Отметьте хранителей и статистов с помощью ROW_NUMBER.
Перед пояснением: теперь вы можете видеть все пять повторяющихся строк. Какой столбец, добавленный в таблицу, позволит кому-либо еще действовать с ним, не переделывая вашу работу?
Номер копии. ROW_NUMBER() — это оконная функция, то есть она вычисляет значение для каждой строки, ничего не сворачивая. PARTITION BY customer_id перезапускает нумерацию для каждого клиента, и ORDER BY signup_date решает, кто получит номер один. Итак, копия номер 1 — ваш хранитель, а все, что выше 1, — лишнее, согласно записанному вами правилу.
SELECT customer_id, city, signup_date,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY signup_date
) AS copy_number
FROM customers
ORDER BY customer_id, copy_number;
Дублированные клиенты возвращаются под таким номером.
| customer_id | Город | Дата_регистрации | номер_копии |
|---|---|---|---|
| 103 | Остин | 2026-01-15 | 1 |
| 103 | Остин | 2026-01-15 | 2 |
| 103 | Остин | 2026-01-15 | 3 |
| 105 | Даллас | 2026-02-02 | 1 |
| 105 | Форт-Уэрт | 2026-03-01 | 2 |
Каждый второй клиент просто получает копию номер 1. Оберните ее в подзапрос и сохраните. copy_number > 1 и у вас есть ровно три дополнительных ряда, которые я проверил, чтобы подтвердить: два дополнительных ряда для 103 и ряд Форт-Уэрта для 105.
Вот ответвление варианта «сохранить против удаления», потому что это реальное решение. Удаление дополнительных данных делает таблицу чистой, но уничтожает улики, и если ваше правило хранителя было неверным, правильная строка исчезнет. При выборе дополнительных элементов сохраняется каждая строка и добавляется столбец, в котором указано, какую из них следует использовать в отчетах. Маркировка стоит одного столбца. Удаление стоит возможности изменить свое мнение. Вот почему работающие аналитики сначала отмечают и редко удаляют: фильтруют отчеты по copy_number = 1 и вы получите все преимущества удаления без какого-либо риска. Обратите внимание, что в запросе видно правило маркировки: для 105 ORDER BY signup_date сделал новый ряд в Форт-Уэрте дополнительным. Если компания говорит о новых победах, переверните заказ на signup_date DESC и вратарь тоже переворачивается. Правило представляет собой код, поэтому его можно просматривать и оспаривать.
Семейство оконных функций идет гораздо дальше этого трюка. Оконные функции проходит через него правильно. Если вам нужен узор на пальцах, а не в закладках, введите запрос самостоятельно несколько раз в SQL-тренировка.
6. Схема удаления, указанная один раз, с ограждением
Иногда ответ действительно заключается в удалении строк, обычно в случае аварийной загрузки всей строки, например, клиента 103. Вот шаблон, один раз. В SQLite каждая строка имеет скрытую rowid, поэтому оставьте самый маленький rowid на каждого клиента и удалите остальные. Другие базы данных используют ctid, идентификатор системы или ROW_NUMBER подзапрос, но форма та же.
DELETE FROM customers
WHERE rowid NOT IN (
SELECT MIN(rowid)
FROM customers
GROUP BY customer_id
);
Я запустил его на обработанной таблице, и последующие счетчики составили 11 строк и 11 различных идентификаторов: десятисекундный тест теперь пройден. Два ограждения, и они не являются обязательными.
- Сначала запустите его как SELECT. Изменять
DELETE FROM customersкSELECT * FROM customersс тем же самымWHEREи прочитайте строки, которые умрут. На этой таблице показаны именно три статиста и ничего больше. Если выбор вас удивил, то удаление тоже произойдет, за исключением того, что удаление не позволяет вам смотреть дважды. - Сохраните исходную таблицу. Выполните удаление копии или сначала запишите удаленные строки в боковую таблицу. Обратите внимание, что этот шаблон удаления сохраняет произвольную самую раннюю загруженную строку, а не выбранную, поэтому в случае конфликтов с одинаковыми ключами, таких как клиент 105, он может молча сохранять устаревший город. Именно поэтому маркировка стояла на первом месте.
7. Полное до и после
Та же таблица, тот же вопрос: у каких клиентов есть повторяющиеся строки, и какая копия должна стоять?
До
SELECT customer_id, COUNT(*)
FROM customers
GROUP BY customer_id
HAVING COUNT(*) > 1;
Правильно, и недостаточно, чтобы действовать. Он называет номера 103 и 105, но не показывает ни одной из их строк, не указывает правила хранителя и не дает следующему человеку ничего для просмотра.
после
-- ============================================================
-- STEP 2: Which customer rows are duplicates, and which stands?
-- WHY: 14 rows but only 11 distinct customer_ids, so 3 rows
-- are extra copies. Reports must count customers once.
-- RULE: keeper = earliest signup_date per customer_id.
-- 105's city conflict (Dallas vs Fort Worth) goes to the
-- data owner; nothing is deleted here.
-- ============================================================
--SELECT each customer row with its copy number:
-- ROW_NUMBER restarts at 1 for every customer_id,
-- ordered by signup_date, so copy_number 1 = keeper
SELECT customer_id, email, full_name, city, signup_date,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY signup_date
) AS copy_number
FROM customers
ORDER BY customer_id, copy_number;
Присутствует каждая строка, записано правило хранителя, с которым рецензент может не согласиться, известный конфликт назван, а последующие запросы просто фильтруются, чтобы copy_number = 1. Формат комментариев тот же, что и у Как комментировать SQL, чтобы он обучал.
Теперь представьте, как выполняется запрос маркировки для вашей самой большой таблицы, секционированной по ее естественному ключу. Какой столбец вы бы поместили в ORDER BY принять решение о хранителе, и могли бы вы защитить этот выбор перед лицом, владеющим данными?
8. Крайние случаи, из-за которых поиск дубликатов идет не так, как надо
Перед объяснением: ваш дублирующийся запрос возвращает ноль строк, но следующий отчет по-прежнему учитывает клиентов дважды. Каким образом оба этих утверждения могут быть правдой одновременно?
Пять, каждая из которых стоила кому-то реального времени.
Вы сгруппировали по неправильному определению. Это ответ с нулевыми строками. Группировка по каждому столбцу не находит дубликатов в таблице, где один и тот же идентификатор встречается дважды в разных городах, точно так же, как клиент 105. Когда запрос всей строки возвращается чистым, запустите версию с естественным ключом, прежде чем объявлять таблицу чистой.
Дубликатов нет в этой таблице. Соединение может умножать строки, даже если обе таблицы проходят десятисекундный тест на своих собственных ключах, если вы объединяете столбец, который не уникален ни в одной из таблиц. Если отчет имеет двойной подсчет, проверьте ключ соединения на каждой стороне, а не только идентификатор.
Близкие совпадения ускользают от каждой точной группы. Конечные пробелы, заглавные буквы и варианты написания — это разные значения. GROUP BY. На моем столе специально стоит один из них: Бен Ортис и Бенджамин Ортис. Группировка по электронной почте выявляет эту пару, и я запустил ее: ben.ortiz@mail.com показывает 2 копии под 2 разными идентификаторами. Но группировка по имени никогда не поможет, и истинное нечеткое сопоставление Разрешение объекта, а не больший GROUP BY.
NULL группируются вместе. Две строки с отсутствующим адресом электронной почты попадают в одну корзину, поскольку при группировке все NULL-значения рассматриваются как одно значение. Ведро заготовок со счетом 40 – это не 40 дубликатов. В 40 строках отсутствует ключ, и это отдельная находка, о которой стоит сообщить отдельно.
Дубликаты, обнаруженные во время миграции, — это симптом, а не рутинная работа. При профилировании исходной системы до того, как миграция обнаружит дубликаты, подсчет свидетельствует о том, как вела себя старая система: повторные попытки сохранения, объединение офисов, пользователи, работающие над ошибкой. Запишите, откуда они взялись, прежде чем их кто-нибудь очистит, потому что шаблон предсказывает, что еще не так. Профилирование данных перед миграцией охватывает этот рабочий процесс.
Почему это работает
Метод опирается на одно свойство группировки. Группировка разбивает строки на непересекающиеся наборы по значению, поэтому две строки попадают в один и тот же набор точно тогда, когда они совпадают в каждом сгруппированном столбце. Это делает GROUP BY перечислите точное, осуществимое изложение вашего повторяющегося определения, а не расплывчатое намерение. Реляционная модель с самого начала рассматривала таблицы как наборы строк (Кодд, 1970, Коммуникации ACM, 13(6), 377–387), поэтому именно точное совпадение — это все, что база данных дает вам изначально, и почему для определения одного и того же человека с разным написанием нужны отдельные инструменты.
Привычка ставить отметки имеет более простое оправдание: она делает каждое решение обратимым. Правило хранителя живет в ORDER BY что рецензент может читать и перелистывать, а дополнительные материалы остаются на диске до тех пор, пока кто-то, обладающий властью над данными, не скажет иное. Удаление незаметно внедряет одно и то же правило и обеспечивает его постоянное применение.
Есть также причина, по которой эта страница продолжает просить вас ответить, прежде чем она объяснит. Попытка ответить до того, как вы увидите решение, значительно улучшит то, что вы сохраните, даже если ваша попытка окажется ошибочной (Бисра, Лю, Несбит, Салими и Винн, 2018, Обзор педагогической психологии, 30(3), 703–725), а практика извлечения информации превосходит перечитывание того же материала (Краснее & выбирать карпа, 2006, Психологическая наука, 17(3), 249–255). Решение о том, считать ли клиента 105 дубликатом (прежде чем я вам сказал, это зависит от того, почему три определения вернутся к вам на клавиатуре).
Использование этого в вашем собственном проекте
Проверка каждой вашей таблицы на наличие дубликатов за один присест — это утомительно, и вы оставите ее за четвертым столом. Вместо этого сделайте это по порядку.
- Запустите десятисекундный тест на одной таблице, от которой больше всего зависит ваш текущий отчет.
COUNT(*)противCOUNT(DISTINCT key). Если они совпадают, остановитесь. У вас есть ответ на сегодня. - Если они различаются, запишите повторяющееся определение в одном предложении. прежде чем прикасаться к клавиатуре. «Один и тот же идентификатор клиента» и «идентичная строка» приводят к разным запросам, и если вы выберете один из них вслух, вы не сможете их смешивать.
- Перечислите правонарушителей с помощью GROUP BY и HAVING, затем вытащите полные строки назад с помощью
IN. Прочтите их. Сами строки обычно сообщают вам, какая из трех проблем у вас возникла. - Отметьте хранителей ROW_NUMBER и письменным правилом. Отфильтруйте отчеты по копии номер 1. Ничего не удаляйте на этой неделе.
- Передайте конфликтные строки тому, кто владеет данными. Два города Эллы Рейес не являются проблемой запроса, и если вы сделаете вид, что это не так, вы просто переместите ошибку в ваш отчет.
Если у вас есть бумага поблизости и у вас есть пять минут, один дополнительный рисунок зафиксирует метод. Нарисуйте восемь строк, дайте трем из них общую отметку, сверните строки в сегменты и напишите рядом с ними количество ведер. Обведите ведра над одним. Перерисовка этого по памяти — честная проверка того, владеете ли вы этой идеей.
Все на одном экране
Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.
| концепция | Что он делает |
|---|---|
| Первый шаг | Решите, какие столбцы определяют «то же самое». Это решение выбирает запрос. |
| Дубликат всей строки | Каждый столбец соответствует. Обычно это авария при погрузке. Безопасно удалить после маркировки. |
| Дубликат с естественным ключом | Тот же реальный идентификатор, другие столбцы отличаются. Кто-то должен выбрать истинную версию. |
| Тот же человек, разное написание | Точного совпадения не существует. Это разрешение сущностей, отдельный инструмент. |
| Десятисекундный тест | COUNT(*) против COUNT(DISTINCT key). Любой пробел – это количество доп. |
| Список дубликатов | GROUP BY key HAVING COUNT(*) > 1. Список GROUP BY — это ваше определение. |
| Посмотреть полные строки | WHERE key IN (the summary), или присоединитесь к нему снова. В любом случае одни и те же строки. |
| Хранители отметок | ROW_NUMBER() OVER (PARTITION BY key ORDER BY rule). Копия 1 сохраняется, копия выше 1 является дополнительной. |
| Зачем отмечать, а не удалять | Маркировка двусторонняя, и правило хранителя остается видимым. Удаление — ни то, ни другое. |
| Шаблон удаления | Держать MIN(rowid) за ключ, остальные удалите. SELECT его первым. Сохраните исходную таблицу. |
| Дубликатов не найдено | Прежде чем объявить таблицу чистой, проверьте другое определение. |
| В любом случае сообщите о двойном учете | Проверьте клавишу соединения с обеих сторон. Объединяет несколько строк, каждая из которых выглядит чистой по отдельности. |
| НУЛЕВЫЕ ключи | Все значения NULL делят одну корзину. В этом подсчете отсутствуют данные, а не дубликаты. |
| Во время миграции | Дубликаты являются свидетельством исходной системы. Зарегистрируйте их перед очисткой. |
COUNT(*) против COUNT(DISTINCT key) на каждой таблице, прежде чем доверять числу из нее. Десять секунд, и именно в этом разница между поиском дубликатов и тем, чтобы их нашел ваш заинтересованный человек. Если поиск дубликатов дает сбой, который не описан на этой странице, существует общая шлейф диагностики на предмет застревания.Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. Моим любимым дубликатом, когда-либо найденным, был клиент, который существовал дважды, потому что оба офиса клялись, что зарегистрировали ее первым. Какова самая странная причина появления дубликата в ваших данных, и нашел ли его запрос или это сделал человек?
Ссылки
- Кодд, EF (1970). Реляционная модель данных для крупных общих банков данных. Коммуникации ACM, 13(6), 377–387.
- Бисра К., Лю К., Несбит Дж. К., Салими Ф. и Винн П. Х. (2018). Побуждение к самообъяснению: метаанализ. Обзор педагогической психологии, 30(3), 703–725.
- Рёдигер, Х.Л., и Карпике, Дж.Д. (2006). Улучшенное обучение с помощью тестов: прохождение тестов на память улучшает долгосрочное запоминание. Психологическая наука, 17(3), 249–255.
SQL для аналитиков составляет 458 страниц, GROUP BY и HAVING используются для поиска того, что дублируется, прежде чем оно достигнет общего количества.
SQL для аналитиков, 19 $ →тот Курс SQL запускает SQLite в браузере, поэтому вы можете заполнять дубликаты, выслеживать их и наблюдать за изменением количества по ходу работы. Рядом с ним лежат рабочие примеры, карточки и пробный экзамен. Ничего устанавливать, учетная запись не требуется.
Откройте набор SQL →