有没有人有方法来理解复杂的SQL语句?在阅读结构/ OO代码时,通常有一些抽象层可以帮助您将其分解为可管理的块.但是,通常在SQL中,您似乎必须同时跟踪查询的多个部分中发生的情况.
此问题的推动力是此问题中讨论的关于复杂连接的SQL查询.在盯着答案查询几分钟后,我终于决定使用特定记录逐步查询查看发生了什么.这是我能想到的唯一方法来逐一理解查询.
有没有更好的方法将SQL查询分解为可管理的部分?
当我查看一段复杂的SQL代码时,这就是我所做的.
首先,如果是更新或删除,我添加代码(如果它不存在并注释掉)以使其成为选择.切勿首次尝试更新或删除,而不先在选择中查看结果.如果是更新,我确保选择显示当前值以及我将其设置为什么,以确保我得到所需的结果.
了解连接对于理解复杂的SQL至关重要.对于每次加入我都会问自己为什么会这样?有四个基本原因.你需要一个select列的列,你需要一个where子句的字段,你需要join作为第三个表的桥接,或者你需要加入到表中来过滤记录(比如检查有订单的客户的详细信息)但是不需要订单细节,这通常可以通过IF EXISTS where子句更好地完成.如果是左连接或右连接(我倾向于重写所以一切都是左连接,这使生活更简单.),我考虑内部连接是否有效.为什么我需要左连接?如果我不知道答案,我会双向运行,看看数据中的差异是什么.如果有派生表,我将首先看一下(运行select的那一部分以查看结果是什么)来理解它为什么存在.如果存在子查询,我将尝试理解它们,如果它们很慢,则会尝试转换为派生表,因为它们通常要快得多.
接下来,我看看这些where
条款.在这个地方,您的特定数据库中的坚实基础会派上用场.例如,我知道在我的数据库中,我可能需要查看邮件地址以及我可能需要查看其他地址的场合.这有助于我知道where子句中是否缺少某些内容.否则我会考虑条款中的每个项目,where
并弄清楚为什么需要在那里,然后我会考虑是否有任何遗漏应该存在.在查看之后,我考虑是否可以进行调整以使查询变得可搜索.
我还考虑下一个选择列表的任何复杂位.那个案例陈述做了什么?为什么有子查询?这些功能有什么作用?(我总是查找我不熟悉的任何函数的函数代码.)为什么有明显的?是否可以通过使用派生表或聚合函数和group by语句来消除它?
最后,最重要的是,我运行选择并根据我对业务的了解确定结果是否正确.如果您不了解您的业务,您将不知道查询是否正确.语法正确并不意味着正确的结果.通常,您现有的用户界面中有一部分可用作指导结果是否正确的指南.如果我有一个显示客户订单的屏幕,并且我正在做包含客户订单的报告,我可能会检查一些个别客户,以确保它显示正确的结果.
如果当前查询过滤不正确,我将删除它的一些内容,以找出什么是摆脱我不想要的记录或添加我不想要的记录.通常你会发现连接是一对多的,你需要一对一(在这种情况下使用派生表!)或者你会发现你认为你需要的一些信息where
对于所有人都不是真的您需要的数据或where
缺少某些条款的数据.它有助于拥有所有领域where
当你执行此操作时,在select中的子句(如果它们不在select中).它甚至可以帮助显示所有连接表中的所有字段并真正查看数据.当我这样做时,我经常在where子句中添加一点点来获取我不应该存在的一些记录而不是所有记录.
一个会破坏大量查询的偷偷摸摸的东西是where
引用左连接右侧表格中的字段的子句.这将它变成了一个内部联接.如果您确实需要左连接,则应将这些条件添加到连接本身.
这些可能是一些有用的提示..
评论 - 弄清楚一个小块的作用并对其进行评论,以便您在以后再参考它时理解它.
语法突出显示 - 确保您正在查看带有对查询进行颜色编码的代码.
缩进 - 重新组织查询以使您有意义..选项卡结束,添加回车符.
例如:
select ID, Description, Status from ABC where Status = 1 OR Status = 3
可以更好地写为:
select ID, Description, Status from ABC where Status = 1 OR Status = 3
通过更复杂的查询,您将看到更大的好处.
这是解开查询的过程.
首先我格式化SQL.
然后我注释掉SQL的所有部分,而不是最主要或最重要的表的基本部分来回答问题.
然后我将开始取消注释连接,选择列,分组,顺序字段和过滤器以分析查询的不同部分以查看发生的情况.或突出显示 - 执行在某些工具中有效.
子查询通常可以独立执行.
执行这些中的每一个通常可以让我更好地掌握查询中发生的事情.
大多数情况下,这只是经验和适当的缩进.
缩进和评论有很多帮助.我遇到的最有价值的事情是WITH语句.它在Oracle中,处理子查询重构.它允许您将大型查询分解为一组看似较小的查询.每个只是更易于管理.
这是一个例子
WITH ssnInfo AS ( SELECT SSN, UPPER(LAST_NAME), UPPER(FIRST_NAME), TAXABLE_INCOME, CHARITABLE_DONATIONS FROM IRS_MASTER_FILE WHERE STATE = 'MN' AND -- limit to in-state TAXABLE_INCOME > 250000 AND -- is rich CHARITABLE_DONATIONS > 5000 -- might donate too ), doltishApplicants AS ( SELECT SSN, SAT_SCORE, SUBMISSION_DATE FROM COLLEGE_ADMISSIONS WHERE SAT_SCORE < 100 -- About as smart as a Moose. ), todaysAdmissions AS ( SELECT doltishApplicants.SSN, TRUNC(SUBMISSION_DATE) SUBMIT_DATE, LAST_NAME, FIRST_NAME, TAXABLE_INCOME FROM ssnInfo, doltishApplicants WHERE ssnInfo.SSN = doltishApplicants.SSN ) SELECT 'Dear ' || FIRST_NAME || ' your admission to WhatsaMattaU has been accepted.' FROM todaysAdmissions WHERE SUBMIT_DATE = TRUNC(SYSDATE) -- For stuff received today only ;
使用内联视图可以完成同样的操作,但是还可以在需要时创建临时表.在某些情况下,您可以复制出子查询并在大型查询的上下文之外执行它.
此表单还允许您将过滤器子句与单个子查询放在一起,并保存最终选择的连接子句.
在工作中,我们的开发小组通常发现它们更容易维护,而且通常更快.
格式化有所帮助,但理解集合论和扩展,关系理论,甚至更有帮助.
对查询执行方式的模糊理解也不会造成影响(表扫描,索引扫描,索引跳转,哈希表合并等); 查询计划程序可以向您显示这些操作
一些操作(有,存在,有)一开始可能很麻烦
首先要了解每个表发生了什么,以及如何连接表