MySQL Запрашивает базу данных за еще не назначенные периоды времени

#mysql #time

#mysql #время

Вопрос:

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

Например, если у пользователя есть запись в st_schedule, назначающая их классу с 8:00 до 9: 00, они не будут иметь права на любой класс, время которого приходится на период с 8:00 до 9:00. Класс, который выполнялся с 7:15 до 8: 15 утра, лишил бы пользователя права. Я сохраняю время начала и окончания занятий в базе данных отдельно для целей сравнения. Важно, чтобы это было как можно более гибким, поэтому концепция времени «блокировки» и назначения времени блокам невозможна.

Вот выдержки из таблиц:

 table st_classes (holds class information)
id
start_time
end_time

table st_schedule (holds schedule information)
id
user_id
class_id
  

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

Ответ №1:

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

Что-то вроде этого. Полностью неофициально и непроверено:

 SELECT
    *
FROM
    st_schedule s
    INNER JOIN st_classes c ON c.id = s.class_id
    INNER JOIN st_classes all_classes 
        ON all_classes.start_time NOT BETWEEN c.start_time AND c.end_time
        AND all_classes.end_time NOT BETWEEN c.start_time AND c.end_time
WHERE
   s.user_id = 1
  

Редактировать: Попробуйте # 2
У меня есть только мгновение, чтобы взглянуть на это. Я думаю, что я отменил второе предложение join. Псевдоним all_classes представляет полный список классов, где псевдоним «c» представляет классы, на которые зарегистрирован учащийся.

 SELECT DISTINCT
    all_classes.*
FROM
    st_schedule s
    INNER JOIN st_classes c ON c.id = s.class_id
    INNER JOIN st_classes all_classes 
        ON c.start_time NOT BETWEEN all_classes.start_time AND all_classes.end_time
        AND c.end_time NOT BETWEEN all_classes.start_time AND all_classes.end_time
WHERE
   s.user_id = 1
  

Комментарии:

1. это близко, но то, что я получаю обратно, используя именно то, что вы ввели, — это список реальных классов, на которые зарегистрирован мой тестовый пользователь, повторяя один раз для записей класса и один раз для записей расписания. Я немного не в курсе части «all_classes» — это для указания третьей базы данных или для приведения группы результатов?

2. Если это все еще не работает, опубликуйте инструкции DDL и INSERT для создания тестовых данных (с ожидаемыми результатами).

Ответ №2:

Это использует табличные переменные в mssql, но выбранные sql должны быть переведены в mysql

Сначала образец данных

 DECLARE @st_classes TABLE
    (
     ID INT NOT NULL,
     Title VARCHAR(40) NOT NULL,
     StartTime DATETIME NOT NULL,
     EndTime DATETIME NOT NULL
    )       

DECLARE @st_schedule TABLE
    (
     ID INT NOT NULL,
     UserID INT NOT NULL,
     ClassID INT NOT NULL
    )       

INSERT INTO @st_classes (ID, Title, StartTime, EndTime)
SELECT 1,'Class1','08:00:00','09:30:00' UNION 
SELECT 2,'Class2','09:30:00','11:30:00' UNION
SELECT 3,'Class3','11:30:00','16:00:00' UNION
SELECT 4,'Class4','16:00:00','17:30:00' UNION
SELECT 5,'Class5','09:00:00','11:45:00' UNION
SELECT 6,'Class6','07:00:00','18:00:00' 

INSERT INTO @st_schedule(ID, UserID, ClassID)
SELECT 1,1,1 UNION
SELECT 2,1,2 UNION
SELECT 3,2,6
  

Далее немного sql для подтверждения соединения таблиц OK (выбор запланированных курсов для пользователя с идентификатором 1) — Возвращает класс 1 и 2

 SELECT *
FROM    @st_schedule AS S INNER JOIN 
        @st_classes AS C ON S.ClassID = C.ID
WHERE  S.UserID = 1 
  

Теперь нам нужно выбрать все идентификаторы курсов, где они совпадают по времени с запланированными пользователями (включая запланированные) — Возвращает 1,2,5,6

 SELECT  AC.ID
FROM    @st_classes AS AC
        INNER JOIN ( SELECT C.StartTime,
                            C.EndTime
                     FROM   @st_schedule AS S
                            INNER JOIN @st_classes AS C ON S.ClassID = C.ID
                     WHERE  S.UserID = 1
                   ) AS UC ON ( AC.StartTime < DATEADD(ss, -1, UC.EndTime)
                                AND DATEADD(ss, -1, UC.EndTime) > UC.StartTime
                              )
GROUP BY AC.ID 
  

Теперь нам нужно выбрать все курсы, идентификатор курса которых отсутствует в нашем списке совпадающих идентификаторов курсов. — Возвращает курс 3 и 4

 SELECT  *
FROM    @st_classes
WHERE   ID NOT IN (
        SELECT  AC.ID
        FROM    @st_classes AS AC
                INNER JOIN ( SELECT C.StartTime,
                                    C.EndTime
                             FROM   @st_schedule AS S
                                    INNER JOIN @st_classes AS C ON S.ClassID = C.ID
                             WHERE  S.UserID = 1
                           ) AS UC ON ( AC.StartTime < DATEADD(ss, -1, UC.EndTime)
                                        AND DATEADD(ss, -1, UC.EndTime) > UC.StartTime
                                      )
        GROUP BY AC.ID )
  

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

Комментарии:

1. Красота! С некоторыми незначительными изменениями форматирования для MySQL это отлично решило проблему! Большое спасибо за потрясающую работу! Версия в формате MySQL приведена ниже:

2. ВЫБЕРИТЕ * ИЗ st_classes, ГДЕ id ОТСУТСТВУЕТ ( ВЫБЕРИТЕ ac.id ИЗ st_classes КАК ac INNER JOIN ( ВЫБЕРИТЕ c.start_time, c.end_time ИЗ st_schedule КАК s INNER JOIN st_classes КАК c В s.class_id = c.id ГДЕ s.user_id = 1 ) КАК uc ON (ac.start_time < uc.end_time ) И uc.end_time > uc.start_time ГРУППИРУЮТСЯ По ac.id )