Аналитикам, занимающимся самыми разными делами, часто приходится решать подобные задачи. Но при их решении можно столкнуться с некоторыми сложностями. Например:
- Существует множество различных функций, которые либо делают одно и то же, либо работают схожим образом, но отличаются в некоторых деталях. Сложно выбрать именно ту функцию, которая нужна при решении конкретной задачи.
- В разных диалектах SQL имеются различные функции. Поэтому функция, которая подошла бы при работе с Postgres, может оказаться совсем неподходящей при работе с MySQL.
- Столбец в базе данных может иметь неподходящий формат или тип данных. Поэтому придётся потратить некоторое время на преобразование данных и на приведение их в подходящий вид. Это тоже может усложнить задачу.
Давайте начнём с самого простого. А именно — рассмотрим один SQL-пример и разберём несколько функций, которые можно использовать для разбора дат на составные части. Подобными делами часто приходится заниматься тому, кто работает в сфере Data Science. А вот — видеодемонстрация приёмов работы с датами в SQL.
Работа с датами на Data Science-собеседованиях
Рассмотрим этот вопрос:
Вам предоставлен набор данных, собранный по результатам санитарных проверок. Нужно подсчитать ежегодное количество проверок, в ходе которых были выявлены нарушения в кафе 'Roxanne Cafe'
. Если в ходе проверки было выявлено нарушение, то в столбце 'violation_id'
будет присутствовать некое значение. Выведите количество таких проверок с группировкой по годам в нисходящем порядке.
Данные содержатся в таблице sf_restaurant_health_violations
, в которой имеются следующие поля:
Задача это довольно простая, поэтому я не буду детально разбирать её решение. Вместо этого я уделю особое внимание тому, что имеет отношение к работе с датами.
После того, как мы поняли, что от нас требуется, рассмотрим подход к решению подобных задач.
Подход к решению задач по работе с базами данных
- Посмотрим на данные.
- Выберем столбцы, данные которых нужны для ответа на вопрос.
- Теперь, ориентируясь в данных, мы можем выбрать те столбцы, которые, как нам известно, помогут нам ответить на вопрос.
- В нашем случае это будут столбцы
inspection_date
,violation_id
,business_name
.
- Примем решение о том, как должен выглядеть ответ на вопрос.
- Ещё один действительно важный этап решения подобных задач заключается в представлении себе того, как должны выглядеть выходные данные, получаемые при взаимодействии с базой данных, и того, как должно выглядеть решение задачи. В частности, речь идёт о том, какие столбцы понадобится включить в выходные данные.
- В нашем случае это — год из столбца
inspection_date
и число проверок, которое будет представлено в видеcount()
. - Известно, что название кафе и идентификатор нарушения будут использованы для фильтрации данных, а это значит, что они пригодятся при составлении выражения
WHERE
.
Я, решая подобные задачи, предпочитаю делать всё поэтапно, реализуя на каждом шаге что-то одно и тестируя получившийся код. И, собственно говоря, вам я советую тоже так поступать, когда вы, решая какую-то рабочую задачу, взаимодействуете с настоящей базой данных. Этот подход можно использовать не только на работе, но и на собеседовании. Так вы сможете в подробностях раскрыть тому, кто проводит собеседование, ход ваших размышлений.
▍Фильтрация данных
Для начала применим фильтр. Обычно я начинаю работу именно с этого шага.
SELECT *
FROM sf_restaurant_health_violations
WHERE business_name = 'Roxanne Cafe'
AND violation_id IS NOT NULL
▍Получение необходимых выходных данных
Теперь попытаемся получить необходимые нам выходные данные. Может, для извлечения сведений о годе, в котором проводилась проверка, стоит воспользоваться конструкцией вида
EXTRACT(year FROM request_date::DATE)
, которая описана здесь и возвращает значение двойной точности?
SELECT EXTRACT (YEAR
FROM inspection_date) AS YEAR,
count(*) AS n_inspections
FROM sf_restaurant_health_violations
WHERE business_name = 'Roxanne Cafe'
AND violation_id IS NOT NULL
GROUP BY YEAR
ORDER BY YEAR ASC
Но результаты работы этого запроса нас не устроят, так как столбец
inspection_date
, на самом деле, хранит не дату. Это — объект, который, в соответствии с особенностями платформы, хранит либо текстовые данные, либо данные типа varchar
. Для работы этой платформы используется Python, поэтому кое-что из того, что можно тут увидеть, имеет отношение к Python. Со временем мы попытаемся с этим справиться.
Приведём столбец к соответствующему типу, используя либо конструкцию с двумя двоеточиями, либо функцию приведения типов. Два двоеточия — это, в сущности, и есть функция приведения типов, которой можно пользоваться в Postgres. А функции приведения типов могут использоваться и в других диалектах SQL вроде MySQL.
Допустимо, кроме того, поместить YEAR
в выражение GROUP BY
, так как выражение SELECT
выполняется первым. В результате интерпретатору, после выполнения этого выражения, уже будет известно о том, что в запросе имеется столбец с именем YEAR
:
SELECT EXTRACT (YEAR
FROM cast(inspection_date as DATE)) AS YEAR,
count(*) AS n_inspections
FROM sf_restaurant_health_violations
WHERE business_name = 'Roxanne Cafe'
AND violation_id IS NOT NULL
GROUP BY YEAR
ORDER BY YEAR ASC
▍Важное замечание
Часто нужно изолировать части даты в выражении
SELECT
— так же, как мы сделали это в нашем примере. Но нередко встречается и необходимость изоляции частей дат в выражении WHERE
, что нужно в том случае, когда требуется фильтровать данные. Если нам, например, нужно отобрать только данные, относящиеся к 2015 году, это можно сделать, воспользовавшись соответствующим фильтром:
SELECT EXTRACT (YEAR
FROM cast(inspection_date as DATE)) AS YEAR,
count(*) AS n_inspections
FROM sf_restaurant_health_violations
WHERE business_name = 'Roxanne Cafe'
AND violation_id IS NOT NULL
AND EXTRACT (YEAR FROM cast(inspection_date as DATE)) = 2015
GROUP BY YEAR
ORDER BY YEAR ASC
▍Ещё одно важное замечание
Разные диалекты SQL, например — MySQL, Postgres, Oracle и MS SQL Server, обладают различными функциями для работы с датами. Например, функция
EXTRACT()
имеется в большинстве диалектов.
Если вы пользуетесь Postgres, это значит, что вам доступна функция date_part()
, которая похожа на EXTRACT
.
SELECT date_part ('YEAR', inspection_date :: DATE) AS YEAR,
count(*) AS n_inspections
FROM sf_restaurant_health_violations
WHERE business_name = 'Roxanne Cafe'
AND violation_id IS NOT NULL
GROUP BY YEAR
ORDER BY YEAR ASC
В MySQL можно пользоваться функцией
YEAR()
.
В других диалектах чего-то наподобие date_part()
может и не быть, но в них имеется что-то своё со схожими возможностями. Поэтому не удивляйтесь, если встретитесь с разными функциями, делающими одно и то же.
Итоги
Выбор части даты из соответствующего поля — это обычная задача, с которой сталкиваются аналитики и дата-сайентисты. Помимо представления неких данных с разбивкой по годам, может возникнуть необходимость в разбивке их по месяцам, или — по годам и по месяцам.
Для решения подобных задач тоже существует множество функций. То, какими именно функциями можно пользоваться, зависит от конкретного SQL-диалекта. Я обнаружил, что это в работе с датами вызывает больше всего неприятностей. Многие, включая меня, путаются, попадая в ситуацию, когда существует множество функций, которые, как кажется, решают одну и ту же задачу.
Как вы работаете с датами, создавая SQL-запросы?
Комментариев нет:
Отправить комментарий