SQL найти последовательные кварталы - PullRequest
1 голос
/ 19 августа 2011

Я хотел бы предоставить скидку в размере 20% от стоимости этого класса участнику, который посещал занятия в последнем квартале (если участник посещает занятия в течение трех последовательных кварталов, и ему будут предоставлены скидки в последний раздве четверти).И суммируйте все скидки ($), которые я выдал.

Вот таблицы в базе данных:

ENROLLMENT (class_id, member_id, cost)

CLASS (class_id, сезона, года) и атрибутом сезона может быть весна, лето, осень, зима.

И данные будут выглядеть так:

Зачисление

Class_id  Member_id  Cost
--------  ---------  ----
3         3          20 
1         9          15 
2         9          20 
4         10         30 
3         10         10 
5         5          10 
4         9          30 
...

Класс

class_id  Season  Year 
--------  ------  ----
1         Spring  2008 
2         Fall    2008 
3         Spring  2009 
4         Winter  2008 
5         Summer  2008 
...

Ответы [ 4 ]

0 голосов
/ 19 августа 2011

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

В любом случае, вот моя версия ваших тестовых данных.Я использую имена, чтобы было легче увидеть, что происходит:

SQL> select s.name as student
  2         , c.name as class
  3         , q.season||' '||q.year as quarter
  4         , q.q_id
  5         , c.base_cost
  6  from  enrolments e
  7          join students s
  8              on (s.s_id = e.s_id)
  9          join classes c
 10              on (c.c_id = e.c_id)
 11          join quarters q
 12              on (q.q_id = c.q_id)
 13  order by s.s_id, q.q_id
 14  /

STUDENT    CLASS                QUARTER               Q_ID  BASE_COST
---------- -------------------- --------------- ---------- ----------
Sheldon    Introduction to SQL  Spring 2008            100        100
Sheldon    Advanced SQL         Spring 2009            104        150
Howard     Introduction to SQL  Spring 2008            100        100
Howard     Information Theory   Summer 2008            101         75
Rajesh     Information Theory   Summer 2008            101         75
Leonard    Crypto Foundation    Autumn 2008            102        120
Leonard    PHP for Dummies      Winter 2008            103         90
Leonard    Advanced SQL         Spring 2009            104        150

8 rows selected.

SQL>

Как видите, у меня есть таблица QUARTERS, первичный ключ которой Q_ID увеличивается на единицу в календарном порядке.

Я собираюсь использовать синтаксис Oracle для решения этой проблемы, в частности, аналитическую функцию LAG:

SQL> select s.name as student
  2         , c.name as class
  3         , q.season||' '||q.year as quarter
  4         , q.q_id
  5         , c.base_cost
  6         , lag (q.q_id) over (partition by s.s_id order by q.q_id) prev_q_id
  7  from  enrolments e
  8          join students s
  9              on (s.s_id = e.s_id)
 10          join classes c
 11              on (c.c_id = e.c_id)
 12          join quarters q
 13              on (q.q_id = c.q_id)
 14  order by s.s_id, q.q_id
 15  /

STUDENT    CLASS                QUARTER               Q_ID  BASE_COST  PREV_Q_ID
---------- -------------------- --------------- ---------- ---------- ----------
Sheldon    Introduction to SQL  Spring 2008            100        100
Sheldon    Advanced SQL         Spring 2009            104        150        100
Howard     Introduction to SQL  Spring 2008            100        100
Howard     Information Theory   Summer 2008            101         75        100
Rajesh     Information Theory   Summer 2008            101         75
Leonard    Crypto Foundation    Autumn 2008            102        120
Leonard    PHP for Dummies      Winter 2008            103         90        102
Leonard    Advanced SQL         Spring 2009            104        150        103

8 rows selected.

SQL>

Итак, просматривая столбцы PREV_Q_ID, мы видим, что у Говарда, Шелдона и Леонарда было большечем один курс.Только Леонард прошел три курса.Сравнивая значения в столбцах PREV_Q_ID и Q_ID, мы можем видеть, что два курса Говарда находятся в последовательных кварталах, в то время как Шелдона нет.

Теперь мы можем сделать несколько математических операций:

SQL> select student
  2          , class
  3          , quarter
  4          , base_cost
  5          , discount*100 as discount_pct
  6          , base_cost - (base_cost*discount) as actual_cost
  7  from
  8          ( select student
  9                  , class
 10                  , quarter
 11                  , base_cost
 12                  , case
 13                      when prev_q_id is not null
 14                           and q_id - prev_q_id = 1
 15                      then 0.2
 16                      else 0
 17                    end       as discount
 18                 , s_id
 19                 , q_id
 20            from
 21                  (
 22                  select s.name as student
 23                         , c.name as class
 24                         , q.season||' '||q.year as quarter
 25                         , q.q_id
 26                         , c.base_cost
 27                         , lag (q.q_id) over (partition by s.s_id order by q.q_id) prev_q_id
 28                         , s.s_id
 29                  from  enrolments e
 30                          join students s
 31                              on (s.s_id = e.s_id)
 32                          join classes c
 33                              on (c.c_id = e.c_id)
 34                          join quarters q
 35                              on (q.q_id = c.q_id)
 36                  )
 37          )
 38  order by s_id, q_id
 39  /

(искусственный перерыв, чтобы избавиться от необходимости прокручивать страницу вниз, чтобы увидеть результаты)

STUDENT    CLASS                QUARTER      BASE_COST DISCOUNT_PCT ACTUAL_COST
---------- -------------------- ----------- ---------- ------------ -----------
Sheldon    Introduction to SQL  Spring 2008        100            0         100
Sheldon    Advanced SQL         Spring 2009        150            0         150
Howard     Introduction to SQL  Spring 2008        100            0         100
Howard     Information Theory   Summer 2008         75           20          60
Rajesh     Information Theory   Summer 2008         75            0          75
Leonard    Crypto Foundation    Autumn 2008        120            0         120
Leonard    PHP for Dummies      Winter 2008         90           20          72
Leonard    Advanced SQL         Spring 2009        150           20         120

8 rows selected.

SQL>

Итак, Говард иЛеонард получает скидки на свои занятия, а Шелдон и Радж этого не делают.

0 голосов
/ 19 августа 2011

Попробуйте это

select member_id, cost*0.8 as cost_discount, cost
from Enrollment e inner join
(select member_id, 
    group_concat(season ORDER BY season SEPARATOR ',') as group_season, year as year1 
    from class as c inner join ENROLLMENT as e on (c.class_id=e.class_id) group by Member_id, year
    having group_season='winter,spring,summer' or group_season='spring,summer,fall'
     or LOCATE('fall',group_season) <> 0 
) as t
on t.member_id=e.member_id inner join class as c on (c.class_id=e.class_id)
where year1='2011' and locate(season, substr(group_season, 7))

К сожалению, я не проверял это на синтаксическую ошибку. Сумма скидки:

select sum(cost - cost_discount) from (first_query);
0 голосов
/ 19 августа 2011

Предложение: используйте таблицу календаря .

В нем будет по одной строке на каждый день, который вам когда-либо понадобится (прошедшие и будущие даты), что должно составлять только десятки тысяч строк (всегда можно добавить больше в будущем;)

Каждая строка может иметь следующие атрибуты (столбцы):

effective_date
current_quarter_season
current_quarter_year
prior_quarter_season
prior_quarter_year
next_prior_quarter_season
next_prior_quarter_year

Возможно, вы можете придумать лучшие имена :)

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

Идея состоит в том, что SQL является декларативным языком, который лучше всего работает с декларативными решениями, такими как календарьтаблица.

0 голосов
/ 19 августа 2011

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

Что-то вроде этого должно работать:

SET @q0_season = 'Fall';
SET @q0_year = 2011;
SET @q1_season = 'Summer';
SET @q1_year = 2011;
SET @q2_season = 'Spring';
SET @q2_year = 2011;


SELECT DISTINCT e.class_id, e.member_id, e.cost, if (max(e1.member_id) is null, 0, 0.20) * if(max(e2.member_id) is null, 1, 2) discount
FROM enrollment e
INNER JOIN class c 
    ON c.class_id = e.class_id 
    AND c.season = @q0_season AND c.year = @q0_year
LEFT JOIN (enrollment e1 
    INNER JOIN class c1 ON c1.class_id = e1.class_id
        AND c1.season = @q1_season AND c1.year = @q1_year)
    ON e1.member_id = e.member_id
LEFT JOIN (enrollment e2
    INNER JOIN class c2 ON c2.class_id = e2.class_id
        AND c2.season = @q2_season AND c2.year = @q2_year)
    ON e2.member_id = e.member_id
GROUP BY e.class_id, e.member_id, e.cost;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...