【问题标题】:Formatting Clear and readable SQL queries格式化清晰易读的 SQL 查询
【发布时间】:2011-07-24 14:32:41
【问题描述】:

我正在编写一些带有多个子查询和大量连接的 SQL 查询,包括子查询内部和子查询生成的表。

我们没有使用视图,所以这是不可能的。

写完后,我看着它,挠头想知道它在做什么,因为我无法理解它。

您使用哪种格式来尝试清理这种混乱?可能是缩进?

【问题讨论】:

  • 什么平台? SSMS 和 MySQL Workbench 的插件将为您“美化”您的 SQL 代码。
  • 好问题。我自己还没有找到一个令人信服的 SQL 格式化规则。
  • @Brian Driscoll,你知道 MySQL Workbench 的哪些插件?
  • @Brian,我正在使用 PL/SQL。是否有任何独立的脚本或工具可以美化代码?我可以在哪里粘贴代码,它会给我一个美化的版本。

标签: sql plsql code-cleanup


【解决方案1】:

哇,这里有很多回复,但我在很多人中没有看到的一件事是评论!我倾向于在整个过程中添加很多 cmets,尤其是对于大型 SQL 语句。格式化很重要,但放置得当且有意义的 cmets 非常重要,不仅对你,对需要维护代码的可怜人来说也是如此;)

【讨论】:

    【解决方案2】:

    唯一正确格式化 SQL 的方法是:

    SELECT t.mycolumn        AS column1
          ,t.othercolumn     AS column2
          ,SUM(t.tweedledum) AS column3
    FROM   table1 t
          ,(SELECT u.anothercol
                  ,u.memaw                  /*this is a comment*/
            FROM   table2       u
                  ,anothertable x
            WHERE  u.bla       = :b1        /*the bla value*/
            AND    x.uniquecol = :b2        /*the widget id*/
           ) v
    WHERE  t.tweedledee = v.anothercol
    AND    t.hohum      = v.memaw
    GROUP BY t.mycolumn
            ,t.othercolumn
    HAVING COUNT(*) > 1
    ;
    

    ;)

    说真的,我喜欢使用 WITH 子句(如前所述)来驯服非常复杂的 SQL 查询。

    【讨论】:

      【解决方案3】:

      男孩这是一个加载的问题。 :) 这个网站上有多少聪明人,就有多少方法可以做到这一点。也就是说,在构建复杂的 sql 语句时,我是这样保持理智的:

      select
          c.customer_id
         ,c.customer_name
         ,o.order_id
         ,o.order_date
         ,o.amount_taxable
         ,od.order_detail_id
         ,p.product_name
         ,pt.product_type_name
      from
          customer c
      inner join
          order o
          on c.customer_id = o.customer_id
      inner join
          order_detail od
          on o.order_id = od.order_id
      inner join
          product p
          on od.product_id = p.product_id
      inner join
          product_type pt
          on p.product_type_id = pt.product_type_id
      where
          o.order_date between '1/1/2011' and '1/5/2011'
      and
          (
              pt.product_type_name = 'toys'
           or
              pt.product_type_name like '%kids%'
          )
      order by
          o.order_date
         ,pt.product_type_name
         ,p.product_name
      

      如果您有兴趣,我可以发布/发送插入、更新和删除以及相关子查询和复杂连接谓词的布局。

      这能回答你的问题吗?

      【讨论】:

      • 似乎大多数人都同意空格!
      • 我喜欢关注每一行的原子性。使用此模式,我可以快速扫描层次结构以查找要查找的节点,然后进行钻取。以我的经验,这样做的主要缺点是打印输出会吃纸。从积极的方面来说,它们很少包裹或溢出页面。
      • 我喜欢这种布局的一点是,当您试图找出为什么它没有返回预期结果时,可以很容易地注释掉部分查询。编写复杂查询时始终很重要。
      【解决方案4】:

      一个古老的问题,有一千种意见,没有一个正确的答案,我最喜欢的问题之一。这是我的两分钱。

      关于子查询,最近我发现使用“极端”缩进和添加 cmets 更容易理解:

      SELECT mt.Col1, mt.Col2, subQ.Dollars
       from MyTable1 mt
        inner join (--  Get the dollar total for each SubCol
                    select SubCol, sum(Dollars) Dollars
                     from MyTable2
                     group by SubCol) subQ
         on subQ.SubCol = mt.Col1
       order by mt.Col2
      

      至于其他分,我只在第一个单词上使用大写。通过连续查询页面,可以更轻松地确定何时开始新的查询。

      当然,您的里程会有所不同。

      【讨论】:

        【解决方案5】:

        对于大型查询,我倾向于非常依赖使用WITH 的命名结果集。这允许预先定义结果集,并使主查询更简单。命名结果集也可能有助于使查询计划更有效,例如postgres 将结果集存储在临时表中。

        例子:

        WITH 
          cubed_data AS (
             SELECT 
                dimension1_id,
                dimension2_id,
                dimension3_id,
                measure_id,
                SUM(value) value
             FROM
                source_data
             GROUP BY
                CUBE(dimension1, dimension2, dimension3),
                measure
          ), 
          dimension1_label AS(
             SELECT 
                dimension1_id,
                dimension1_label
             FROM 
                labels 
             WHERE 
                object = 'dimension1'
          ), ...
        SELECT 
          *
        FROM  
          cubed_data
          JOIN dimension1_label USING (dimension1_id)
          JOIN dimension2_label USING (dimension2_id)
          JOIN dimension3_label USING (dimension3_id)
          JOIN measure_label USING (measure_id)
        

        这个例子有点做作,但我希望它比内联子查询更清楚。当我为 OLAP 使用准备数据时,命名结果集对我有很大帮助。如果您有/想要创建递归查询,则还必须使用命名结果集。

        WITH 至少适用于当前版本的 Postgres、Oracle 和 SQL Server

        【讨论】:

        • WITH 关键字很有趣。绝对让代码更简洁,因为我可以在后续行中按名称引用结果集。
        • CTE 非常适合提高查询的可读性,但不幸的是,它们有时会掩盖错误并使它们更难调试。如果 CTE 中发生错误,您将收到一个错误,该错误通常指向您的查询,而不是显示它发生的位置。如果 CTE 是嵌套的,那就更难调试了。
        【解决方案6】:

        表别名和简单的一致性会让你走得很远

        看起来不错的是主要关键字 SELECT、FROM、WHERE(等等)的断行。

        连接可能比较棘手,缩进连接的 ON 部分会将其重要部分放在前面。

        在同一级别上分解复杂的逻辑表达式(连接和 where 条件)也有帮助。

        在逻辑上缩进相同级别的语句(子查询、左括号等)

        将所有关键字和标准函数大写。

        真正复杂的 SQL 不会回避 cmets - 尽管通常您会在 SQL 脚本中发现这些而不是动态 SQL。

        编辑示例:

        SELECT a.name, SUM(b.tax)
        FROM   db_prefix_registered_users a 
               INNER JOIN db_prefix_transactions b 
                   ON a.id = b.user_id
               LEFT JOIN db_countries
                   ON b.paid_from_country_id = c.id
        WHERE  a.type IN (1, 2, 7) AND
               b.date < (SELECT MAX(date) 
                         FROM audit) AND
               c.country = 'CH'
        

        所以,最后总结一下 - 一致性最重要。

        【讨论】:

          【解决方案7】:

          当然可以缩进,但您也可以使用 cmets 拆分子查询,使您的别名真正有意义,并指定它们引用的子查询,例如内部客户,外部客户。

          公用表表达式在某些情况下确实有助于将查询分解为有意义的部分。

          【讨论】:

            【解决方案8】:

            我喜欢使用类似的东西:

            SELECT    col1,
                      col2,
                      ...
            FROM
                MyTable as T1
            INNER JOIN
                MyOtherTable as T2
                    ON t1.col1 = t2.col1
                    AND t1.col2 = t2.col2
            LEFT JOIN
                (   
                    SELECT 1,2,3
                    FROM Someothertable
                    WHERE somestuff = someotherstuff
                ) as T3
                ON t1.field = t3.field
            

            【讨论】:

              【解决方案9】:

              一般来说,我遵循一组简单的分层格式规则。基本上,SELECT、FROM、ORDER BY 等关键字都在自己的行中。每个字段都有自己的一行(以递归方式)

              SELECT 
                  F.FIELD1,
                  F.FIELD2,
                  F.FIELD3
              FROM
                  FOO F 
              WHERE 
                  F.FIELD4 IN 
                  (
                      SELECT 
                          B.BAR
                      FROM 
                          BAR B
                      WHERE
                          B.TYPE = 4
                          AND B.OTHER = 7
                  )
              

              【讨论】:

                【解决方案10】:

                将它放在一个视图中以便更容易可视化,也许将屏幕截图作为文档的一部分。您不必保存视图或将其用于任何其他目的。

                【讨论】:

                  【解决方案11】:

                  通常,人们在保留字上换行,并缩进任何子查询:

                  SELECT *
                  FROM tablename
                  WHERE value in
                     (SELECT *
                     FROM tablename2 
                     WHERE condition)
                  ORDER BY column
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-12-10
                    • 1970-01-01
                    • 2014-04-10
                    • 1970-01-01
                    • 2021-07-08
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多