Придерживаться правил именования при разработке, всегда является хорошим тоном. В нашей команде мы придерживаемся SQL-рекомендации от Simon Holywell с некоторыми поправками
Именование таблиц должно подчиняться следующим правилам:
Имен таблицы не имеющей родительской таблицы должно отражать содержимое таблицы.
Пример:
Имя дочерней таблицы формируется так: имя_родительской_таблицы_имя_дочерней_таблицы. Дочерняя - та, из которой, при удалении записи в родительской таблице, удаляются все записи имеющие ссылку на удаляемую запись в родительской таблице.
Пример:
Имя дочерней таблицы имеющей строчную часть должна содержать потфикс row: имя_родительской_таблицы_имя_дочерней_таблицы_row.
Пример:
Любая таблица должна иметь первичный ключ — автоинкрементное поле с именем «id».
Именование полей таблицы должно подчиняться следующим правилам:
Пример:
Перечень имен полей для обязательного использования, при совпадении преследуемой логики:
Именование индексов таблицы должно подчиняться следующим правилам:
«Имя индекса» «Префикс индекса» + «_» + наименование таблицы + «$» + перечисление имен полей таблицы, участвующих в построении индекса, разделенных символом «$».
«Имя индекса» - можно не указывать (необязательное требование)
«Префикс индекса» может принимать одно из следующих значений:
Пример:
iu_credit_penalty$owner_id$doc_num (уникальный индекс в таблице «credit_penalty» по полям «owner_id» + «doc_num»)
Именование ограничения целостности таблицы должно подчиняться следующим правилам:
«Префикс ограничения целостности» + «_» + наименование таблицы + «$» + перечисление имен полей таблицы, участвующих в построении индекса, разделенных символом «$».
«Префикс ограничения целостности» может принимать одно из следующих значений:
Например внешний ключ:
fk_User$id__Post$userId
Сформирован по формуле:
fk_ + ИмяТекущейТаблицы[$ИмяПоляТекущейТаблицы[$...]] + __ + ИмяВнешнейТаблицы[$ИмяПоляВнешнейТаблицы[$...]]
Приведенный выше пример может быть аналогичен следующему:
fk_User__Post
но только, если является единственным по отношению к Внешней таблице.
Именование триггера таблицы должно подчиняться следующим правилам: «t» + «Префикс типа триггера» + «_» + «наименование таблицы» + при необходимости «бизнес-описание».
«Префикс типа триггера» может иметь длину от двух до четырех символов.
Первый символ префикса - опциональный, описывает время срабатывания триггера и принимает одно из следующих значений:
Второй и (для комбинированного триггера) последующие символы — опциональные, это комбинация в алфавитном порядке:
Рекомендации:
Пример:
tau_section_update_documents : section - таблица в которой срабатывает триггер, update_documents - то, что делает триггер (обновляет документы при изменении раздела на сайте), au - триггер запустится после того, как будет обновлена строка в таблице sectionИмя переменной языка “Transact SQL” == «Имя переменной», где «Типизированный префикс» имеет следующие значения:
Имя переменной языка “Transact SQL” == «Имя переменной», где «Типизированный префикс» имеет следующие значения:
• @vc == varchar
• @ch == char
• @i == int, tinyint
• @dt == datetime, smalldatetime
• @f == float
• @m == money
• @cur == cursor
• @tbl == tableНиже описаны примеры того, как делать не нужно, с объяснением, почему этого следует придерживаться.
Мы используем три имени баз данных: OrdersFromCACHE, OrdersFromCACHE_test, OrdersFromCACHE_dev, поэтому, если в миграции будет указано имя, то такая миграция не сработает на проде. Пример неправильного запроса:
SELECT * INTO table_2 FROM OrdersFromCACHE_dev.dbo.table_1В данном параграфе объясняются неочевидные моменты производительности.
COLLATE приводит к ухудшению производительности запросов. От COLLATE необходимо избавляться:
COLLATE DATABASE_DEFAULTЕсли Вашему запросу необходимо написать COLLATE чтобы он работал - имейте в виду, вы делаете что-то неправильно.
В запросах и хранимых процедурах не используйте табличные переменные:
DECLARE @permanent TABLE(serv_id VARCHAR(255), doc_id INT)потому что это приводит к ухудшению производительности запросов. Если необходимо используйте временные таблицы, например так:
SELECT field1, field2
INTO #tmp_table
FROM tableНиже представлен пример плохого SQL-запроса
-- абстрактное имя результирующего набора, правильней было бы: base_price
WITH t1 as (
-- Подобный запрос может быть переиспользован, поэтому мы в команде поступаем так:
-- 1. создаем PHP-функцию функцию, которая возвращает SQL
-- 2. создаем PHP-функцию функцию, которая возвращает значение используя функцию 1
SELECT
top 1 mpb.price_id
FROM
[dbo].[med_PriceBind] mpb
WHERE mpb.date_start < @fr_price_date
AND mpb.office_id = @branch_id
ORDER BY mpb.date_start DESC
)
SELECT
-- каждое поле должно быть объявлено в отдельной строке + должен быть отступ слева (4 пробела)
e.DateReg, p.MobileNumber,
count(1)-- не используйте указание поля по номеру (это плохо читается и путает разработчиков незнакомых с данной фичей)
,full_name_weight+partial_name_weight-- отсутствие пробела вокруг плюса, делает код трудночитаемым
-- нельзя делать подзапросы в SELECT-e, это сильно влияет на производительность (тут нужно было сделать JOIN):
(SELECT id FROM other_table WHERE other_table.order_num = oe.order_num) AS ot_id
-- сокращение должно быть по заглавным буквам имени таблицы (правильно: ote)
FROM OrdersToExport oe
-- использование круглых скобок без необходимости
INNER JOIN Patients p ON (p.AID = oe.PatID)
INNER JOIN t1 ON t1.price_id = oe.base_price_id
-- поле order_num является строковым, использование числовых сравнений к строковым, не применяет индекс
-- второе условие выборки должно быть перенесено на след. строку (все что начинается с AND, включая AND)
WHERE oe.order_num = 12345678 AND oe.Status = 1
AND (-- очень странно видеть select в WHERE, тут явно надо было использовать LEFT или INNER JOIN
select sum(mad.total_cost)
from med_appointmentdetail mad
where mad.order_id = oe.order_num
) = :orderVal-- имя параметра очень странное, совсем не совпадает с именем параметра в таблице