Эффективное программирование на T-SQL: Правила оформления кода

Правила оформления кода T-SQL

«Если Вы хотите иметь то, что никогда не имели, — начните делать то, что никогда не делали».

Ричард Бах

Как называть объекты и элементы данных в процессе программирования на языке T-SQL в Microsoft SQL Server, мы разобрались, теперь давайте поговорим о том, как оформлять свои инструкции и SQL запросы, иными словами, весь код, который мы пишем на T-SQL.

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

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

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

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

Первый запрос

select productid,productname from (select distinct productid,productname,category from testtable where price=l00) as testtable inner join testtable2 ON testtable.category=testtable2.categoryid

Второй запрос

 

SELECT ProductId, ProductName 
FROM (SELECT DISTINCT ProductId, ProductName, Category 
    FROM TestTable1 
    WHERE Price = 100) AS T1 
INNER JOIN TestTable2 AS T2 ON T1.Category = T2.CategoryId

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

Сейчас я расскажу, как же оформлять код на T-SQL, чтобы его можно было легко читать и сопровождать.

Регистр - строчные и прописные буквы

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

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

Все операторы языка T-SQL и зарезервированные слова пишите прописными буквами

Операторы, управляющие конструкции, системные функции и ключевые слова в T-SQL необходимо четко визуально выделять, так как именно они формируют алгоритм действий Ваших инструкций и SQL запросов. Таким образом, чтобы программист, который будет читать код T-SQL, смог быстро и, главное, четко увидеть (прочитать) общий алгоритм действий, нужно записывать все такие системные слова в верхнем регистре, ведь слова, записанные ЗАГЛАВНЫМИ буквами, больше всего привлекают внимание.

Это одно из основных правил оформления T-SQL инструкций!

Для примера, к словам, которые необходимо так записывать, можно отнести:

  • SELECT
  • INSERT

  • UPDATE

  • DELETE

  • FROM

  • WHERE

  • JOIN

  • GROUP BY
  • ORDER BY
  • DISTINCT

  • TOP

  • ROLLUP

  • IF ELSE

  • ON

  • AS

  • SUBSTRING

  • CASE

  • И все-все подобные слова!

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

Например, сравните этот запрос

select distinct productid, productname from goods where price = 100

с этим, в котором я применил только одно правило.

SELECT DISTINCT productid, productname FROM goods WHERE price = 100

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

Имена объектов схемы начинайте с прописной буквы

Когда мы пишем предложение, то имена собственные принято начинать с заглавной буквы, все это знают и все к этому привыкли, в противном случае текст для читателя становится менее понятным, и у него могут возникнуть вопросы. Поэтому представьте, что в T-SQL объекты БД - это собственные имена, и их необходимо начинать с заглавной буквы, чтобы визуально выделить.

К объектам схемы относятся - таблицы, представления, функции, хранимые процедуры и так далее. Если имя состоит из двух слов, то тогда каждое слово начинайте с заглавной буквы или отделяйте их нижним подчеркиванием. Таким образом, имя таблицы testable превращается в TestTable, что более наглядно и понятно.

Пишите названия столбцов так, как они указаны в определении таблицы

Если столбец назван ProductId, так и пишите в запросах и инструкциях, не нужно писать productid или PRODUCTID, так как это сбивает с толку читателя, и ему приходится присматриваться к названию столбца, мысленно форматировать его, чтобы четко определить, что это за столбец.

Если столбец назван с нижним подчёркиванием строчными буквами, так и записывайте, для примера, если бы мы назвали столбец product_id, что вполне допустимо, то и во всех запросах нужно писать именно так, а не Product_Id или Product_ID.

Константные переменные называйте и записывайте заглавными буквами

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

@MAX_NUMBER_ORDERS

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

Обычные переменные и параметры пишите так, как они указаны при объявлении

Если переменную во время объявления Вы назвали @order_id, то не стоит в процессе ее использования писать @Order_ID, что вполне допустимо SQL сервером. Так как, если переменная в разных участках кода будет записана по-разному, у Вас может сложиться впечатление, что это две разных переменных.

Отступы и пробелы

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

Некоторые могут сказать, что использование пробелов и отступов увеличивает объем всего кода. Да, но это не скажется на производительности T-SQL инструкции, в Microsoft SQL Server наличие лишних пробелов в коде никак не влияет производительность. Поэтому использование отступов и пробелов также является очень важным при форматировании кода T-SQL.

Начинайте каждую логическую конструкцию, каждую секцию с новой строки

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

Вот пример, который плохо отформатирован в части пробелов и отступов

   SELECT T1.ProductId,T1.ProductName,T2.CategoryName FROM Goods AS T1 
INNER JOIN Categories AS T2 ON T1.Category=T2.CategoryId WHERE T1.Price=100

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

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

SELECT T1.ProductId, T1.ProductName, T2.CategoryName
FROM Goods AS T1
INNER JOIN Categories AS T2 ON T1.Category = T2.CategoryId
WHERE T1.Price = 100

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

 

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

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

Пример, где все сливается

 

SELECT ProductId,ProductName,CategoryName,Price
FROM Goods

Пример, где каждый столбец четко отделен от другого

 

SELECT ProductId, ProductName, CategoryName, Price
FROM Goods

Кроме того, запятую пишите строго после названия столбца, не нужно делать пробел перед запятой или, что еще хуже, переносить ее на новую строку. Так как при написании обычного текста делается именно так, странно же будет, если перед запятой будет стоять пробел или с нее начнется новая строка. При написании SQL запросов это также выглядит странно, что и делает код менее читабельным. Иными словами, весь смысл заключается в соблюдении естественных правил пунктуации.

Пример странной расстановки запятых

 

SELECT ProductId , ProductName ,CategoryName ,
Price
FROM Goods

Пример естественной расстановки запятых

SELECT ProductId, ProductName, CategoryName, Price
FROM Goods

 

Отделяйте пробелами операторы и значения

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

WHERE Price=100 AND Category=1

или

T1.Category=T2.CategoryId

или

SET @ProductId=1;

Лучше четко отделите оператор пробелами:

WHERE Price = 100 AND Category = 1

или

T1.Category = T2.CategoryId

или

SET @ProductId = 1;

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

Каждое условие WHERE начинайте с новой строки

Очень часто SQL запрос состоит из множества условий, и, если все эти условия записаны в неотформатированном виде, их корректировка становится серьёзной проблемой.

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

 

SELECT ProductId, ProductName, CategoryName, Price FROM Goods
WHERE Price = 100 AND Category = 1 AND ProductActive = 1 AND Discount > 5 AND DAY(DeliveryDate) = 15

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

 

SELECT ProductId, ProductName, CategoryName, Price
FROM Goods
WHERE Price = 100
   AND Category = 1
   AND ProductActive = 1
   AND Discount > 5
   AND DAY(DeliveryDate) = 15

 

Каждое новое объединение JOIN начинайте с новой строки

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

 

SELECT G.ProductId, G.ProductName, C.CategoryName, S.SectionName
FROM Goods AS G
INNER JOIN Categories AS C ON G.Category = C.CategoryId INNER JOIN Sections AS
S ON C.Section = S.SectionId
WHERE G. Price = 100

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

Поэтому обязательно пишите каждое объединение JOIN в отдельной строке, как в примере ниже.

 

SELECT G.ProductId, G.ProductName, C.CategoryName, S.SectionName
FROM Goods AS G
INNER JOIN Categories AS C ON G.Category = C.CategoryId
INNER JOIN Sections AS S ON C.Section = S.SectionId
WHERE G.Price = 100

 

Команды, идущие после определения INSERT или MERGE, отделяйте отступами

Иными словами, инструкции INSERT и MERGE должны иметь первый уровень по вертикали. Таким образом, например, перед VALUES и USING необходимо делать отступ, я не говорю уже о том, что они должны начинаться с новой строки. Тем самым конструкция приобретает более наглядный вид, особенно, если определение всей инструкции будет велико. Это достигается за счет того, что, просматривая всю инструкцию, Вам достаточно провести глазами по левому краю, чтобы определить, где начало того или иного действия.

Неплохо

 

--другие инструкции

INSERT INTO Goods (ProductName, Price)
VALUES (ProductName, Price);

--другие инструкции

Но так лучше

 

--другие инструкции

INSERT INTO Goods (ProductName, Price)
     VALUES (ProductName, Price);

--другие инструкции

Если после INSERT будет идти SELECT или OUTPUT, то также вставляйте перед ними отступ. Это относится и к другим операциям модификации данных, т.е. если после UPDATE идет OUTPUT, то его также стоит сдвинуть немного вправо.

Если столбцов в SELECT много, выстраивайте их по вертикали

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

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

 

SELECT
ProductId,ProductName,CategoryId,CategoryName,Price,ProductDescripti on,ProductActive,DeliveryDate, Discount,Section,Column11,Column12 FROM Goods

Чтобы упростить этот запрос для понимания, можно записать его следующим образом:

 

SELECT ProductId, ProductName,
    ProductDescription, ProductActive, 
    DeliveryDate, Price, Discount, 
    CategoryId, CategoryName, Section, 
    Column11, Column12
FROM Goods

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

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

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

 

SELECT ProductId,
       ProductName,
       ProductDescription,
       ProductActive,
       DeliveryDate,
       Price,
       Discount,
       CategoryId,
       CategoryName,
       Section,
       Column11,
       Column12
FROM Goods

 

При перечислении столбцов в INSERT выстраивайте их по вертикали

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

Вместо чего-то подобного

 

     INSERT INTO TestTable 
(ProductName,CategoryId,CategoryName,Price,ProductDescription,ProductActive,Del iveryDate,Discount,Section,Column11,Column12)
     VALUES 
(ProductName,CategoryId,CategoryName,Price,ProductDescription,ProductActive,Del iveryDate,Discount,Section,Column11,Column12);

Пишите как-то так

 

INSERT INTO TestTable (ProductName, ProductDescription, ProductActive,
                      DeliveryDate, Price, Discount,
                      CategoryId, CategoryName, Section, Column11, Column12)
      VALUES (ProductName, ProductDescription, ProductActive,
             DeliveryDate, Price, Discount,
             CategoryId, CategoryName, Section, Column11, Column12);

Форматируйте объявление и передачу параметров в процедуры и функции

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

Вот пример плохого форматирования при объявлении параметров процедуры

 

       CREATE PROCEDURE Add_NewProduct @ProductName VARCHAR(100),@CategoryId 
INT=NULL,@Price MONEY,@ProductDescription VARCHAR(300),@ProductActive TINYINT=0,
       @DeliveryDate DATETIME,@Discount NUMERIC(4,2)=NULL,@Section INT=NULL,@Column11 INT,@Column12 FLOAT)
       AS

Вот более удачный вариант

 

CREATE PROCEDURE Add_NewProduct (
                    @ProductName VARCHAR(100),       --Наименование 
                    @CategoryId INT = NULL,          --Категория, необязательный параметр
                    @Price MONEY,                    --Цена
                    @ProductDescription VARCHAR(300),--Описание 
                    @ProductActive TINYINT = 0, --...
                    @DeliveryDate DATETIME,
                    @Discount NUMERIC(4,2) = NULL, --... @Section INT = NULL,
                    @Column11 INT,
                    @Column12 FLOAT
                    )
AS

Сравните два варианта, какой лучше читается?

 

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

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

Пример без форматирования

    EXEC Add_NewProduct @ProductName,@CategoryId,@Price,@ProductDescription,
@ProductActive, @DeliveryDate,@Discount,
@Section,@Column11,@Column12

Пример с форматированием

 

EXEC Add_NewProduct @ProductName,
                    @CategoryId,
                    @Price,
                    @ProductDescription,
                    @ProductActive, 
                    @DeliveryDate, 
                    @Discount, 
                    @Section, 
                    @Column11, 
                    @Column12

Если параметров 2-3, то может и не стоит их вытягивать по вертикали, но если больше, то для повышения удобочитаемости лучше записывайте их так, тем более есть еще причины, по которым нужно так делать, но об этом мы поговорим позднее в разделе «Правила написания кода».

Команды UNION и UNION ALL выделяйте отдельными строками

Если в запросе используется объединение с помощью конструкций UNION и UNION ALL, то перед и после этих ключевых слов вставляйте пустые строки, т.е. выделяйте их. Такое выделение позволит Вам четко видеть каждый из запросов, входящих в такую конструкцию объединения.

Даже так можно не заметить объединение UNION ALL

 

SELECT ProductId, ProductName
FROM Goods
UNION ALL
SELECT ProductId, ProductName
FROM Goods

А так Вы уже четко видите, что это два запроса

 

SELECT ProductId, ProductName 
FROM Goods

UNION ALL

SELECT ProductId, ProductName 
FROM Goods

 

Подзапросы подчиняются тем же правилам, что и обычные запросы

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

Не нужно писать что-то подобное

 

SELECT ProductId, ProductName
FROM Goods
WHERE ProductId IN (SELECT ProductId FROM Orders WHERE Price > 100)
AND Category = 1

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

 

SELECT ProductId, ProductName
FROM Goods
WHERE ProductId IN (SELECT ProductId
                    FROM Orders
                    WHERE Price > 100)
   AND Category = 1

 

Если подзапрос используется в качестве источника данных, то его также нужно

отформатировать.

Пример без форматирования

 

SELECT ProductId, ProductName FROM (SELECT ProductId, ProductName, 
Category FROM Goods WHERE Price > 100) AS Goods
WHERE Category = 1

Пример с форматированием

 

SELECT ProductId, ProductName
FROM (SELECT ProductId, ProductName, Category
      FROM Goods
      WHERE Price > 100) AS Goods
WHERE Category = 1

 

Вас заинтересует / Intresting for you:

Эффективное программирование н...
Эффективное программирование н... 4023 просмотров Sergey Mon, 03 Jan 2022, 16:30:08
Эффективное программирование н...
Эффективное программирование н... 4345 просмотров Sergey Wed, 22 Dec 2021, 19:25:05
Процедуры, функции и параметры...
Процедуры, функции и параметры... 3851 просмотров Илья Дергунов Mon, 17 Sep 2018, 14:47:45
Работа с файлами в PL/SQL: чте...
Работа с файлами в PL/SQL: чте... 17160 просмотров Akmal Mon, 03 Dec 2018, 09:00:44
Войдите чтобы комментировать