При использовании в SELECT-запросах операторов OR и AND можно легко ошибиться в составлении логического выражения. И проблема таких ошибок в том, что запрос выполняется как будто ошибки нет, но данные выводятся неверные.
Если у вас мало опыта в написании таких SQL-запросов, то можно легко не заметить проблему. Давайте разберем несколько популярных ошибок.
Работать будем с таблицей team, которая содержит список разработчиков:
id | first_name | last_name | language | level |
---|---|---|---|---|
1 | Дмитрий | Васильев | python | junior |
2 | Валерий | Юрьев | php | middle |
3 | Андрей | Ушаков | python | senior |
4 | Петр | Кузуб | javascript | junior |
5 | Наталья | Кузнецова | python | middle |
6 | Руслан | Исаков | php | senior |
7 | Сергей | Медведев | php | junior |
8 | Алексей | Борисов | python | middle |
9 | Валерия | Маркова | javascipt | senior |
Каждый разработчик оценивается project-менеджером по двум критериям, после чего он отбирает программиста на тот или иной проект. Основные критерии — это уровень владения технологией: junior, middle или senior, и базовый язык программирования, на котором пишет разработчик.
Нам как менеджерам поступает заявка на разработку проекта и на основании этой заявки мы должны подобрать необходимых разработчиков.
Представьте, что сейчас нас интересуют только мидллы и сеньоры. Напишем SQL-запрос:
SELECT * FROM team WHERE level = 'middle' AND level = 'senior'
После выполнения запроса мы увидим пустую таблицу.
id | first_name | last_name | language | level |
---|
И тут мы столкнулись с первой ошибкой. В нашем запросе база данных по очереди берет строку и проверяет находится ли в колонке level значение "middle" И значение "senior". Оператор AND — это условие одновременности. То есть исходя из нашего запроса в поле level должно находится одновременно и "middle", и "senior", что невозможно, так разработчик может быть только одного уровня.
Почему мы допустили такую ошибку? И дело в формулировках, когда я озвучивал задание, то сказал, что нас интересуют только мидлы И сеньоры. Это правильное выражение с точки зрения русского языка, но неверное с точки зрения логики. Чтобы получить верный результат надо чтобы поле level содержало ИЛИ "middle", ИЛИ "senior". Поэтому заменим AND на OR:
SELECT * FROM team WHERE level = 'middle' OR level = 'senior'
Теперь разработчики подобраны верно:
id | first_name | last_name | language | level |
---|---|---|---|---|
2 | Валерий | Юрьев | php | middle |
3 | Андрей | Ушаков | python | senior |
5 | Наталья | Кузнецова | python | middle |
6 | Руслан | Исаков | php | senior |
8 | Алексей | Борисов | python | middle |
9 | Валерия | Маркова | javascipt | senior |
Такую ошибку легко допустить, если в формулировке задачи стоит союз И. Но не стоит его напрямую переносить в SQL-запрос. Возможно здесь кроется ошибка, особенно если мы сравниваем с одним полем.
Давайте рассмотрим другой случай. На этот раз нам нужно разработать бэкенд для сайта. И для этого нам нужны все Python и PHP разработчики. Напишем:
SELECT * FROM team
WHERE basic_language = 'Python' OR basic_language = "PHP"
Обратите внимание, что нужны нам все Python И PHP разработчики, но пишем мы OR. После выполнения запроса мы получим такую таблицу:
id | first_name | last_name | language | level |
---|---|---|---|---|
1 | Дмитрий | Васильев | python | junior |
2 | Валерий | Юрьев | php | middle |
3 | Андрей | Ушаков | python | senior |
5 | Наталья | Кузнецова | python | middle |
6 | Руслан | Исаков | php | senior |
7 | Сергей | Медведев | php | junior |
8 | Алексей | Борисов | python | middle |
В итоговую таблицу попали и те и другие программисты. Отлично. Но давайте расширим условие. Скажем, нам нужны только middle разработчики на этих языках. Добавим:
SELECT * FROM team
WHERE
basic_language = 'Python' OR
basic_language = "PHP" AND
level = "middle"
После выполнения мы получим такую таблицу:
id | first_name | last_name | language | level |
---|---|---|---|---|
1 | Дмитрий | Васильев | python | junior |
2 | Валерий | Юрьев | php | middle |
3 | Андрей | Ушаков | python | senior |
5 | Наталья | Кузнецова | python | middle |
8 | Алексей | Борисов | python | middle |
И результат довольно странный, в итоговой таблице присутствуют также джуниоры и сеньоры. И дело в том, что операторы OR и AND имеют приоритет выполнения и у AND он выше. Поэтому в первую очередь база данных находит всех мидлов, которые пишут на PHP. Такому условию соответствует только одна запись, а далее срабатывает оператор OR, который добавляет всех Python-разработчиков.
То есть мы ищем ИЛИ мидлов на PHP, ИЛИ Python-разработчиков без указания их уровня владения языком. Поэтому в таблицу также попали джуниоры и сеньоры.
Чтобы решить поставленную задачу, нужно четко расставить приоритет с помощью скобок. Как в математике. Поместим скобки справа и слева от OR:
SELECT * FROM team
WHERE
(basic_language = 'Python' OR
basic_language = "PHP") AND
level = "middle"
Теперь в таблице остались только мидлы на Python и PHP:
id | first_name | last_name | language | level |
---|---|---|---|---|
2 | Валерий | Юрьев | php | middle |
5 | Наталья | Кузнецова | python | middle |
8 | Алексей | Борисов | python | middle |
Каждый раз когда в одном запросе встречаются OR и AND, обращайте внимание на порядок выполнения, иначе можно легко получить неверные данные.
Вообще, даже не так, каждый раз когда у вас есть в запросе есть OR и AND применяйте скобки для расстановки приоритетов, даже если уверены в своём коде.
Такая практика позволит избежать неприятных логических ошибок.