SQL (ANSI)ПрограммированиеРазработчик SQL / Инженер данных

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

Проходите собеседования с ИИ помощником Hintsage

Ответ на вопрос

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

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

Критически, спецификация диапазона ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW обеспечивает детерминированное накопление по строкам. Если строгая обработка связей необходима — когда все записи, имеющие пограничное значение, должны быть включены или исключены как единое целое — RANGE заменит ROWS. Финальная фильтрация должна происходить в внешнем запросе, так как оконные функции логически вычисляются после условия WHERE.

WITH ranked_products AS ( SELECT product_id, product_name, annual_revenue, SUM(annual_revenue) OVER ( ORDER BY annual_revenue DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS cumulative_revenue, SUM(annual_revenue) OVER () AS total_revenue FROM inventory ), pareto_subset AS ( SELECT product_id, product_name, annual_revenue, CAST(cumulative_revenue AS DECIMAL) / total_revenue AS cumulative_pct FROM ranked_products ) SELECT product_id, product_name, annual_revenue, cumulative_pct FROM pareto_subset WHERE cumulative_pct <= 0.80;

Ситуация из жизни

Национальный розничный продавец электроники столкнулся с растущими затратами во время квартальных инвентаризаций, что требовало изоляции высокоценных SKU, представляющих 80% от общего капитала склада (50 миллионов долларов по 40,000 предметам), чтобы приоритизировать подсчет циклов.

Решение 1: Извлечение из таблицы включало в себя экспорт аналитиками CSV файлов в Excel, сортировку по стоимости единицы и ручное суммирование до достижения порога. Плюсы не требовали времени на разработку. Минусы включали сбои приложения с большими наборами данных, требования к пересчету каждый час и предотвратили реальную интеграцию с системой управления складом.

Решение 2: Расчет на уровне приложения использовал скрипт Python, который потоково обрабатывал строки и поддерживал бегущий аккумулятор. Плюсы обеспечивали гибкую логику и простоту отладки. Минусы приводили к значительной задержке сети при передаче миллионов строк, блокировке исполнения однопоточного анализа данных и ограничениями по памяти на клиентской машине.

Решение 3: Подход на основе наборов ANSI SQL реализовало запрос с оконной функцией непосредственно в хранилище PostgreSQL. Плюсы включали задержку на уровне миллисекунд, исключение перемещения данных и автоматическое обновление с ночными обновлениями. Минусы требовали углубленных знаний SQL для обслуживания.

Выбранное решение и результат: Решение 3 было развернуто как представление, выявив, что лишь 12% SKU составили 80% стоимости. Объем аудита был сокращен на 88%, сэкономив 340 трудозатрат в квартал, при этом удерживая полное покрытие материальной стоимости.

Что часто упускают кандидаты


Как выбор между спецификациями диапазона ROWS и RANGE влияет на порог 80%, когда присутствуют дубликаты значений?

RANGE рассматривает соседние строки с идентичными ORDER BY значениями как одну группу; если граница 80% попадает в пределах связи, RANGE включает всю группу, что потенциально превышает 80%. ROWS обрабатывает физические смещения независимо от связей, что может разбить логическую бизнес-единицу. Кандидаты часто упускают, что ANSI SQL позволяет явно настраивать это поведение; для финансовой отчетности RANGE обеспечивает, чтобы постоянные периоды не разбивались, в то время как ROWS предлагает более точную настройку для отдельных элементов.


Почему расчет накопительного процента должен выполняться в производной таблице или CTE, а не напрямую в условии WHERE?

Оконные функции логически оцениваются во время фазы SELECT, которая происходит после того, как условие WHERE фильтрует строки. Попытка фильтрации по cumulative_revenue / total_revenue <= 0.8 напрямую в WHERE приводит к синтаксической ошибке, так как результат оконной функции ещё не материализован. Кандидаты часто сталкиваются с трудностями в понимании логического порядка обработки ANSI SQL: FROMWHEREGROUP BYHAVINGWINDOWSELECTORDER BY. Решение требует вложенности для вычисления оконной функции во внутреннем запросе, а затем фильтрации результирующего столбца во внешнем запросе.


Как бы вы оптимизировали этот запрос, если таблица инвентаризации содержит миллиарды строк, и 80% подмножества, как ожидается, будет очень маленьким?

Кандидаты часто упускают оптимизационный шаблон Top-N. Вместо того чтобы вычислять оконную функцию по всей таблице, предварительный фильтр с использованием подзапроса с DENSE_RANK() или NTILE() может ограничить вычисление окна наиболее значительными кандидатами. Альтернативно, использование PARTITION BY, если анализ сегментирован по категории, предотвращает полные сканирования таблицы. Понимание того, что оконные функции принуждают к операции сортировки, и что индексирование по столбцу выручки по убыванию может исключить затраты на сортировку, критично для масштаба.