如何使复杂的SQL查询更易于编写?[关闭]


42

我发现编写涉及多个(至少3-4个)表之间的联接并涉及多个嵌套条件的复杂SQL查询非常困难。我被要求编写的查询很容易用几句话来描述,但是可能需要大量的欺骗性代码才能完成。我发现自己经常使用临时视图来编写这些查询,这似乎有些曲折。您可以提供哪些提示以使这些复杂查询更容易?更具体地说,如何将这些查询分解为实际编写SQL代码所需的步骤?

请注意,我是被要求编写的SQL,是数据库课程家庭作业的一部分,因此,我不希望有能够为我完成工作的软件。我想真正理解我正在编写的代码。

更多技术细节:

  • 该数据库托管在本地计算机上运行的PostgreSQL服务器上。
  • 数据库非常小:最多有七个表,最大的表少于50行。
  • SQL查询将通过LibreOffice Base不变地传递到服务器。

临时视图实际上非常有用,因为您可以对很难暗示SQL解析器的表(例如显式复杂索引)执行操作。

就个人而言,我发现使用GUI(例如LibreOffice Base“在设计视图中创建查询”或Office Access“创建”>“查询设计”)作弊更容易,然后查看由此产生的SQL。有时有必要修改GUI设计人员提供的SQL,但这提供了一个很好的起点
kurdtpage 2015年

Answers:


49

我将大部分这些都基于试图获得“正确的”答案,因此您可能会发现存在一些性能问题。加速错误查询毫无意义。

了解表之间的关系 -大多数将是一对多的。知道“很多”表。确定您的联接所需的字段。

考虑一下LEFT加入方案 -选择上个月的所有雇员及其薪水。如果上个月他们没有得到薪水怎么办?

了解结果集: 1)在电子表格中,为您的查询手动输入至少一个正确的记录。2)以足够简单的形式编写查询,以识别应返回多少记录。使用这两种方法来测试您的查询,以确保加入新表不会改变结果。

将查询分为可管理的部分 -您无需一次全部编写。复杂查询有时只能是简单查询的集合。

注意聚合的混合级别:如果必须将月度,季度值和年初至今的值放入同一结果集中,则需要在按不同值分组的查询中分别计算它们。

知道何时使用UNION有时将子组分成自己的select语句会更容易。如果您有一个包含经理和其他雇员的表,并且必须在每一列上基于这些组之一中的成员资格执行Case语句,则编写Manager查询并将其与Employee查询合并可能会更容易。每个人将包含自己的逻辑。必须在不同的行中包含来自不同表的项目是显而易见的用途。

复杂/嵌套公式 -尽量一致地缩进,不要害怕使用多行。“ CASE WHEN CASE WHEN CASE WHEN”会让您发疯。花一些时间思考这些。最后保存复杂的计算。首先获取正确的记录。然后,您将知道正确的值,从而攻击复杂的公式。查看公式中使用的值将帮助您发现必须考虑NULL值的区域以及处理零分误差的地方。

在添加新表时经常进行测试,以确保仍能获得所需的结果集,并知道哪个连接或子句是罪魁祸首。


1
真的很棒的东西。我想再次强调Jeff关于寻找LEFT联接并将复杂查询拆分为更小,更易于管理的查询,然后将它们组合的观点。我几乎每天都在大型数据库上编写大型查询,尤其是这两件事一直在出现。始终尽可能快地运行查询和子查询,以确保获得每个步骤都希望看到的数据。
CodexArcanum

@CodexArcanum-当您对大数据运行查询时,使用TOP并没有什么害处;)
JeffO 2012年

我同意您提出的所有建议
亚历山德罗·罗西

28
  1. 缩进将是第一件事,如果您尚未这样做的话。它不仅对简单的查询都有用,而且在连接和查询比a更复杂的时候至关重要select top 1 [ColumnName] from [TableName]

  2. 正确缩进后,在适当的情况下,禁止在查询内部添加注释。不要过度使用它们:如果代码足够明确,那么添加注释只会损害代码的清晰度。但是对于查询中不太明确的部分,仍然欢迎使用它们。

    请注意,较长的查询(包括带有注释的查询)将意味着您的应用程序服务器和数据库服务器之间的带宽使用量更大。另请注意,除非您正在使用每秒处理大量请求的Google规模产品,并且需要出色的性能和资源使用,否则注释所增加的大小可能不会对您造成任何影响。

  3. 在表,列等上强制使用相同的样式也大大提高了可读性。当旧的数据库有表PRODUCTusersUSERS_ObsoleteDONT_USEPR_SHIPMENTSHRhbYd_UU,有人在做一些非常错误的。

  4. 在查询上强制使用相同的样式也很重要。例如,如果您要编写Microsoft SQL Server查询,而您决定使用[TableName]而不是TableName,请坚持使用。如果您在后面换行select,请不要只在一半的查询中进行查询,而应全部查询。

  5. *除非有充分的理由(例如if exists(select * from [TableName] where ...)在Microsoft SQL Server中),否则不要使用。这不仅*会对某些(如果不是大多数)数据库造成负面的性能影响,而且对于使用您的查询的开发人员也无济于事。同样,开发人员必须按名称访问值,而不要按索引访问值。

  6. 最后,对于选择而言,提供视图没有错。对于其他任何事情,也可以使用存储过程,这取决于项目和与您一起工作的人¹。


¹有些人讨厌存储过程。其他人不喜欢它们有几个原因(至少对于他们来说是完全有效的)。

²您的同事,其他学生,您的老师等


9

这里有些暗处,但是如果您正在编写许多临时视图,您可能还没有意识到大多数地方都可以在SQL语句中放置表格,该表格可以用查询代替。

因此,可以将表A联接到一直用作临时视图B的查询,而不是将表A连接到临时视图B。例如:

    SELECT A.Col1, A.Col2, B.Col1,B.Col2
      FROM (SELECT RealTableZ.Col1, RealTableY.Col2, RealTableY.ID as ID
              FROM RealTableZ 
   LEFT OUTER JOIN RealTableY
                ON RealTableZ.ForeignKeyY=RealTableY.ID
             WHERE RealTableY.Col11>14
            ) As B
        INNER JOIN A
                ON A.ForeignKeyY=B.ID

这个例子是毫无意义的,但是应该解释语法。

对于非“特殊”视图(索引,分区),这将导致与使用视图相同的查询计划。

为了使编写起来更容易,您可以在编写整个查询之前验证每一部分,以确保获得期望的结果。

我很抱歉,如果这对您已经是旧帽子了。


3
我对SQL非常了解,我真的很讨厌这种缩进:它看起来不错,但“在我看来”完全没有用。原因有两个:我不清楚该左外部联接是主查询的一部分还是子查询的一部分,它需要代码美化器,并且每当您想添加几行时都需要重新美化所有文本。仅需要TABS的计划缩进更加灵活。我没有拒绝您的回答,但是我真的不鼓励任何使用这种风格的人……尤其是在他们需要我帮助的时候。
亚历山德罗·罗西

7

代替使用临时视图,请使用WITH子句。这使得将大型查询分解为可读性更高的较小部分变得更加容易。


1
如果确实使用cte,请注意该查询仅会持续到下一个查询运行为止,因此在某些情况下,如果您在多个查询中使用cte,则使用临时表可能会更好。
雷切尔

3
  1. 如果您还不熟悉集合论,请先熟悉一下。SQL以集合论为基础,对集合的更多了解将帮助您更加熟悉SQL的工作方式。
  2. 练习更多的SQl,如果您只是学习SQL,需要花一些时间来理解如何做所有事情,那么在真正理解它们之前就需要花费一些时间。Joins是一个很好的例子,您使用它们的次数越多,就越能理解它。
  3. 确保要查询的表设计正确
  4. 不要害怕在选择的查询上使用视图,特别是如果您有一个需要用多种不同方式优化的通用集时

1

像其他任何东西一样,您希望将问题分解为可管理的部分。

顺便说一句,这就是您解决复杂问题的方式。

因此:在运行外部查询之前,您想签出子查询以查看它确实返回了您想要的内容。您想尝试对要加入的每个表进行最小化的联接,以便可以看到您确实在认真地考虑它。像这样的东西。希望一次输入所有内容并准确地得到您想要的东西,这是不现实的。

一旦达到一定程度的复杂度,一条SQL语句本身就是一个小程序。真正了解如何组合,选择,过滤和输出数据将带来很大的不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.