Distinct. Удаление дубликатов
Оператор DISTINCT
в SELECT запросах используется для удаления дублирующихся строк из выборки. В общем виде запрос выглядит следующим образом:
select distinct col_name1, col_name2, col_name3, ...
from table_name
select distinct col_name1, col_name2, col_name3, ...
from table_name
Подготовка данных
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
выглядит следующим образом:
ID | FIRST_NAME | LAST_NAME | BD | JOB |
---|---|---|---|---|
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 | Бухгалтер |
Удаление дупликатов из одной колонки
Теперь получим список имён сотрудников:
select first_name
from employees
select first_name
from employees
FIRST_NAME
------------
Василий
Александр
Евгения
Владимир
Владимир
Ирина
Ирина
Евгения
FIRST_NAME
------------
Василий
Александр
Евгения
Владимир
Владимир
Ирина
Ирина
Евгения
Можно заметить, что некоторые имена(Ирина, Владимир, Евгения) дублируются. Теперь посмотрим, какой мы получим результат, если применим оператор DISTINCT
:
select distinct first_name
from employees
select distinct first_name
from employees
FIRST_NAME
-----------
Евгения
Василий
Александр
Владимир
Ирина
FIRST_NAME
-----------
Евгения
Василий
Александр
Владимир
Ирина
Теперь каждое имя повторяется только один раз.
DISTINCT учитывает все колонки в строке
Теперь применим DISTINCT
к выборке из нескольких строк:
select distinct id, first_name
from employees
select distinct id, first_name
from employees
ID | FIRST_NAME |
---|---|
7 | Ирина |
8 | Евгения |
5 | Владимир |
6 | Ирина |
2 | Александр |
1 | Василий |
3 | Евгения |
4 | Владимир |
Теперь дубликаты имен остались, и дело здесь в том, что DISTINCT
удаляет дублирующиеся строки, а они в данном случае уникальны, так как, несмотря на повторяющиеся имена сотрудников, каждая строка имеет уникальное значение в колонке ID
.
NULL учитывается
Distinct
учитывает NULL значения также, как и все остальные:
select distinct last_name
from employees
select distinct last_name
from employees
LAST_NAME
------------
Цветочкина
Столяров
Петров
Васина
Сидоров
Иванова
-
Иванов
LAST_NAME
------------
Цветочкина
Столяров
Петров
Васина
Сидоров
Иванова
-
Иванов
Строка с пустой фамилией, как и все остальные, попала в выборку.
DISTINCT с агрегатными функциями
Когда DISTINCT
используется с агрегатными функциями, дупликаты колонок не учитываются.
Для начала посчитаем количество не пустых имён в таблице:
select count(first_name) cd
from employees
select count(first_name) cd
from employees
CD
---
8
CD
---
8
А теперь посчитаем количество уникальных имён в таблице:
select count(distinct first_name) cd
from employees
select count(distinct first_name) cd
from employees
CD
---
5
CD
---
5
Параллельно количеству уникальных имён мы можем получить и количество всех имён:
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
Получим количество должностей сотрудников и сгруппируем их по именам:
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_NAME | JOB_CNT |
---|---|
Евгения | 2 |
Василий | 1 |
Александр | 1 |
Владимир | 2 |
Ирина | 2 |
Здесь должно быть всё понятно — у нас есть по два сотрудника с именем «Евгения», «Владимир» и «Ирина», у которых указаны значения в поле JOB
.
А теперь посчитаем, сколько уникальных наименований должностей приходится на каждое имя:
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_NAME | JOB_CNT |
---|---|
Евгения | 1 |
Александр | 1 |
Василий | 1 |
Владимир | 2 |
Ирина | 2 |
Обратить следует внимание на строку с именем «Евгения» - теперь там указано число 1. Это потому, что в нашей таблице у сотрудников с таким именем одна и та же должность — бухгалтер, и количество уникальных значений здесь будет равно 1. Вообще, здесь следует помнить о порядке выполнения запроса — группировка выполняется первее, чем выборка значений, а значит и оператор DISTINCT
будет применяться к уже сгруппированному набору данных.