запрос на самостоятельное соединение с группировкой - PullRequest
0 голосов
/ 02 сентября 2018

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

Логика, которую необходимо достичь: для каждой продолжительности курса найдите все остальные курсы, активные в течение этой продолжительности, и сумму Number_of_students из этих курсов, сгруппированных по room_type.
Существуют дополнительные сложности, но упрощенная версия проблемы представлена ​​ниже.

В настоящее время я использую hsqldb, решение должно использовать синтаксис std sql для переносимости между базами данных.

Стол заказов

BOOKING_ID| COURSE_ID| NUMBER_OF_STUDENTS| ROOM_TYPE_ID
    10    |    2     |        1          |    1
    20    |    1     |        2          |    1
    30    |    3     |        1          |    3
    40    |    1     |        3          |    4
    50    |    5     |        1          |    2
    60    |    6     |        2          |    2
    70    |    3     |        2          |    1
    80    |    4     |        1          |    3

Таблица курсов

COURSE_ID| START_DATE |  END_DATE
    1    | 2018-05-15 |  2018-06-14    //sample course
    2    | 2018-05-11 |  2018-05-20    //starts before ends between sample course
    3    | 2018-05-18 |  2018-05-22    //starts between ends between sample course
    4    | 2018-05-20 |  2018-06-20    //starts between ends after sample course
    5    | 2018-05-10 |  2018-06-20    //starts before ends after sample course
    6    | 2018-05-10 |  2018-05-14    //starts and ends before sample course
    7    | 2018-06-15 |  2018-06-20    //starts and ends after sample course

Таблица номеров (нам это здесь не нужно, просто для полноты)

ROOM_TYPE_ID| ROOM_CAPACITY| ROOM_LOCATION
    1       |    1         |  HILL
    2       |    2         |  HILL
    3       |    1         |  OCEAN
    4       |    2         |  OCEAN

вывод (отображается только для course_id 1, необходим для всех курсов)

COURSE_ID | ROOMTYPE | COURSE_STUDENT | OTHER_STUDENTS 
    1     |   1      |        2       |      3           //1(course 2) + 2 (course 3)
    1     |   2      |        0       |      1           //1(course 5)
    1     |   3      |        0       |      2           //1(course 3) + 1(course 4)
    1     |   4      |        3       |      0           //no students on others

Я мог ТОЛЬКО выяснить условие соответствия перекрывающихся курсов для данного курса startDate , endDate

Courses.START_DATE <= startDate  AND Courses.END_DATE >= endDate    OR        //matches any course spanning current course
Courses.START_DATE >= startDate  AND Courses.END_DATE <= startDate  OR        //matches any course starting during the current course
Courses.START_DATE >= endDate    AND Courses.END_DATE <= endDate              //matches any course ending during the current course

, за пределами которого мои скудные навыки sql, подвели меня. Я мог бы раскрутить некоторый Java-код, чтобы решить это .... но это было бы хромым и неэффективным.

Ответы [ 2 ]

0 голосов
/ 08 сентября 2018

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

SELECT
        THIS_COURSE.COURSE_ID, 
        OTHER_COURSE.ROOM_TYPE_ID,
        SUM(CASE  WHEN THIS_COURSE.BOOKING_ID = OTHER_COURSE.BOOKING_ID THEN OTHER_COURSE.NUMBER_OF_STUDENTS ELSE 0 END) AS COURSE_STUDENTS,
        SUM(CASE  WHEN THIS_COURSE.BOOKING_ID <> OTHER_COURSE.BOOKING_ID THEN OTHER_COURSE.NUMBER_OF_STUDENTS ELSE 0 END) AS OTHER_STUDENTS,
        SUM(OTHER_COURSE.NUMBER_OF_STUDENTS) AS TOTAL_STUDENTS
FROM

(
    SELECT 
        BOOKINGS.BOOKING_ID, 
        BOOKINGS.COURSE_ID, 
        BOOKINGS.NUMBER_OF_STUDENTS, 
        BOOKINGS.ROOM_TYPE_ID, 
        COURSES.START_DATE, 
        COURSES.END_DATE 
    FROM 
        BOOKINGS , COURSES 
    WHERE 
        BOOKINGS.COURSE_ID = COURSES.COURSE_ID
) THIS_COURSE

LEFT JOIN 
(
    SELECT 
        BOOKINGS.BOOKING_ID, 
        BOOKINGS.COURSE_ID, 
        BOOKINGS.NUMBER_OF_STUDENTS, 
        BOOKINGS.ROOM_TYPE_ID, 
        COURSES.START_DATE, 
        COURSES.END_DATE 
    FROM 
        BOOKINGS , COURSES 
    WHERE 
        BOOKINGS.COURSE_ID = COURSES.COURSE_ID
) OTHER_COURSE

ON 
    THIS_COURSE.BOOKING_ID <> OTHER_COURSE.BOOKING_ID OR
    THIS_COURSE.BOOKING_ID = OTHER_COURSE.BOOKING_ID

WHERE
        (THIS_COURSE.START_DATE <= OTHER_COURSE.START_DATE AND THIS_COURSE.END_DATE >= OTHER_COURSE.END_DATE)  OR
        (THIS_COURSE.START_DATE <= OTHER_COURSE.START_DATE AND THIS_COURSE.END_DATE >= OTHER_COURSE.START_DATE)  OR
        (THIS_COURSE.START_DATE <= OTHER_COURSE.END_DATE   AND THIS_COURSE.END_DATE >= OTHER_COURSE.END_DATE)  

GROUP BY 
    THIS_COURSE.COURSE_ID, OTHER_COURSE.ROOM_TYPE_ID

Ниже приведен sql для создания примера данных

CREATE TABLE Bookings(BOOKING_ID INTEGER NOT NULL PRIMARY KEY, COURSE_ID INTEGER NOT NULL, NUMBER_OF_STUDENTS INTEGER NOT NULL, ROOM_TYPE_ID INTEGER NOT NULL)
CREATE TABLE Courses(COURSE_ID INTEGER NOT NULL PRIMARY KEY, START_DATE DATE,  END_DATE  DATE)
CREATE TABLE Rooms(ROOM_TYPE_ID INTEGER NOT NULL PRIMARY KEY, ROOM_CAPACITY INTEGER NOT NULL, ROOM_LOCATION VARCHAR(25))

INSERT INTO Bookings VALUES(    10   ,    2    ,        1         ,    1 )
INSERT INTO Bookings VALUES(    20   ,    1    ,        2         ,    1 )
INSERT INTO Bookings VALUES(    30   ,    3    ,        1         ,    3 )
INSERT INTO Bookings VALUES(    40   ,    1    ,        3         ,    4 )
INSERT INTO Bookings VALUES(    50   ,    5    ,        1         ,    2 )
INSERT INTO Bookings VALUES(    60   ,    6    ,        2         ,    2 )
INSERT INTO Bookings VALUES(    70   ,    3    ,        2         ,    1 )
INSERT INTO Bookings VALUES(    80   ,    4    ,        1         ,    3 )
INSERT INTO Bookings VALUES(    90   ,    7    ,        1         ,    4 )


INSERT INTO Courses VALUES(    1    ,'2018-05-15', '2018-06-14' )
INSERT INTO Courses VALUES(    2    ,'2018-05-11', '2018-05-20' )
INSERT INTO Courses VALUES(    3    ,'2018-05-18', '2018-05-22' )
INSERT INTO Courses VALUES(    4    ,'2018-05-20', '2018-06-20' )
INSERT INTO Courses VALUES(    5    ,'2018-05-10', '2018-06-20' )
INSERT INTO Courses VALUES(    6    ,'2018-05-10', '2018-05-14' )
INSERT INTO Courses VALUES(    7    ,'2018-06-15', '2018-06-20' )


INSERT INTO Rooms VALUES(    1       ,    1        ,  'HILL')
INSERT INTO Rooms VALUES(    2       ,    2        ,  'HILL')
INSERT INTO Rooms VALUES(    3       ,    1        ,  'OCEAN')
INSERT INTO Rooms VALUES(    4       ,    2        ,  'OCEAN')
0 голосов
/ 02 сентября 2018

Вы действительно хотите необходимое количество комнат каждого типа для каждого курса. поэтому вам нужно начать с таблицы COURSES и соединить ее с двумя другими таблицами.

SELECT * FROM COURSES JOIN BOOKINGS USING (COURSE_ID) JOIN ROOMS USING (ROOM_TYPE_ID)

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

WITH ROOM_BOOKINGS AS (
  SELECT 
    BOOKINGS.BOOKING_ID, 
    BOOKINGS.COURSE_ID, 
    BOOKINGS.NUMBER_OF_STUDENTS, 
    BOOKINGS.ROOM_TYPE_ID, 
    COURSES.START_DATE, 
    COURSES.END_DATE, 
    ROOMS.ROOM_CAPACITY
  FROM 
    COURSES JOIN BOOKINGS USING (COURSE_ID) JOIN ROOMS USING (ROOM_TYPE_ID)
 ) 
 SELECT * FROM ROOM_BOOKINGS THIS_COURSE LEFT JOIN ROOM_BOOKINGS OTHER_COURSE
 ON (THIS_COURSE.START_DATE, THIS_COURSE.END_DATE + 1 DAY) OVERLAPS (OTHER_COURSE.START_DATE, OTHER_COURSE.END_DATE + 1 DAY)
 AND THIS_COURSE.ROOM_TYPE_ID = OTHER_COURSE.ROOM_TYPE_ID 
 AND THIS_COURSE.COURSE_ID  <> OTHER_COURSE.COURSE_ID

Вам необходимо выполнить вышеуказанный запрос и добавить условие в SELECT, чтобы вернуть только один курс. Вам также нужны GROUP BY A.COURSE_ID, A.ROOM_TYPE_ID, A.NUMBER_OF_STUDENTS, ... и SUM (B.NUMBER_OF_STUDENS) для достижения желаемого результата.

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

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