Использовать работу, выполненную в одном SQL-запросе, чтобы упростить вторую?

У меня есть база данных, среди прочего, следующие две таблицы:

classes is a straightforward table that has one row per class in a class schedule.

sessions is a table that characterizes the days and times that each class meets, where each row is capable of expressing a notion like:

"Tuesdays | Jan 22-Mar 5 | 6-9pm"
"Tuesdays & Thursdays | Jan 22-Mar 7 | 6-9pm"
"Monday-Thursday | Jan 21-24 | 3-6pm"
"Saturday | Mar 9 | 9am-4pm"

и так далее.

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

В настоящее время я использую два разных запроса, чтобы получить информацию о классе и сеансе для классов, которые соответствуют определенному набору критериев, например:

select c.class_id, c.title, c.instructor, c.num_seats, c.price
  from classes c
  join classes_by_department cbd 
    on (cbd.class_id = c.class_id)
  join /* several other tables */
    on /* several other join conditions */
 where cbd.department_id = '{$dept_id}'
   and /* several other qualifying conditions */
;

и это:

select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
  from sessions s
  join classes c
    on (c.class_id = s.class_id)
  join classes_by_department cbd
    on (cbd.class_id = s.class_id) 
  join /* the same other tables */
    on /* the same other join conditions */
 where cbd.department_id = '{$dept_id}'
   and /* the same other qualifying conditions */
;

Это отлично работает, и, по крайней мере, в текущем приложении - таблицы недостаточно велики, а трафик не достаточно тяжелый, так как два запроса являются проблемой. Тем не менее, это кажется мне немного расточительным, и мне интересно, не существует ли способа улучшить работу, уже выполненную первым запросом, для выполнения второго (вместо того, чтобы занять один и тот же запрос дважды и просто выберите разные столбцы).

Конечно, я понимаю, что я мог бы просто выбрать все соответствующие столбцы из classes и sessions в одном запросе (второй), но мне нравится тот факт, что в текущем подход, первый запрос предоставляет ровно одну строку для каждого класса, а не столько строк, сколько класс имеет записи сеанса. Мне нужно будет перестроить существующую логику, которая обрабатывает результаты запроса, если я объединил запросы. (Да, я знаю, ваа ...)

Одним из решений, которое пришло мне в голову, является сбор всех class_id s, возвращенных первым запросом в вектор (так как я должен итерации по этим результатам) в любом случае, а затем форматировать содержимое этого вектора в качестве содержимого из списка значений для предложения IN , так что второй запрос просто станет:

select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
  from sessions s
 where s.class_id in (/* value-list */);

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

Но ... ну ... это просто не очень приятно для кого-то, кто хочет улучшить свои SQL-отбивные, которые я буду свободно признавать, довольно рудиментарны. Он чувствует себя неэлегантным и не очень «SQL-ish», или каким бы то ни было SQL-эквивалентом термина Pythonic .

Может ли кто-нибудь предложить что-то более подходящее?

3
nl ja de

2 ответы

Канонический способ делать то, что вы хотите, - это использовать представления. Определите свой первый запрос как:

create view vw_MyClasses as
    select c.class_id, c.title, c.instructor, c.num_seats, c.price, cbd.department_id
    from classes c
         join classes_by_department cbd 
         on (cbd.class_id = c.class_id)
         join /* several other tables */
         on /* several other join conditions */
   where /* several other qualifying conditions */

Тогда ваш запрос класса будет:

select *
from vw_MyClasses
where department_id = '{$dept_id}'

Затем ваш второй запрос может быть:

 select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
 from sessions s
 where s.class_id in (select class_id from vw_MyClasses 
                                      where department_id = '{$dept_id}');

Или, что может быть более эффективным в MySQL:

 select s.class_id, s.start_date, s.end_date, s.day_bits, s.start_time, s.end_time
 from sessions s
 where exists (select 1 from vw_MyClasses mc where mc.class_id = s.class_id limit 1)

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

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

1
добавлено
@Hephaestus. , , Я не знаю, что такое флаги. Но скажите, что преподавателем является профессор X. Если это важно, добавьте флаг в таблицу классов (или отдельную таблицу ClassDetails) для IsTaughtByX. Конечно, вы знаете, что нужно поддерживать это и аналогичные флаги, поэтому это может не упростить общую проблему. С другой стороны, это может облегчить отчетность, если другие используют базу данных.
добавлено автор Gordon Linoff, источник
Если критерием выбора является просто пользователь, вы должны написать представление, которое включало пользователя в столбцы выбора. Затем приложение добавит предложение where , чтобы сделать соответствующий выбор. Другое мнение для каждого пользователя плохое, в общем.
добавлено автор Gordon Linoff, источник
К сожалению, мой синтаксис был неправильным. См. Модификацию, которую я только что сделал. Поместите department_id в список выбора , , затем используйте его в предложении where . Возможно, вам не нужны все столбцы в представлении. , , вы можете выбрать те, которые вы хотите.
добавлено автор Gordon Linoff, источник
Да, это хороший способ объяснить это.
добавлено автор Gordon Linoff, источник
+1 для упрощения второго запроса от изменений в первом. Не могли бы вы объяснить немного больше о том, что вы хотите сказать о «флажках» в таблице классов? (FWIW, таблица классов только претерпевает значительные изменения один раз в квартал, и в такие моменты я могу принудительно выполнить любое необходимое обновление после этого.)
добавлено автор Hephaestus, источник
Оставляя в стороне «флаги» на данный момент: я считаю, что представления предполагают относительно долговечными, и их инструкции SELECT нельзя параметризовать - по крайней мере, в MySQL. Это приложение должно поддерживать случайный онлайн-просмотр различными пользователями, поэтому, если я не понял что-то (полностью возможно!), Мне кажется, что мне придется разворачивать новое представление каждый раз, когда пользователь хочет видеть классы другого отдела , или меняет один или несколько других условий квалификации. Если да, то это действительно победа?
добавлено автор Hephaestus, источник
Реальность еще хуже, я боюсь: каждый пользователь может предоставить свои собственные ценности для отдела, уровень навыков (например, «начальный уровень | промежуточный | продвинутый»), минимальная/максимальная цена и т. Д. И т. Д. Я повторяю формальные параметры в строке запроса с заданными пользователем значениями для каждого запрошенного просмотра страницы. (Я обозначил формальный параметр в OP с синтаксисом PHP-ish {? Param} - извините за любую путаницу, если этот синтаксический стиль был незнакомым.)
добавлено автор Hephaestus, источник
Ого, я думаю, теперь вижу. Я видел ссылки в другом месте на использование представлений для де-нормализации таблиц - и если я правильно понимаю ваш recast select , похоже, это суть того, что вы предлагаете. (Да?)
добавлено автор Hephaestus, источник

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

Я думаю, что это нормально, как есть, и это SQL-ishness хорошо.

0
добавлено
Ну, одним из преимуществ замены второго запроса тем, что использует условие IN , является то, что он делает его непроницаемым для изменений сложного набора объединений и условий в первом запросе, который в настоящее время должен быть дублируется во втором. Даже если это параллельное обновление кода отключается без заминки, читателю кода все равно придется убедить себя, что два набора объединений и условий на самом деле одинаковы. Нет такой проблемы в модифицированном втором запросе, который никогда не должен меняться, пока первый продолжает выбирать c.class_id, независимо от других деталей.
добавлено автор Hephaestus, источник
DBA - русскоговорящее сообщество
DBA - русскоговорящее сообщество
1 345 участник(ов)

Общаемся и обсуждаем темы, посвященные DBA, PostgreSQL, Redis, MongoDB, MySQL, neo4j, riak и т.д. См. также: @devops_ru, @kubernetes_ru, @docker_ru, @nodejs_ru Рекомендуем сразу отключить уведомления, чтобы пребывание здесь было полезным и комфортным.

MySQL
MySQL
995 участник(ов)

The group is about MySQL. For code use hastebin.com. Admin: @smlkw

SqlCom.ru - Стиль жизни SQL
SqlCom.ru - Стиль жизни SQL
908 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только MS SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz). Предложения в @sql_ninja

SQL_Ninja
SQL_Ninja
340 участник(ов)

Правила чата - https://t.me/sqlcom/88269 @sqlcom - основной канал (только SQL) @sql_ninja - второй канал (SQL вопросы начального уровня и свободное общение) @Gopnegbot - Викторина по SQL Server (наберите в привате /quiz)