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

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

Мы не используем представления, так что об этом не может быть и речи.

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

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


person MxLDevs    schedule 28.03.2011    source источник
comment
какая платформа? Существуют плагины для SSMS и MySQL Workbench, которые украсят ваш код SQL.   -  person Brian Driscoll    schedule 28.03.2011
comment
Хороший вопрос. Мне еще предстоит найти убедительное правило форматирования для SQL.   -  person René Nyffenegger    schedule 28.03.2011
comment
@ Брайан Дрисколл, какие плагины для MySQL Workbench вы знаете?   -  person dcarneiro    schedule 28.03.2011
comment
@ Брайан, я использую PL/SQL. Существуют ли какие-либо автономные скрипты или инструменты, которые украсят код? Где я мог бы просто вставить код, и он даст мне улучшенную версию.   -  person MxLDevs    schedule 28.03.2011
comment
stackoverflow.com/questions/392001/sql-string-formatter   -  person Unreason    schedule 28.03.2011
comment
лучший способ сделать код SQL более читабельным — это использовать стандарты внутри вашей команды. Существуют такие инструменты, как SQLinForm, которые поддерживают групповые стандарты с использованием профилей форматирования.   -  person Guido    schedule 01.06.2013


Ответы (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

person Aleksi Yrttiaho    schedule 28.03.2011
comment
Ключевое слово WITH интересно. Определенно делает код намного чище, поскольку я могу просто ссылаться на набор результатов по имени в последующих строках. - person MxLDevs; 28.03.2011
comment
CTE отлично подходят для улучшения читаемости запросов, но, к сожалению, иногда они могут скрывать ошибки и затруднять их отладку. Если в CTE возникает ошибка, вы получите сообщение об ошибке, указывающее на ваш запрос в целом, а не на то, где оно произошло. Если CTE являются вложенными, это еще больше усложняет отладку. - person jahu; 19.06.2020

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

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

Отвечает ли это на ваш вопрос?

person Data Monk    schedule 28.03.2011
comment
Похоже, что большинство людей согласны с пробелами! - person MxLDevs; 28.03.2011
comment
Мне нравится сосредотачиваться на атомарности каждой строки. Используя этот шаблон, я могу быстро сканировать иерархию в поисках узла, который я ищу, а затем детализировать. По моему опыту, основным недостатком этого является то, что распечатки съедают бумагу. Положительным моментом является то, что они редко переворачиваются или выходят за пределы страницы. - person Data Monk; 28.03.2011
comment
Что мне нравится в этом макете, так это то, что части запроса легко комментировать, когда вы пытаетесь выяснить, почему он не возвращает ожидаемые результаты. Всегда важно при написании сложных запросов. - person HLGEM; 29.03.2011

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

SELECT *
FROM tablename
WHERE value in
   (SELECT *
   FROM tablename2 
   WHERE condition)
ORDER BY column
person D.N.    schedule 28.03.2011

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

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

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

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

Логический отступ одного и того же уровня оператора (подзапросы, открывающие скобки и т. д.)

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

Действительно сложный 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'

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

person Unreason    schedule 28.03.2011

В целом я следую простому иерархическому набору правил форматирования. По сути, такие ключевые слова, как 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
    )
person RQDQ    schedule 28.03.2011

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

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
person JNK    schedule 28.03.2011

Единственный верный и правильный способ форматирования 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-запросов.

person Jeffrey Kemp    schedule 29.03.2011

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

person Beth    schedule 28.03.2011

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

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

person Kevin Roche    schedule 28.03.2011

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

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

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

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

Ваш пробег, конечно, будет варьироваться.

person Philip Kelley    schedule 28.03.2011

Вау, здесь много ответов, но я не видел во многих КОММЕНТАРИИ! Я обычно добавляю много комментариев, особенно к большим операторам SQL. Форматирование важно, но правильно размещенные и осмысленные комментарии чрезвычайно важны не только для вас, но и для бедолаги, которому нужно поддерживать код ;)

person tbone    schedule 29.03.2011