6 простых способов использовать передовые методы SQL

SQL является основой анализа и преобразования данных, но в некоторых случаях требуется более сложный подход. Вот тут-то и пригодятся передовые методы SQL.

1. Оконные функции для сложных вычислений

Оконные функции подобны швейцарскому армейскому ножу SQL — универсальны, мощны и всегда готовы помочь вам справиться с любой задачей обработки данных. Они позволяют выполнять сложные вычисления по строкам результирующего набора, которые связаны с текущей строкой.

Текущий итог

SELECT 
  category,
  SUM(revenue) OVER (PARTITION BY category ORDER BY date) AS running_total
FROM sales_data;

В этом примере функция SUM используется для расчёта общей выручки по каждой товарной категории. Предложение OVER используется для определения окна, в пределах которого будет вычисляться функция. Предложение PARTITION BY используется для группировки данных по категориям продуктов, а предложение ORDER BY используется для сортировки данных по дате. Результатом является таблица, в которой показан текущий общий доход по каждой товарной категории.

Скользящая средняя

SELECT
  date,
  AVG(amount) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
  sales
ORDER BY
  date;

Здесь функция AVG используется с предложением OVER для вычисления скользящего среднего. Предложение ORDER BY указывает, что данные должны быть упорядочены по дате, а предложение ROWS BETWEEN 6 PREVIOUS AND CURRENT ROW указывает, что среднее значение должно быть рассчитано для текущей строки и шести предыдущих строк.

Верхние n строк

SELECT
  date,
  region,
  amount,
  DENSE_RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank
FROM
  sales
WHERE
  rank <= 3;

Здесь функция DENSE_RANK используется с предложением OVER для присвоения ранга каждой строке на основе объёма продаж в порядке убывания. В предложении PARTITION BY указано, что расчёт должен выполняться отдельно для каждого региона, а в предложении ORDER BY указано, что расчёт должен выполняться в порядке увеличения объема продаж.

Обратите внимание, что мы используем DENSE_RANK, а не RANK. RANK присваивает уникальный номер каждому отдельному значению в наборе данных, в то время как DENSE_RANK присваивает последовательные номера каждому уникальному значению, независимо от того, сколько раз оно появляется в наборе данных.

Процентили

SELECT
  student_id,
  score,
  PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY score) OVER (PARTITION BY student_id) AS percentile_90
FROM
  scores;

Здесь функция PERCENTILE_CONT используется с предложением OVER для вычисления 90-го процентильного балла для каждого учащегося. PARTITION BY указывает, что расчёт должен выполняться отдельно для каждого учащегося, а пункт ORDER BY указывает, что расчёт должен выполняться в порядке получения баллов. Предложение WITHIN GROUP указывает, что вычисление процентиля должно выполняться внутри группы баллов для каждого учащегося.

Оконные функции в SQL – это мощный инструмент, позволяющий выполнять сложные вычисления по строкам результирующего набора. С помощью всего нескольких строк кода вы можете рассчитать текущие итоги, процентили и другую причудливую статистику, которая вызовет зависть у всех ваших коллег по SQL.

2. Оконные функции для сравнения строк

Оконные функции не только отлично подходят для сложных вычислений, но и удобны для сравнения и группировки данных, что делает их идеальным инструментом для многозадачных пользователей, занимающихся анализом данных.

Получить предыдущее или следующее значение

SELECT
  order_date,
  order_total,
  LAG(order_total) OVER (ORDER BY order_date) AS previous_order_total,
  order_total - LAG(order_total) OVER (ORDER BY order_date) AS order_total_diff
FROM
  orders;

Этот запрос вычисляет разницу между общим объемом текущего заказа и общим объёмом предыдущего заказа для каждого заказа, используя функцию LAG и предложение ORDER BY.

Получить первое или последнее значение

SELECT
  customer_id,
  order_date,
  order_total,
  FIRST_VALUE(order_total) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_total
  LAST_VALUE(order_total) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_total
FROM
  orders;

Этот запрос получает общую сумму первого и последнего заказов для каждого клиента, используя функции FIRST_VALUE и LAST_VALUE соответственно, с предложениями PARTITION BY и ORDER BY для определения окна.

Сегменты гистограмм

SELECT
  product_id,
  price,
  WIDTH_BUCKET(price, 0, 100, 4) AS bucket_number
FROM
  products;

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

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

3. Общие табличные выражения и подзапросы

Непрекращающиеся дебаты в сообществе SQL по поводу распространённых табличных выражений и подзапросов так же горячо оспариваются, как и извечный спор о том, является ли “Крепкий орешек” рождественским фильмом или нет. Независимо от того, предпочитаете ли вы элегантность CTE или универсальность подзапросов, выбор в конечном счёте сводится к тому, что лучше всего подходит для ваших конкретных потребностей в данных.

“Крепкий орешек” – это рождественский фильм? Скорее всего, нет. Я всё ещё смотрю его каждый праздничный сезон? Да, смотрю! Вам не нужно выбирать между CTE и подзапросами, просто когда их использовать, чтобы ваши запросы выполнялись как у Джона Макклейна, эффективно и неостановимо (или, по крайней мере, не застревали в вентиляционной шахте).

CTEs

WITH revenue_by_category AS (
  SELECT category, SUM(revenue) AS total_revenue
  FROM sales_data
  GROUP BY category
)
SELECT 
  category,
  total_revenue,
  total_revenue / (SELECT SUM(total_revenue) FROM revenue_by_category) AS revenue_share
FROM revenue_by_category

CTE работают в пределах ограниченной области и обеспечивают большую читаемость и возможность повторного использования, что может облегчить понимание запросов и их обслуживание.

Подзапросы

SELECT *
FROM customers
WHERE customer_id IN (
  SELECT customer_id
  FROM orders
  GROUP BY customer_id
  ORDER BY COUNT(*) DESC
  LIMIT 10
)

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

CTE и подзапросы – это мощные функции SQL, которые могут помочь вам выполнять сложные запросы. Понимание того, когда использовать каждый из них, поможет вам писать более эффективные SQL-запросы.

4. Рекурсивные общие табличные выражения (CTEs)

Петли? В SQL? Еще бы! С помощью рекурсивных общих табличных выражений (CTEs) вы можете создавать циклы, которые позволяют исследовать иерархические структуры данных, такие как деревья и графики, мощным и гибким способом. Вызывая запрос внутри самого себя, вы можете разбить сложные задачи анализа данных на более мелкие, более управляемые части и повторять их до тех пор, пока не получите нужную информацию.

Вот пример рекурсивного CTE, который вычисляет общую стоимость продукта и всех его компонентов:

WITH RECURSIVE product_cost AS (
  SELECT id, cost, id AS component_id
  FROM products
  WHERE id = 1
  UNION ALL
  SELECT products.id, products.cost, product_cost.component_id
  FROM products
  JOIN product_components ON products.id = product_components.product_id
  JOIN product_cost ON product_components.component_id = product_cost.id
)
SELECT component_id, SUM(cost) AS total_cost
FROM product_cost
GROUP BY component_id

В этом примере рекурсивный CTE с именем product_cost используется для вычисления общей стоимости продукта и всех его компонентов. Базовый вариант определяется в первой части CTE, где выбирается стоимость исходного продукта. Рекурсивный случай определён во второй части CTE, где выбирается стоимость компонентов продукта и объединяется с предыдущими результатами. Рекурсия продолжается до тех пор, пока не будут обработаны все компоненты. Наконец, основной запрос вычисляет общую стоимость каждого компонента путем группировки по столбцу component_id.

5. Самостоятельные соединения

Самостоятельные соединения подобны просмотру зеркального отражения ваших данных — по сути, вы присоединяете таблицу к самой себе, чтобы сравнить строки внутри одной и той же таблицы. Это полезно, когда вам нужно проанализировать взаимосвязи между точками данных в рамках одной таблицы.

Чтобы выполнить самостоятельное соединение, вам нужно присвоить таблице другое псевдонимное имя для каждого экземпляра соединения. Это позволяет вам проводить различие между двумя экземплярами одной и той же таблицы при обращении к столбцам в вашей инструкции SELECT.

SELECT e.name AS employee_name, m.name AS manager_name
FROM employees e
JOIN employees m ON e.manager_id = m.employee_id;

В этом примере мы присоединяем таблицу “employees” к самой себе, используя столбец “manager_id” и присоединяясь к столбцу “employee_id”. Результатом этого запроса будет таблица с двумя столбцами: “employee_name” и “manager_name”.

6. PIVOT

Данные могут быть неразберихой, но с помощью функции PIVOT в SQL вы можете привести их в порядок как профессионал. Преобразуя ваши данные на основе строк в столбчатый формат, вы можете увидеть вещи в совершенно новом свете. Просто имейте в виду, что не все диалекты SQL поддерживают это волшебное преобразование, поэтому следите за любыми проблемами совместимости.

Если вы работаете с диалектом SQL, который не поддерживает PIVOT, не волнуйтесь! Вы всё ещё можете достичь аналогичных результатов, используя операторы CASE.

Вот пример того, как вы можете сводить данные с помощью PIVOT:

SELECT *
FROM (
   SELECT category, year, revenue
   FROM sales
) sales
PIVOT (
   SUM(revenue)
   FOR year IN ([2018], [2019], [2020])
) AS sales_pivot;

И вот как вы можете достичь этих результатов, используя операторы CASE:

SELECT
   category,
   SUM(CASE WHEN year = '2018' THEN revenue ELSE 0 END) AS `2018`,
   SUM(CASE WHEN year = '2019' THEN revenue ELSE 0 END) AS `2019`,
   SUM(CASE WHEN year = '2020' THEN revenue ELSE 0 END) AS `2020`
FROM sales
GROUP BY category;

В обоих примерах SQL-запрос выбирает данные из таблицы “sales” и группирует их по столбцу “category”. Затем мы используем функции SUM с операторами PIVOT или CASE для создания трёх новых столбцов, содержащих выручку по каждой категории за каждый из 2018, 2019 и 2020 годов.

Надеюсь, что данная статья оказалась полезной для вас!

+1
2
+1
5
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *