Выяснение совокупного количества пропущенных строк в группе

У меня есть таблица на SQL о годах обучения учеников в школе:

CREATE TABLE myt (
  student_name VARCHAR(50),
  student_year INT
);

INSERT INTO myt (student_name, student_year) VALUES
('john', 2010),
('John', 2011),
('John', 2012),
('John', 2019),
('John', 2020),
('alex', 2005),
('tim', 2000),
('tim', 2000),
('jack', 2020),
('jack', 2024);


 student_name student_year
         john         2010
         John         2011
         John         2012
         John         2019
         John         2020
         alex         2005
          tim         2000
          tim         2000
         jack         2020
         jack         2024

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

Окончательный результат должен выглядеть так:

 student_name student_year total_years missed_years percent_missed
         john         2010           1            0              0
         john         2011           2            0              0
         john         2012           3            0              0
         john         2013           4            1             25
         john         2014           5            2             40
         john         2015           6            3             50
         john         2016           7            4           57.1
         john         2017           8            5           62.5
         john         2018           9            6           66.7
         john         2019          10            6             60
         john         2020          11            6           54.5
         alex         2005           1            0              0
          tim         2000           1            0              0
          tim         2000           1            0              0
         jack         2020           1            0              0
         jack         2021           2            1             50
         jack         2022           3            2           66.7
         jack         2023           4            3             75
         jack         2024           5            3             60

Я попытался сделать следующий подход:

  • Поскольку Netezza имеет очень ограниченные функции SQL (например, не поддерживает рекурсивные или коррелированные запросы или функции генерации последовательностей - я решил вручную создать CTE календаря, который содержит все годы между самым ранним и последним годом)
  • Затем я попытался присоединить CTE этого календаря к исходной таблице.
  • Отсюда я попытался использовать операторы CASE WHEN, чтобы посмотреть, какие годы отсутствуют, и соответствующим образом выполнить вычисления.

Вот мой подход:

WITH calendar_years AS (
    SELECT 2000 AS year UNION ALL
    SELECT 2001 UNION ALL
    SELECT 2002 UNION ALL
    SELECT 2003 UNION ALL
    SELECT 2004 UNION ALL
    SELECT 2005 UNION ALL
    SELECT 2006 UNION ALL
    SELECT 2007 UNION ALL
    SELECT 2008 UNION ALL
    SELECT 2009 UNION ALL
    SELECT 2010 UNION ALL
    SELECT 2011 UNION ALL
    SELECT 2012 UNION ALL
    SELECT 2013 UNION ALL
    SELECT 2014 UNION ALL
    SELECT 2015 UNION ALL
    SELECT 2016 UNION ALL
    SELECT 2017 UNION ALL
    SELECT 2018 UNION ALL
    SELECT 2019 UNION ALL
    SELECT 2020 UNION ALL
    SELECT 2021 UNION ALL
    SELECT 2022 UNION ALL
    SELECT 2023 UNION ALL
    SELECT 2024
),
student_years AS (
  SELECT 
    student_name,
    MIN(student_year) AS min_year,
    MAX(student_year) AS max_year
  FROM myt
  GROUP BY student_name
),
student_calendar AS (
  SELECT 
    s.student_name,
    c.year
  FROM student_years s
  JOIN calendar_years c ON c.year BETWEEN s.min_year AND s.max_year
),
filled_years AS (
  SELECT 
    sc.student_name,
    sc.year,
    CASE WHEN m.student_year IS NULL THEN 1 ELSE 0 END AS is_missing
  FROM student_calendar sc
  LEFT JOIN myt m ON sc.student_name = m.student_name AND sc.year = m.student_year
),
aggregated AS (
  SELECT 
    student_name,
    year,
    SUM(is_missing) OVER (PARTITION BY student_name ORDER BY year) AS missed_years,
    COUNT(*) OVER (PARTITION BY student_name ORDER BY year) AS total_years
  FROM filled_years
)
SELECT 
  student_name,
  year,
  total_years,
  missed_years,
  (missed_years * 1.0 / total_years * 1.0) * 100 AS percent_missed
FROM aggregated
ORDER BY student_name, year;

Окончательный результат выглядит так:

student_name year total_years missed_years percent_missed
     John 2010           1            0        0.00000
     John 2011           2            0        0.00000
     John 2012           3            0        0.00000
     John 2013           4            1       25.00000
     John 2014           5            2       40.00000
     John 2015           6            3       50.00000
     John 2016           7            4       57.14286
     John 2017           8            5       62.50000
     John 2018           9            6       66.66667
     John 2019          10            6       60.00000
     John 2020          11            6       54.54545
     alex 2005           1            0        0.00000
     jack 2020           1            0        0.00000
     jack 2021           2            1       50.00000
     jack 2022           3            2       66.66667
     jack 2023           4            3       75.00000
     jack 2024           5            3       60.00000
      tim 2000           2            0        0.00000
      tim 2000           2            0        0.00000

Имеет ли такой подход смысл для решения этой проблемы?

Кстати, я не знаю правил по этому поводу, но ИМХО лучше задавать вопросы такого рода на CodeReview.

Basil Peace 18.06.2024 03:42
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
6
1
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Имеет ли такой подход смысл для решения этой проблемы?

Учитывая указанные ограничения

Netezza имеет очень ограниченные функции SQL.

Да, ваш подход правильный.

Кроме того, ваш код хорошо отформатирован и легко читается.

Ваш код слегка раздут. Лично я бы включил student_calendar и aggregated CTE в filled_years. В данном случае это не улучшит производительность, так что это просто выбор стиля кода.

Есть некоторые проблемы с входными данными:

  1. john и John
  2. ('tim', 2000) ряд повторяется

Из-за этого ваш фактический результат отличается от ожидаемого. Я подозреваю, что это должно быть исправлено во входных данных. Но если входные данные нельзя изменить, то №1 можно легко исправить в коде с помощью LOWER. Исправление №2 в коде более сложное. Пожалуйста, оставьте комментарий, если вам это нужно, я расширю ответ.

Также обратите внимание, что percent_missed в ожидаемом результате округляется до 1 дробной цифры. Ваш нет.

Другие вопросы по теме