Skip to content

Distinct. Удаление дубликатов

Оператор DISTINCT в SELECT запросах используется для удаления дублирующихся строк из выборки. В общем виде запрос выглядит следующим образом:

sql
select distinct col_name1, col_name2, col_name3, ...
from table_name
select distinct col_name1, col_name2, col_name3, ...
from table_name

Подготовка данных

sql
create table employees(
    id number not null,
    first_name varchar2(50 char) not null,
    last_name varchar2(100 char),
    bd date not null,
    job varchar2(100)
);

insert into employees
values(1, 'Василий', 'Петров',
    to_date('07.10.1990', 'dd.mm.yyyy'), 'Машинист');

insert into employees
values(2, 'Александр', 'Сидоров',
    to_date('18.07.1980', 'dd.mm.yyyy'), 'Бухгалтер');

insert into employees
values(3, 'Евгения', 'Цветочкина',
    to_date('18.07.1978', 'dd.mm.yyyy'), 'Бухгалтер');

insert into employees
values(4, 'Владимир', 'Столяров',
    to_date('18.07.1977', 'dd.mm.yyyy'), 'Слесарь');

insert into employees
values(5, 'Владимир', 'Иванов',
    to_date('01.10.1987', 'dd.mm.yyyy'), 'Сторож');

insert into employees
values(6, 'Ирина', 'Васина',
    to_date('20.03.1962', 'dd.mm.yyyy'), 'Специалист отдела кадров');

insert into employees
values(7, 'Ирина', 'Иванова',
    to_date('31.12.1990', 'dd.mm.yyyy'), 'Арт-директор');

insert into employees
values(8, 'Евгения', NULL,
    to_date('18.07.1978', 'dd.mm.yyyy'), 'Бухгалтер');
create table employees(
    id number not null,
    first_name varchar2(50 char) not null,
    last_name varchar2(100 char),
    bd date not null,
    job varchar2(100)
);

insert into employees
values(1, 'Василий', 'Петров',
    to_date('07.10.1990', 'dd.mm.yyyy'), 'Машинист');

insert into employees
values(2, 'Александр', 'Сидоров',
    to_date('18.07.1980', 'dd.mm.yyyy'), 'Бухгалтер');

insert into employees
values(3, 'Евгения', 'Цветочкина',
    to_date('18.07.1978', 'dd.mm.yyyy'), 'Бухгалтер');

insert into employees
values(4, 'Владимир', 'Столяров',
    to_date('18.07.1977', 'dd.mm.yyyy'), 'Слесарь');

insert into employees
values(5, 'Владимир', 'Иванов',
    to_date('01.10.1987', 'dd.mm.yyyy'), 'Сторож');

insert into employees
values(6, 'Ирина', 'Васина',
    to_date('20.03.1962', 'dd.mm.yyyy'), 'Специалист отдела кадров');

insert into employees
values(7, 'Ирина', 'Иванова',
    to_date('31.12.1990', 'dd.mm.yyyy'), 'Арт-директор');

insert into employees
values(8, 'Евгения', NULL,
    to_date('18.07.1978', 'dd.mm.yyyy'), 'Бухгалтер');

Итого, таблица employees выглядит следующим образом:

IDFIRST_NAMELAST_NAMEBDJOB
1ВасилийПетров07-OCT-90Машинист
2АлександрСидоров18-JUL-80Бухгалтер
3ЕвгенияЦветочкина18-JUL-78Бухгалтер
4ВладимирСтоляров18-JUL-77Слесарь
5ВладимирИванов01-OCT-87Сторож
6ИринаВасина20-MAR-62Специалист отдела кадров
7ИринаИванова31-DEC-90Арт-директор
8Евгения-18-JUL-78Бухгалтер

Удаление дупликатов из одной колонки

Теперь получим список имён сотрудников:

sql
select first_name
from employees
select first_name
from employees
FIRST_NAME
------------
Василий
Александр
Евгения
Владимир
Владимир
Ирина
Ирина
Евгения
FIRST_NAME
------------
Василий
Александр
Евгения
Владимир
Владимир
Ирина
Ирина
Евгения

Можно заметить, что некоторые имена(Ирина, Владимир, Евгения) дублируются. Теперь посмотрим, какой мы получим результат, если применим оператор DISTINCT:

sql
select distinct first_name
from employees
select distinct first_name
from employees
FIRST_NAME
-----------
Евгения
Василий
Александр
Владимир
Ирина
FIRST_NAME
-----------
Евгения
Василий
Александр
Владимир
Ирина

Теперь каждое имя повторяется только один раз.

DISTINCT учитывает все колонки в строке

Теперь применим DISTINCT к выборке из нескольких строк:

sql
select distinct id, first_name
from employees
select distinct id, first_name
from employees
IDFIRST_NAME
7Ирина
8Евгения
5Владимир
6Ирина
2Александр
1Василий
3Евгения
4Владимир

Теперь дубликаты имен остались, и дело здесь в том, что DISTINCT удаляет дублирующиеся строки, а они в данном случае уникальны, так как, несмотря на повторяющиеся имена сотрудников, каждая строка имеет уникальное значение в колонке ID.

NULL учитывается

Distinct учитывает NULL значения также, как и все остальные:

sql
select distinct last_name
from employees
select distinct last_name
from employees
LAST_NAME
------------
Цветочкина
Столяров
Петров
Васина
Сидоров
Иванова
-
Иванов
LAST_NAME
------------
Цветочкина
Столяров
Петров
Васина
Сидоров
Иванова
-
Иванов

Строка с пустой фамилией, как и все остальные, попала в выборку.

DISTINCT с агрегатными функциями

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

Для начала посчитаем количество не пустых имён в таблице:

sql
select count(first_name) cd
from employees
select count(first_name) cd
from employees
CD
---
8
CD
---
8

А теперь посчитаем количество уникальных имён в таблице:

sql
select count(distinct first_name) cd
from employees
select count(distinct first_name) cd
from employees
CD
---
5
CD
---
5

Параллельно количеству уникальных имён мы можем получить и количество всех имён:

sql
select count(distinct first_name) cd,
       count(first_name) cnt
from employees
select count(distinct first_name) cd,
       count(first_name) cnt
from employees
CD | CNT
---------
5  |  8
CD | CNT
---------
5  |  8

Результат наглядно демонстрирует тот факт, что здесь DISTINCT применялся только к той функции, в которой был указан.

DISTINCT и GROUP BY

Получим количество должностей сотрудников и сгруппируем их по именам:

sql
select first_name, count(job) job_cnt
from employees
group by first_name
select first_name, count(job) job_cnt
from employees
group by first_name
FIRST_NAMEJOB_CNT
Евгения2
Василий1
Александр1
Владимир2
Ирина2

Здесь должно быть всё понятно — у нас есть по два сотрудника с именем «Евгения», «Владимир» и «Ирина», у которых указаны значения в поле JOB.

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

sql
select first_name, count(distinct job) job_cnt
from employees
group by first_name
select first_name, count(distinct job) job_cnt
from employees
group by first_name
FIRST_NAMEJOB_CNT
Евгения1
Александр1
Василий1
Владимир2
Ирина2

Обратить следует внимание на строку с именем «Евгения» - теперь там указано число 1. Это потому, что в нашей таблице у сотрудников с таким именем одна и та же должность — бухгалтер, и количество уникальных значений здесь будет равно 1. Вообще, здесь следует помнить о порядке выполнения запроса — группировка выполняется первее, чем выборка значений, а значит и оператор DISTINCT будет применяться к уже сгруппированному набору данных.