Практически невозможно найти двух людей, которые отформатировали бы даже самый простой SQL-запрос одинаково. Причем каждый будет абсолютно уверен, что именно его стиль наиболее понятный и правильный. Что приводит к спорам и баталиям на code review, а самое главное к трудностям при чтении чужих запросов. Не существует и какого-нибудь большого авторитетного style-guide для SQL, какие существуют для других языков. И все решается в основном делом вкуса, о котором как известно не спорят. Возможно проблема в отсутствии теоретической основы, некого физического обоснования почему стоит придерживаться каких либо определенных правил при оформлении SQL кода. Давайте попробуем разобраться.


Примечание от переводчика:


Этот небольшой текст неожиданно вызвал довольно бурное обсуждение на Reddit'е, поэтому я решил поделиться им на Хабре.

Если я прихожу, например, на очередной Java-проект, то просто настраиваю авто форматирование в IDEA согласно внутренним или внешним стандартам кодирования, а за тем, чтобы у всех было все одинаково и понятно, следит внимательный и беспощадный Checkstyle на процессе сборки. Можно немного поспорить по поводу места открывающей фигурной скобки, однострочных if'ов для особо принципиальных, ну может быть еще несколько мелочей. Но по большому счету все более-менее уже успокоились, и в других языках и платформах дела обстоят примерно так же, но только не в SQL.


И это ожидаемо, ввиду декларативной природы SQL. В императивных языках мы определяем и контролируем порядок выполнения операций, и это напрямую отражается на форматировании — отступы, переносы, скобки и пр. Но при написании SQL запроса мы понятия не имеем в каком порядке он будет исполняться и это лишает нас важной точки опоры. В результате мы не имеем единого принятого стиля оформления, каждый опирается на своё чувство прекрасного.


Но почти все эти разные стили (каждый из которых хорош по своему) имеют одну общую деталь. В типографике это называется "коридор" и считается дефектом верстки. Для примера рассмотрим простой запрос. Коридор (отмечен красным) визуально разрывает наш запрос на 2 неаккуратных куска и взгляд (особенно читающего впервые) проваливается в него, теряется в соседних коридорах (прям как в Хогвартсе), тем самым отвлекая внимание от ключевых конструкций запроса и усложняя его понимание:



Но Легендарный Джо Селко в своей книге "Стиль программирования Джо Селко на SQL" предлагает пойти дальше и вытягивать SQL коридоры в вертикальные оси, что задает единую структуру всему запросу, конструктивно выделяет его отдельные части и облегчает его чтение и понимание. Рассмотрим тот же запрос, но с осью вместо коридора:



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



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


В этом посте я использую свой (наверняка не самый лучший) способ построения осей, но уверен, что вы найдете свой собственный более подходящий способ.

Комментарии (43)


  1. Xeldos
    12.02.2022 21:42
    +15

    А то, что глаз спотыкается о запятые на каждой строчке - так, мелочи. Главное, красивую линию в нужном месте провести.


    1. 411
      12.02.2022 21:48
      +2

      Да запятые эт отдельная тема для холивара. Те же trailing запятые (не в SQL, но к примеру в js) кто-то любит, кто-то нет (я). Кто-то для избавления от trailing запятых начинает добавлять leading запятые, примерно как в посте (не я).

      Тут и люди с отступами табами могут повозмущаться.

      Идея вертикальной линии мне в целом нравится, если редактор или IDE будет уметь это делать автоматом. Если нет, можно и не заморачиваться на мой взгляд.


    1. mgramin Автор
      12.02.2022 22:11

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


      1. WaterSmith
        12.02.2022 22:35
        +8

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

        Например, если в первом запросе (который с коридором) удалить строку j.name as job_name то очень легко забыть при этом удалить запятую в конце предыдущей строке. В запросе с осью, такой ситуации не возникнет.


        1. Xeldos
          13.02.2022 07:58

          Речь не о пользе, а о "красоте". И о том, что чтение строки начинается с запятой, а красивая линия проведена после нее.


          1. vasyakolobok77
            13.02.2022 12:40
            +1

            Красота всегда субъективна. Но кроме красоты здесь важна еще и функциональность, и подверженность ошибкам.


        1. shashurup
          13.02.2022 08:32
          +3

          До тех пор, пока не удалишь первое поле и будет лишняя лидирующая запятая. ИМХО, как-то принципиально проблема не решается, а выглядит ужасно.


          1. vasyakolobok77
            13.02.2022 12:41

            Большей частью это вкусовщина, но чаще столбцы добавляются и удаляются с конца, нежели с начала. Сужу сугубо по своей практике.


        1. Akina
          13.02.2022 17:59

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

          Лукавите.

          При лидирующих запятых и удалении полей из списка вывода дополнительные телодвижения нужны только в случае, когда удаляется самое первое из полей списка вывода (ну или несколько первых). Но при финализирующих запятых всё абсолютно так же - дополнительные телодвижения нужны лишь в случае, когда удаляется самое последнее из полей списка вывода (ну или несколько последних).

          Так что разницы - вообще никакой. И там, и там есть ситуация, когда "очень легко забыть при этом удалить запятую". Но забывчивость отдельно взятого программиста ну никак не может служить основанием для формирования или изменения стиля форматирования кода.

          Если же сравнивать такие два стиля по-настоящему, то разница между ними имеется. При ведущих запятых "ось" располагается между двумя группами символов. Тогда как при финализирующих запятых слева от оси символов не будет.

          Кстати, а как насчёт вторичных осей - в секции FROM? я насчёт выравнивания по вертикали кляуз ON. Или альтернативно - ON опускается на строку и выравнивается по оси справа. Т.е. соответственно

            FROM first_table t1
            JOIN table_2 t2     ON t1.f1 = t2.f1
            JOIN table_no_3 t3  ON t2.f2 = t3.f2

          или

            FROM table_1 t1
            JOIN table_2 t2 
                 ON t1.f1 = t2.f1
            JOIN table_3 t3
                 ON t2.f2 = t3.f2


        1. BASic_37
          13.02.2022 23:52

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


      1. ATwn
        13.02.2022 16:20

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


    1. XaBoK
      12.02.2022 23:21
      +4

      У лидирующей запятой есть плюс - легко закоментить конкретную строку или вклинить еще одну. Во времена динамического построения запросов - это было очень удобно. Такой вот модулярный формат получался.


    1. CORRECTOR86
      14.02.2022 10:07
      +1

      Истинно )))) Но автор - все равно красивчик


  1. MFilonen2
    12.02.2022 21:47
    +3

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


  1. EvilMan
    13.02.2022 00:37
    +17

    Давайте попробуем каждый отформатировать эти два запроса (попроще и посложнее), желательно с пояснениями. Может кому-то чьи-то идеи и понравятся и пригодятся. Начну, пожалуй.

    /* запрос попроще */
    SELECT
        e.first_name,
        e.last_name,
        e.salary,
        d.name AS department_name,
        j.name AS job_name
    FROM
        employee e
        JOIN departments d
            ON d.id = e.dept_id
        JOIN jobs j
            ON j.id = e.job_id
    WHERE
        d.name = 'IT' AND
        j.name = 'Engineer' AND
        e.salary > 100
    ;
    
    /* запрос посложнее */
    SELECT
        e.first_name,
        e.last_name,
        e.salary,
        d.name,
        j.name,
        (   SELECT
                SUM(o.cost)
            FROM
                orders o
            WHERE
                o.employee_id = e.id AND
                o.status = 'closed' ) AS sum_cost
    FROM
        employee e
        JOIN departments d
            ON d.id = e.dept_id
        JOIN jobs j
            ON j.id = e.job_id
    WHERE
        e.salary > (    SELECT
                            AVG(se.salary)
                        FROM
                            employee se
                            JOIN departments sd
                                ON sd.id = se.dept_it
                        WHERE
                            d.name = 'IT' )
    ;

    Основные моменты:

    • Служебные слова в верхнем регистре, чтобы за них глаз цеплялся

    • Визуальное разбиение запроса на секции (SELECT, FROM, WHERE, GROUP BY и т.п.)

    • Подзапросы должны бросаться в глаза

    • Точка с запятой на отдельной строке оставлена специально, чтобы при копи-пасте её забывать, и запрос не выполнялся сразу при вставке.


    1. unsignedchar
      13.02.2022 00:54

      Так лучше. Коридор с точки зрения типографики, возможно, красивое. Но лучше всё же видеть структуру заклинания.


    1. mentin
      13.02.2022 01:11
      +2

      То что надо, я так же форматирую, с одним изменением: таблицы employee и departments тут равнозначны (можно поменять местами, ничего не изменится), нет причины сдвигать правую часть JOIN вбок. Я форматирую

      FROM
          employee e
      JOIN
          departments d
          ON d.id = e.dept_id

      Или короткий вариант,

      FROM employee e
      JOIN departments d
          ON d.id = e.dept_id

      Общая идея, что FROM и JOIN равноуровневые конструкции.


      1. shashurup
        13.02.2022 08:29

        4 пробела, да - консистентно, но, имхо, ON как-то коряво, что-ли, висит.


    1. PsihXMak
      13.02.2022 01:26
      +3

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

      /* запрос попроще */
      SELECT e.first_name, e.last_name, e.salary,
             d.name AS department_name,
      	     j.name AS job_name
        FROM employee e
        JOIN departments d
          ON d.id = e.dept_id
        JOIN jobs j
          ON j.id = e.job_id
       WHERE d.name = 'IT' 
         AND j.name = 'Engineer' 
         AND e.salary > 100;
      
      /* запрос посложнее */
      SELECT e.first_name, e.last_name, e.salary,
             d.name, j.name,
             ( SELECT SUM(o.cost)
                 FROM orders o
                WHERE o.employee_id = e.id 
                  AND o.status = 'closed' 
             ) AS sum_cost
        FROM employee e
        JOIN departments d
          ON d.id = e.dept_id
      	
        JOIN jobs j
          ON j.id = e.job_id
         AND ...
         AND ...
         
        LEFT JOIN jobs2 j2
          ON j2.id = e.job_id	
       WHERE e.salary > ( SELECT AVG(se.salary)
                            FROM employee se
                            JOIN departments sd
                              ON sd.id = se.dept_it
                           WHERE d.name = 'IT' 
                        );
      • Служебные слова в верхнем регистре

      • Разбиение запросов и подзапросов на коридоры и блоки

      • Все служебные команды всегда с новой строки, за исключением AS

      • Пустой строкой можно выделять сложные блоки, требующие внимания

      • Скобки - всегда в одну линию

      • AND и OR - для каждой вложенности должен быть сделать отступ

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

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

      Схемка


      1. shashurup
        13.02.2022 08:26

        Мне идея с коридором тоже понравилась, до тех пор пока я LEFT OUTER JOIN не написал :(


        1. PsihXMak
          13.02.2022 10:00

          В моём варианте эта проблема решена

          SELECT *
            FROM tabel1 t1
            LEFT OUTER JOIN table2 t2
              ON t2.id = t1.id
           RIGHT INNER JOIN table3 t3
              ON t3.id = t1.id
           WHERE ...


          1. shashurup
            13.02.2022 10:20
            +2

            Дык коридор-то не получился - table2 и table3 не под table1.


            1. PsihXMak
              13.02.2022 11:40
              +3

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

              Из своего опыта я пришёл к выводу, что читаемая структура запроса намного важнее содержания запроса, по этому, использую блоки внутри коридора. В этих блоках форматирование свободное.

              Например так

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


              1. shashurup
                13.02.2022 11:50
                +1

                Да, структура действительно хорошо простматривается. Попробую, спасибо!


    1. RekGRpth
      13.02.2022 06:30

      я вот так бы отформатировал

      /* запрос попроще */
      SELECT  e.first_name,
              e.last_name,
              e.salary,
              d.name AS department_name,
              j.name AS job_name
      FROM    employee e
      JOIN    departments d ON d.id = e.dept_id
      JOIN    jobs j ON j.id = e.job_id
      WHERE   d.name = 'IT' AND
              j.name = 'Engineer' AND
              e.salary > 100;
      /* запрос посложнее */
      SELECT  e.first_name,
              e.last_name,
              e.salary,
              d.name,
              j.name,
              (
                  SELECT  SUM(o.cost)
                  FROM    orders o
                  WHERE   o.employee_id = e.id
                  AND     o.status = 'closed'
              ) AS sum_cost
      FROM    employee e
      JOIN    departments d ON d.id = e.dept_id
      JOIN    jobs j ON j.id = e.job_id
      WHERE   e.salary > (
          SELECT  AVG(se.salary)
          FROM    employee se
          JOIN    departments sd ON sd.id = se.dept_it
          WHERE   d.name = 'IT'
      );


    1. IsUnavailable
      13.02.2022 12:16
      +2

      /* запрос попроще */
      select e.first_name as first_name,
             e.last_name  as last_name,
             e.salary	    as salary,
             d.name       as department_name,
             j.name       as job_name
        from employee e
        join departments d
          on d.id = e.dept_id
        join jobs j
          on j.id = e.job_id
       where d.name = 'IT' 
         and j.name = 'Engineer'
         and e.salary > 100;
      
      /* запрос посложнее */
      select e.first_name,
             e.last_name,
             e.salary,
             d.name,
             j.name,
             (select sum(o.cost)
                from orders o
               where o.employee_id = e.id 
                 and o.status = 'closed') as sum_cost
        from employee e
        join departments d
          on d.id = e.dept_id
        join jobs j
          on j.id = e.job_id
       where e.salary > (select avg(se.salary)
                           from employee se
                           join departments sd
                             on sd.id = se.dept_it
                          where d.name = 'IT');
                          
      /* пример case */
      select case
               when col_1 = 1
               then 1
               when col_1 = 2 and col_2 = 1
               then 2
               when col_1 = 3 
                and col_2 = 1 
                and col_3 = 2 
                and col_4 = 3
               then 3
               else 4
              end as test_case
        from test_table
      1. Служебные слова не выделяются регистром. Это вроде как принято сообществом за условие по умолчанию, но пользуясь приведённым мной кодстайлом несколько лет, я не вижу смысла в выделении капсом. Служебные слова выделяются цветом. Это одновременно действенно и не раздражает глаз при просматривании кода.

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

      2. Тот самый "коридор". Это красиво) И в то же время это дополнительный уровень отделения служебных слов от остального текста. К тому же, коридор довольно простой в распознавании и написании принцип. Все служебные слова выстраиваются вдоль него, а не по какой-то сложной, ветвистой логике.

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

      Всё описанное - ИМХО, да и вообще, тема стайлгайдов как по мне хоть и имеет под собой какую-то объективную базу, но как будто бы, эта база совсем небольшая (если она там вообще есть), а вот всю остальную долю темы занимает субъективное восприятие людей, которое довольно хорошо подстраивается под разные условия. Если бы мне приходилось писать несколько лет запросы в стиле EvilMan то, вероятно, я бы так же находил плюсы в этом стиле и не хотел писать по другому :)

      P.S. редактор комментариев хабра для меня загадка. При написании/редактировании подсветка SQL кода одна, а после отправки комментария - другая. Да ещё и после отправки отредактированного комментария подсветка вообще слетает, и появится только после обновления страницы.


    1. vasyakolobok77
      13.02.2022 12:43

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


    1. KislyFan
      13.02.2022 13:01
      +1

      На правах ИМХО

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

      За несколько лет работы с SQL скриптами я пришел к похожему форматированию, особенно после того как пришлось разбираться со скриптами уволившихся коллег

      -- в SELECT разбиваем 100500 выводимых колонок на логические группы, 
      -- каждая из которых начинается с новой строки
      SELECT e.first_name, e.last_name
            ,e.salary
            -- любое изменение будь то введение alias'a, формула и тд 
            -- пишем с новой строки, один столбец - ода строка
            ,d.name AS departament_name
            ,j.name AS job_name
      
      -- также одна строка отступа перед FROM и перед каждым очередным JOIN
      -- это визуально разделяет запрос на группы
      FROM employee e
      
      JOIN departments d
          -- магическая строка 1 = 1 всегда равна TRUE и потому игнорируется
          -- но позволяет в КАЖДОЙ строке условия писать AND в начале строки
          -- по той же причине по которой мы пишем "," в начале строки в SELECT  
          ON 1 = 1
          AND d.id = e.dept_id
          
      JOIN (
          -- в простых запросах скорее всего нет смысла делать отступы перед FROM или WHERE 
          -- это усложнит чтение
          SELECT * 
          FROM jobs
          WHERE 1 = 1 
          AND REGEXP_LIKE(name, '([^=,]+)', 1, 4) = 'WTF'
      ) j
          ON 1 = 1 
          AND j.id = e.job_id
          
      WHERE 1 = 1
          AND d.name = 'IT' 
      --  AND e.last_name <> 'Ivanov'
          AND j.name = 'Engineer' 
          AND e.salary > 100

      Так же с недавнего времени пришел к выводу, что если мы не придерживаемся синтаксиса полного именования

      schema.departament.name = schema.wfttable.departament

      а пользуемся псевдонимами для таблиц, то это очень быстро родит путаницу с названиями полей. Поэтому на мой взгляд, несмотря на лаконичность названий id, name, description и других "общих" названий и зарезервированных ключевых слов, стоит называть их как departament_id, departament_name, departament_description и тд.


      1. shane54
        13.02.2022 15:52

        С условием "1=1" в каждом запросе "для красоты" (для удобства последующего комментирования одного или другого условия WHERE) нужно быть осторожным, как минимум в Oracle иногда, в совсем сложных случаях, добавление условия "1=1" является "трюком", с помощью которого заставляют оптимизатор поменять свое поведение в ту или иную сторону. Не вдаваясь в детали, просто стоит помнить что "1=1" может иметь значение для выполнения конкретного SQL запроса.


        1. KislyFan
          13.02.2022 16:05

          Я сравнивал планы запроса и что-то не заметил разницы. Может быть были неподходящие примеры..


        1. nikolayv81
          14.02.2022 10:08

          Это трюк для того чтобы перестать использовать сохранённый план, т.к. hash запроса меняется, может помочь с т.н. "прибитыми гвоздями" планами, но если лезть в такие дебри, то тут уже не до красоты....

          В дополнение про стиль, сам предпочитаю для join использовать:

          1. Отступ от линии from на два пробела

          2. Написание inner join/left join

          3. on в новой строчке с отступом 2 пробела от линии join

          4. В условии on для условий длиной более 1 сравнения используем заключение каждого условия в скобки, это позволяет выровнять визуально линию and/or-ов

          5. Для or всегда использую скобки, что бы в итоге не получить выгрузку во всю таблицу из-за неудачного редактирования списка из 10 and-ов...

          select --+ hint
               a.a -- comment A
             , b.b
            from tableA a
              left join tableB b
                on ( ( b.c = a.c      )
                 and ( ( b.d >  100 )
                    or ( b.x <> a.x ) ) )
            where ( ( a.y like 'n%' )
                and ( a.z    = 1    ) )
            

          Т.е. по факту у нас избыток скобок (что к примеру ломает автоподмтановку в Terrada SQL Assistant, Но это мелочи), но при этом более менее видны границы условий что важно на некоторых запросах под 100 строк с CTE (with)


    1. Akina
      13.02.2022 18:13

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

      То есть правильно

      WHERE {condition 1}
        AND {condition 2}

      а вовсе даже не

      WHERE {condition 1} AND
            {condition 2}

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

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


    1. Kilor
      14.02.2022 09:03
      +1

      Мои 5 копеек (вот тут живет форматтер по этим правилам):

      SELECT
        e.first_name
      , e.last_name
      , e.salary
      , d.name department_name
      , j.name job_name
      FROM
        employee e
      JOIN
        departments d
          ON d.id = e.dept_id
      JOIN
        jobs j
          ON j.id = e.job_id
      WHERE
        d.name = 'IT' AND
        j.name = 'Engineer' AND
        e.salary > 100;
      
      SELECT
        e.first_name
      , e.last_name
      , e.salary
      , d.name
      , j.name
      , (
          SELECT
            sum(o.cost)
          FROM
            orders o
          WHERE
            o.employee_id = e.id AND
            o.status = 'closed'
        ) sum_cost
      FROM
        employee e
      JOIN
        departments d
          ON d.id = e.dept_id
      JOIN
        jobs j
          ON j.id = e.job_id
      WHERE
        e.salary > (
          SELECT
            avg(se.salary)
          FROM
            employee se
          JOIN
            departments sd
              ON sd.id = se.dept_it
          WHERE
            d.name = 'IT'
        );
      • служебные слова в uppercase, чтобы сразу бросались в глаза

      • смещение "уровня" в 2 пробела

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

      • AS убирается везде, где это допустимо

      • WITH-SELECT-FROM-JOIN-WHERE-GROUP-HAVING-ORDER-LIMIT задают первый уровень запроса, таблицы и WHERE-условия - второй, ON - третий

      • подзапросы всегда с новой строки


  1. shavluk
    13.02.2022 03:57
    +2

    Как будем LEFT JOIN форматировать?


    1. shashurup
      13.02.2022 08:27

      LEFT OUTER JOIN  - еще веселей и реалистичнее.


      1. pankraty
        13.02.2022 08:56
        +5

        Ну, OUTER в принципе избыточно, т.к. LEFT подразумевает OUTER. Но ваш вопрос вполне актуален для случаев, когда нам надо написать LEFT MERGE JOIN или INNER HASH JOIN, например.

        Лично мне вариант с коридорами неудобен тем, что от замены одного оператора может поползти верстка большого куска запроса (в пределе - запроса целиком). Заменил один LEFT на INNER - и либо нарушил аккуратное форматирование ("ад перфекциониста"), либо получил diff на 200 строк.


        1. shashurup
          13.02.2022 09:10
          +1

          Лично мне вариант с коридорами неудобен тем, что от замены одного оператора может поползти верстка большого куска запроса (в пределе - запроса целиком). Заменил один LEFT на INNER - и либо нарушил аккуратное форматирование ("ад перфекциониста"), либо получил diff на 200 строк.

          Кстати, да - очень весомый аргумент против коридора.


        1. shashurup
          13.02.2022 09:18
          +2

          Ну, OUTER в принципе избыточно, т.к. LEFT подразумевает OUTER.

          вот блин, всегда явно писал OUTER :-)

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


        1. Tellamonid
          13.02.2022 18:48

          Коридор же не так работает. Пусть мы пишем простой селект. Тогда слово select будет начинаться прямо с начала строки, перед from будет два пробела, перед where – один, перед having – ни одного, перед left – два, и так далее. То есть от замены join на left join ничего не поползёт. Поползти может только если будет ключевое слово больше шести букв длиной, но это достаточно редко бывает.

          select t1.a,
                 t2.b,
                 count(1) as cnt
            from t1
            left join t2
              on (t1.my_key = t2.my_key)
           where t1.city = 'CHICAGO'
          having count(1) > 1


  1. Ustas4
    13.02.2022 09:12
    +3

    Холивар вечен.

    Можете форматировать как хотите. Но тут два фактора

    1. Работает старый код? Не тронь

    2. Корпоративное форматирование в приоритете


  1. kisskin
    13.02.2022 10:19


  1. shane54
    13.02.2022 12:26
    +2

    В упомянутом в статье обсуждении на Reddit (How do you format your SQL queries?), в комментах есть ссылка на библиотеку Poor Man's T-SQL Formatter - она представлена и в виде CLI, для подключения в системы сборки, в виде плагина для Visual Studio и SSMS - а также в виде online формы, куда можно скопировать "сырой" запрос и тут же увидеть результат. Повставлял туда запросы из комментов к этой статье - интересно, работает, можно всякие опции налету менять и сразу видеть результат. В общем, рекомендую пощелкать, интересно.

    Правда, в описании этой библиотеки Poor SQL, автор честно признается - парсер на данный момент достаточно примитивный и множество функций не реализовано. Последняя активность в репозитории на GitHub около 2.5 лет назад.

    И из этого же обсуждения на Reddit - ещё пара ссылок на релевантные статьи, чтоб уж всё полезное по теме собрать в одном месте:


  1. shane54
    13.02.2022 13:02
    +1

    И ещё вот такая мысль: в большинстве основных IDE есть кнопка "сделать красиво" - в смысле, запускается линтер и SQL запрос форматируется соответственно заданным правилам. На сколько я понимаю, нет единого стандарта для конфигов этих линтеров - в Toad for Oracle свой, в PL/SQL Developer свой, в Oracle SQL Developer свой, в IDE от JetBrains (вроде DataGrip) свой - и тд. (перечислил только инструменты для работы с Oracle, бо это моё направление). Так вот, при работе в команде, обычно же все должны следовать принятым стандартам - всякие Code Style Guide, именование версий, формат тикетов, комментов к коммитам и тд и тп. И вот для общего стандартного форматирования SQL запросов, конфиг файл с правилами для SQL линтера в IDE хранят и версионируют так же, как и другие такого рода конфиги. И новый член команды, приходя на проект и настраивая свое рабочее окружение, кроме всего прочего берет последнюю версию конфига для линтера и настраивает свою IDE "как у всех". И очевидно что проблема тут - поддержка всех разных форматов этих конфигов, если разрешено использовать IDE по своему вкусу. Будь формат конфигов стандартизирован, можно было бы и внутри проектных команд удобно и просто поддерживать свой SQL Style Guide, обновляя и дополняя его по ходу эволюции - так и "где-то в интернетах" могли бы накапливаться конфиги со всякими "стиль как в Google, в Facebook, в Microsoft" и тд - как сейчас есть различные Style Guide'ы по основным языкам программирования от больших компаний.