利用在一个 SQL 查询中完成的工作来简化第二个查询?
Posted
技术标签:
【中文标题】利用在一个 SQL 查询中完成的工作来简化第二个查询?【英文标题】:Leverage the work done in one SQL query to simplify a second one? 【发布时间】:2012-12-28 08:02:48 【问题描述】:我有一个数据库,其中包含以下两个表:
classes
是一个简单的表格,在课程表中每个班级都有一行。
sessions
是一个表格,描述了每个班级聚会的日期和时间,其中每一行都能够表达如下概念:
“星期二 | 1 月 22 日至 3 月 5 日 | 下午 6 点至 9 点” “周二和周四 | 1 月 22 日至 3 月 7 日 | 下午 6 点至 9 点” “周一至周四 | 1 月 21 日至 24 日 | 下午 3 点至 6 点” “星期六 | 3 月 9 日 | 上午 9 点至下午 4 点”
等等。
sessions
中的每一行保证至少有一行 classes
,并且对于某些类,可能有两个或更多关联的会话行。
目前,我使用两个不同的查询来获取符合特定条件集的类的类和会话信息,如下所示:
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 收集到一个向量中(因为无论如何我都必须遍历这些结果),然后将该向量的内容格式化为value-list 用于 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 */);
我不太担心这种解决方案的可扩展性,因为我了解huge SQL queries are no big deal。另外,它可以利用在sessions.class_id
上定义的索引。
但是...嗯...对于那些希望提高自己的 SQL 能力的人来说,这并不是很令人满意,我承认这还很初级。感觉不优雅,而且不是很“SQL-ish”,或者与术语 Pythonic 等价的 SQL 是什么。
谁能提出更合适的建议?
【问题讨论】:
【参考方案1】:执行您想要的操作的规范方法是使用视图。将您的第一个查询定义为:
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)
这样做是有充分理由的。在多个查询中重复这样的逻辑成为维护的噩梦。当您在一处修改逻辑时,很容易忘记在所有地方进行修改。有时,视图是不够的,因此您可能需要使用用户定义的函数,如 here 所述。
此外,如果条件非常有用,您可能需要在类表中放置标志来识别它们。这需要以某种方式维护它们,例如每晚更新或使用触发器。
【讨论】:
+1 以针对第一个查询中的更改强化第二个查询。你能解释一下在类表中放置“标志”是什么意思吗? (FWIW,classes 表每季度只进行一次重大更改,在这种情况下,我可以强制之后进行任何需要的更新。) @Hephaestus 。 . .我不知道这些标志是什么。但是假设 X 教授正在教一个人。如果这很重要,请在课程表(或单独的“ClassDetails”表)中为 IsTaughtByX 添加一个标志。当然,您知道必须维护 this is 和类似的标志,因此它可能不会简化整体问题。另一方面,如果其他人正在使用数据库,它可能会使报告变得更容易。 暂时不考虑“标志”:我的感觉是视图的寿命相对较长,并且它们的 SELECT 语句不能参数化——至少在 MySQL 中不能。有问题的应用程序必须支持不同用户的随意在线浏览,所以除非我误解了什么(完全有可能!),似乎每次用户想要查看不同部门的课程时我都必须启动一个新视图,或改变一个或多个其他合格条件。如果是这样,这真的是一场胜利吗? 如果选择标准只是按用户,那么您将编写一个将用户包含在选择列中的视图。然后应用程序将添加一个where
子句以进行适当的选择。一般来说,每个用户的不同视图是不好的。
现实情况更糟,我担心:每个用户都可以提供他/她自己的部门值、技能水平(例如,“入门级|中级|高级”)、最低/最高价格,等等,等等。我将查询字符串中的形式参数与用户为请求的每个页面视图提供的值重新绑定。 (我在 OP 中使用 php 风格的语法 ?param 表示了一个形式参数——如果不熟悉这种语法风格,如有任何混淆,请见谅。)【参考方案2】:
老实说,我不会打扰。首先,从您告诉我们的内容来看,它工作得很好,对我来说似乎相当优雅。其次,如果没有理由在第二个查询中带回额外数据,则不要这样做。第三,也是迄今为止最重要的一点是,就目前的情况而言,很容易理解正在发生的事情。您可能并不总是唯一一个试图破译这一点的人,重要的是代码可以被其他人阅读。过于复杂的 SQL 查询不好。
我认为它还不错,而且它的 SQL 风格也很好。
【讨论】:
嗯,将第二个查询替换为使用IN
条件的查询的一个优点是,它使其不受第一个查询中复杂的连接和条件集的更改的影响,目前必须在第二个中复制。即使并行代码更新顺利进行,代码的读者仍然必须说服他/她自己,这两组连接和条件实际上是相同的。在修改后的第二个查询中没有这样的问题,只要第一个继续选择 c.class_id,它就不必更改,而不管其他细节。以上是关于利用在一个 SQL 查询中完成的工作来简化第二个查询?的主要内容,如果未能解决你的问题,请参考以下文章