Форматирование понятных и читаемых SQL-запросов

Я пишу некоторые SQL-запросы с несколькими подзапросами и множеством соединений повсюду, как внутри подзапроса, так и в результирующей таблице из подзапроса.

мы не используем, так что это исключено.

после написания я смотрю на него и чешу голову, задаваясь вопросом, что он вообще делает, потому что я не могу следовать за ним.

какое форматирование вы используете, чтобы попытаться разгрести такой бардак? Возможно, отступы?

11 ответов


С большими запросами я склонен полагаться на именованные результирующие наборы, используя 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


мальчик, это загруженный вопрос. :) Есть так много способов сделать это правильно, как есть умные люди на этом сайте. Тем не менее, вот как я держу себя в здравом уме при создании сложных операторов 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

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

это ответ на твой вопрос?


псевдонимы таблиц и простая согласованность помогут вам пройти долгий, долгий путь

то, что выглядит прилично, - это разрыв строк на основных ключевых словах SELECT, FROM, WHERE (etc..).

соединения могут быть сложнее, отступ на части соединений выводит важную часть его на передний план.

разбиение сложных логических выражений (соединений и условий) на одном уровне также помогает.

отступ логически тот же уровень оператора (подзапросы, открытие скобок и т. д.)

использовать все ключевые слова и стандартные функции.

действительно сложный SQL не будет уклоняться от комментариев - хотя обычно вы найдете их в 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'

Итак, в конце подведем итог-согласованность имеет наибольшее значение.


Как правило, люди разбивают строки на зарезервированные слова и отступают от любых подзапросов:

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

В общем, я следую простому иерархическому набору правил форматирования. В принципе, ключевые слова, такие как SELECT, FROM, ORDER by all, идут по собственной линии. Каждое поле идет по своей линии (рекурсивно)

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
    )

Мне нравится использовать что-то вроде:

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

на только true и right способ форматирования 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
;

;)

серьезно, мне нравится использовать с предложениями (как уже предлагалось), чтобы приручить очень сложные SQL-запросы.


поместите его в представление, чтобы его было легче визуализировать, возможно, сохраните скриншот как часть документации. Вам не нужно сохранять представление или использовать его для каких-либо других целей.


отступ, конечно, но вы также можете разделить подзапросы с комментариями, сделать ваши псевдонимы действительно значимыми и указать, к какому подзапросу они относятся, например, innerCustomer, outerCustomer.

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


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

что касается подзапросов, в последнее время мне стало легче следить за тем, что происходит с "экстремальным" отступом и добавлением комментариев:

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

что касается другого цента, я использую только верхний регистр на первом слове. Со страницами запросов запуска, это делает его немного легче выбрать, когда начинается новый.

ваш пробег будет конечно, разные.


Вау, много ответов здесь, но одна вещь, которую я не видел во многих комментариях! Я, как правило, добавляю много комментариев, особенно с большими инструкциями SQL. Форматирование важно, но хорошо размещено и имеет смысл комментарии чрезвычайно важны не только для вас, но и для бедной души, которой нужно поддерживать код;)