Каковы наиболее распространенные анти-паттерны SQL? [закрыто]


232

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


Это вопрос, который не соответствует новым стандартам о том, какой тип вопроса подходит для переполнения стека. Когда его спросили, это не могло быть правдой.
Дэвид Мангейм

@casperOne, не существует ли какой-либо оговорки об «историческом значении», которая бы привела этот вопрос в приемлемость?
Эми Б

26
Мне грустно, что один из самых полезных вопросов на сайте wohole закрыт как неконструктивный.
HLGEM

11
@HLGEM Я полностью согласен. Этот вопрос является прекрасным примером всего, что не так с StackExchange
Кевин Морс

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

Ответы:


156

Меня постоянно разочаровывает тенденция большинства программистов смешивать свою UI-логику на уровне доступа к данным:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

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

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


10
Хороший шаблон плаката-ребенка для максимальной связи между максимально возможным количеством уровней / уровней абстракции.
dkretz

3
Это может быть не очень хорошо для расцепления, хотя по причинам производительности я часто делал подобные вещи, итеративные изменения, выполняемые SQL Server, выполняются быстрее, чем в коде среднего уровня. Я не получаю балл за повторное использование - ничто не мешает вам запустить SP и переименовать столбцы, если вы этого хотите.
Джо Пинеда

54
Мой любимый - когда люди встраивают HTML и javascript, например, SELECT '<a href=... onclick="">' + name '</a>'
Matt Rogish

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

4
@ Matt Rogish - Иисус, кто-то на самом деле это делает?
Axarydax

118

Вот мой топ 3.

Номер 1. Невозможность указать список полей. (Изменить: для предотвращения путаницы: это правило производственного кода. Оно не применяется к одноразовым сценариям анализа - если я не автор.)

SELECT *
Insert Into blah SELECT *

должно быть

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Номер 2. Использование курсора и цикла while, когда будет использоваться цикл while с переменной цикла.

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

Номер 3. DateLogic через строковые типы.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

Должно быть

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

Я видел недавний всплеск «Один запрос лучше, чем два, верно?»

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

Этот запрос требует двух или трех разных планов выполнения в зависимости от значений параметров. Только один план выполнения генерируется и помещается в кеш для этого текста SQL. Этот план будет использоваться независимо от значения параметров. Это приводит к периодической низкой производительности. Гораздо лучше написать два запроса (по одному на каждый план выполнения).


7
хммм, я дам вам +1 за пункты 2 и 3, но разработчики переигрывают правило 1. Иногда оно имеет место.
annakata

1
Какова причина № 1?
Джалф

29
Когда вы используете select *, вы получаете все, что есть в таблице. Эти столбцы могут изменить имена и порядок. Код клиента часто зависит от имен и порядка. Каждые 6 месяцев меня спрашивают, как сохранить порядок столбцов при изменении таблицы. Если бы правило соблюдалось, это не имело бы значения.
Эми Б

Я использовал # 2 иногда, другие я прошел курсором курсора (хотя тогда я сначала сохраняю результаты запроса в таблице var, открываю курсор на этом). Я всегда задавался вопросом, прошел ли кто-нибудь тест производительности обоих.
Джо Пинеда

4
... но, конечно, курсоры почти всегда должны быть последним средством, после того, как не удалось понять, как выполнять работу с SQL на основе множеств. Однажды я потратил около 45 минут, тщательно разбирая ужасный, гигантский курсор PL / SQL в хранимой процедуре (рисовал диаграммы гнилой вещи), которая заполняла большую временную таблицу, затем отбирала содержимое временной таблицы обратно к вызывающей стороне, чтобы отобразить отчет. Выполнение программы потребовало 8,5 минут на существенном оборудовании. После составления схемы всего этого я смог заменить его одним запросом, который дал те же результаты менее чем за 2 секунды. Курсоры, мужик ...
Крейг

71
  • Удобочитаемые поля пароля , например. Самоочевидно.

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

  • Переработка сгенерированных SQL значений PK.

  • Сюрприз, никто еще не упомянул бог-таблицу . Ничто не говорит «органично», как 100 столбцов битовых флагов, больших строк и целых чисел.

  • Затем есть шаблон «Я скучаю по INI-файлам» : хранение CSV- файлов , строк с разделителями каналов или других необходимых для анализа данных в больших текстовых полях.

  • А для MS SQL server использование курсоров вообще . Есть лучший способ сделать любую заданную задачу курсора.

Отредактировано потому что так много!


19
неправильно про курсоры, я бы не решился сказать, что делать что-то определенное на 100% правильно или на 100% неправильно
Шон

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

3
@tuinstoel: Как LIKE '% blah%' может использовать индекс? Индексирование основано на упорядочении, и этот пример ищет случайную среднюю позицию строки. (Порядок индексов по 1-му символу 1-го, и, таким образом, просмотр средних 4 символов дает практически случайный порядок ...)
MatBailie

12
На большинстве серверов баз данных (по крайней мере, на тех, которые я использовал) LIKE может использовать индексы ... при условии, что это поиск по префиксу (LIKE 'xxx%'), то есть до тех пор, пока подстановочные символы не прийти первым в строке поиска. Я думаю, что вы, возможно, немного говорите о разных целях.
Коуэн

10
Как будто ты не любишь LIKE '%LIKE'.
Йохан

62

Не нужно копать для этого: не использовать подготовленные заявления.


3
Ага. Пристально следил в том же контексте, по моему опыту, с «не заманиванием ошибок».
dkretz

1
@stesch: это ничто по сравнению с использованием представлений и переменной отчетной даты. Представления являются антипаттерном, если у вас есть переменная отчетная дата (я предполагаю, что большинство приложений имеют). Добавил бы это в отдельный ответ, но он закрыт, к сожалению.
Стефан Штайгер

56

Используя бессмысленные псевдонимы таблиц:

from employee t1,
department t2,
job t3,
...

Делает чтение большого оператора SQL намного сложнее, чем нужно


49
псевдонимы? черт, я видел настоящие имена столбцов, как это
annakata

10
краткие псевдонимы в порядке. Если вы хотите осмысленное имя, не используйте псевдоним вообще.
Джоэл Коухорн

43
Он не сказал «кратко», он сказал «бессмысленно». В моей книге не было бы ничего плохого в использовании e, d и j в качестве псевдонимов в примере запроса.
Роберт Росни

11
Абсолютно, Роберт - e, d и j будет в порядке со мной.
Тони Эндрюс

8
Я бы использовал emp для работника, dep для отдела и работу для работы (или, может быть, jb) :)
Андрей Ринеа

53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Слепо доверяющий пользовательский ввод
  2. Не использует параметризованные запросы
  3. Открытые пароли

Все это может быть эффективно использовано с использованием некоторого (любого) вида абстрактного слоя базы данных.
dkretz

@doofledorfer: Согласитесь, средний уровень был бы определенно лучше в таком случае, плюс добавление кэширования результатов в качестве приятного побочного эффекта.
Джо Пинеда

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

46

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

Как правило, эта таблица соответствия выглядит следующим образом:

ID INT,
Имя NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Date1 DATETIME,
Дата2 Дата

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


1
Хуже того, я читал, что в новейшей версии Access, которая на самом деле поддерживается автоматически, что, боюсь, будет способствовать большему фетишизму столбцов Value1, Value2, Value3 ...
Джо Пинеда,

Подожди - итак, 8-летний сын - сын собачника?
barrypicker

28

Больше всего мне не нравятся

  1. Использование пробелов при создании таблиц, sprocs и т. Д. Я в порядке с CamelCase или under_scores, а также в единственном или множественном числе и UPPERCASE или в нижнем регистре, но при этом необходимо ссылаться на таблицу или столбец [с пробелами], особенно если [это странно разделено] (да, Я столкнулся с этим) действительно меня раздражает.

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

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

  4. Доступ. Может ли программа быть анти-паттерном? У меня есть SQL Server в моей работе, но многие люди используют доступ из-за его доступности, «простоты использования» и «дружелюбия» для нетехнических пользователей. Слишком много здесь, чтобы вдаваться, но если вы были в подобной среде, вы знаете.


2
# 4 - есть еще один поток для <a href=' stackoverflow.com/questions/327199/…> :).
dkretz

4
Доступ не является СУБД. Это среда RAD с очень простым менеджером баз данных. SQL Server, Oracle и др. не будет никогда заменить его, если не добавить VB-как язык и Crystal Reports , как объект.
Джо Пинеда

26

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


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

7
+1 к комментарию doofledorfer !! Я видел это много, я считаю , это идиотизм и действительно сделать поиск определенного SP очень трудный !!! Также расширено до "vw_" для представлений, "tbl_" для таблиц и т.п., как я их ненавижу!
Джо Пинеда

1
Префиксы могут быть полезны, если вы пишете объекты в файлы (например: для контроля версий, развертывания или миграции)
Рик

1
С какой стати было бы полезно ставить перед каждой хранимой процедурой префикс sp или usp? Это только затрудняет сканирование списка на тот, который вы хотите.
Райан Ланди

25

Чрезмерное использование временных таблиц и курсоров.


2
Хорошее доказательство того, что «все, что я знаю, это процедурные языки».
dkretz

2
Чрезмерное использование чего-либо является по определению нежелательным. Был бы полезен конкретный пример, когда использование временных таблиц / курсоров не требуется.
Джейс Рея

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

24

Для хранения значений времени следует использовать только часовой пояс UTC. Местное время не должно использоваться.


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

1
@CsongorHalmai Во многих местах используется летнее время, поэтому значения времени в течение часа после сдвига могут быть неоднозначными.
Франк Швитерман,

Это, безусловно, правильно для настоящего и прошлого, но для будущего, особенно довольно далекого будущего, явные часовые пояса часто являются необходимостью. Если у вас есть 30-летний вариант, который был только что написан и истекает в 2049-09-27T17: 00: 00 по нью-йоркскому времени, то вы не можете просто слепо предполагать, что это будет 21: 00: 00Z. Конгресс США вполне может изменить правила DST. Вы должны хранить местное время и истинный часовой пояс (America / New_York) отдельно.
Джон Коуэн

23

используя @@ IDENTITY вместо SCOPE_IDENTITY ()

Цитируется из этого ответа :

  • @@ IDENTITY возвращает последнее значение идентификатора, сгенерированное для любой таблицы в текущем сеансе, во всех областях. Вы должны быть осторожны здесь, так как это выходит за рамки. Вы можете получить значение из триггера вместо текущего оператора.
  • SCOPE_IDENTITY возвращает последнее значение идентификатора, сгенерированное для любой таблицы в текущем сеансе и текущей области. Вообще то, что вы хотите использовать.
  • IDENT_CURRENT возвращает последнее значение идентификатора, сгенерированное для конкретной таблицы в любом сеансе и любой области видимости. Это позволяет вам указать, из какой таблицы вы хотите получить значение, в случае, если два приведенных выше не совсем то, что вам нужно (очень редко). Вы можете использовать это, если хотите получить текущее значение IDENTITY для таблицы, в которую вы не вставили запись.

+1 очень верно, может вызвать ошибки, которые было бы трудно отсеять
Axarydax

23

Повторное использование «мертвого» поля для чего-то, для чего оно не было предназначено (например, хранение пользовательских данных в поле «Факс») - хотя это очень заманчиво в качестве быстрого решения!


21
select some_column, ...
from some_table
group by some_column

и предполагая, что результат будет отсортирован по some_column. Я видел это немного с Sybase, где предположение верно (пока).


1
upvote для НИКОГДА, предполагающего порядок сортировки, просто потому, что это было так, как это было показано в инструменте запросов один раз
Joel Coehoorn

3
Я даже видел, что об этом сообщали как об ошибке не раз.
dkretz

6
в MySQL это задокументировано для сортировки. < dev.mysql.com/doc/refman/5.0/en/select.html >. Так что вините MySQL (снова).
Дероберт

1
В Oracle несортированные результаты (почти) всегда соответствовали группировке - до версии 10G. Множество доработок для разработчиков, которые раньше не использовали ORDER BY!
Тони Эндрюс

1
Я был даже в учебном классе, где это было заявлено как факт для SQL Server. Я должен был протестовать действительно громко. Для сохранения всего лишь 20 символов вы полагаетесь на неясное или недокументированное поведение.
erikkallen

20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

Или втиснуть все в одну строчку.


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

17
  • FROM TableA, TableB WHEREСинтаксис JOINS вместоFROM TableA INNER JOIN TableB ON

  • Предположение, что запрос будет возвращен, отсортировано определенным образом, без добавления предложения ORDER BY, просто потому, что так оно и было во время тестирования в инструменте запросов.


5
Мои администраторы Oracle всегда жалуются на то, что я использую «соединения ANSI», то есть то, что вы представляете как правильный путь. Но я продолжаю это делать и подозреваю, что в глубине души они знают это лучше.
Стив Маклеод

1
Я подозреваю, что Oracle желает, чтобы стандартный SQL исчез. :-) Кроме того, вы не можете смешивать неявные и явные JOINS (иначе ANSI JOIN) в MySQL 5 - это не работает. Что является еще одним аргументом в пользу явных JION.
staticsan

3
Я бы сказал, что даже A INNER JOIN B ON - это анти паттерн. Я предпочитаю A INNER JOIN B ИСПОЛЬЗОВАНИЕ.
Джон Нильссон

В настоящее время Oracle поддерживает синтаксис ANSI, но в прошлом этот действительно странный синтаксис использовался для внешних объединений, и слишком много людей все еще используют его.
Cervo

хорошо ... Oracle по-прежнему не позволит вам использовать объединения ANSI для быстро
обновляемых

14

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

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

См. Приложение A O'Reilly SQL Cookbook для хорошего обзора оконных функций.


12

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

Это относится, когда:

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

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


Очень полезная техника для минимального теста T-SQL: в файле .SQL, где вы определяете свой SP, UDF и т. Д., Сразу после него создайте блочный тест, например IF 1 = 2 BEGIN (примеры для вашего кода, с ожидаемыми результатами в комментариях) КОНЕЦ
Джо Пинеда

SQL Server анализирует код в тестовом блоке, даже если он никогда не выполняется. Поэтому, когда ваш объект изменяется и получает больше параметров, или другого типа, и т. Д., Или объект, от которого он зависит, изменяется, вы получите ошибку, просто запросив план выполнения!
Джо Пинеда

Это не всегда возможно проверить с реальными данными. Зачастую сервер dev / test тест недоплачивается и получает долю от живого сервера. Обычно тесты не одобряются для живого сервера. В некоторых местах лучше и есть тестовый или промежуточный сервер с живыми данными.
Cervo

11

Временная таблица злоупотреблений.

Именно такие вещи:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

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

И да, я видел страницы кода в этой форме в производственных БД.


1
+1, согласен. Хотя я обнаружил, по крайней мере, один или два случая, когда этот метод улучшил производительность - связанные запросы были, по меньшей мере, сложными.
a'r

1
Правда - у них есть место, просто не в каждом запросе :)
geofftnz

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

9

Противоположный взгляд: чрезмерная одержимость нормализацией.

Большинство систем SQL / RBDB предоставляют множество функций (транзакции, репликация), которые весьма полезны даже для ненормализованных данных. Дисковое пространство дешево, и иногда может быть проще (более простой код, более быстрое время разработки) манипулировать / фильтровать / искать извлеченные данные, чем написать схему 1NF и справиться со всеми возникающими в ней трудностями (сложные объединения, неприятные подвыборы). , и т.д).

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

(больше мыслей об этом ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )


22
Я думаю, что ненормализация часто является преждевременной оптимизацией.
Tuinstoel

Иногда это так, иногда нет. К счастью, это часто легко проверить, и разные опции работают с разными потребностями БД.
Грегг Линд

17
Нормализация не только для экономии дискового пространства. Это также для создания авторитетного источника данных. Если данные хранятся только в одном месте, согласованность не является побочным продуктом тщательного кодирования, а является побочным продуктом дизайна.
Грант Джонсон

Хранение составных данных в формате JSON - это одно: их все больше и больше поддерживают, и это осознанный компромисс. Использование разделенных запятыми (или какими-либо другими) значениями в попытке сохранить одно объединение является копеечным и глупым.
Джон Коуэн

Решения noSQL демонстрируют выигрыш в производительности за счет дублирования данных за счет исключения поиска в нескольких таблицах. Надевает всю нормализацию на голову. В некоторых примерах данные собираются в нескольких местах, чтобы гарантировать, что один процесс имеет самое быстрое время отклика. Конечно, вопросы об авторитетных источниках входят в игру.
barrypicker

9

Я просто собрал это, основываясь на некоторых ответах SQL здесь, на SO.

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

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


8

Хранимые процедуры или функции без каких-либо комментариев ...


И представления;) Функции true, кроме табличных функций (= представления с параметрами).
Стефан Штайгер

7

1) Я не знаю, что это «официальный» анти-шаблон, но мне не нравится и стараюсь избегать строковых литералов как магических значений в столбце базы данных.

Пример из таблицы MediaWiki 'image':

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

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

Я разрабатываю такие случаи, как поиск int в таблицах ImageMediaType и ImageMajorMime с первичными ключами int.

2) преобразование даты / строки, основанное на определенных настройках NLS

CONVERT(NVARCHAR, GETDATE())

без идентификатора формата


И никаких синтаксических отступов тоже нет. Argghh.
dkretz

2
Почему это плохо? Конечно, если вы пытаетесь выразить набор значений, это работает так же, как и таблица поиска, и лучше подходит для кода, который ее вызывает. Я бы предпочел иметь перечисление в коде моего приложения, которое сопоставляется с ограничением перечисления в моей БД, чем перечисление в коде моего приложения, которое сопоставляется с конкретными строками таблицы поиска. Это просто чище.
Джек Райан

@JackRyan: Это плохо, потому что когда вы меняете список перечислений позже, вам нужно помнить, чтобы изменить его в двух местах сейчас. Это нарушает СУХОЙ . База данных должна быть единственным источником правды.
Джеррат

7

Идентичные подзапросы в запросе.


10
К сожалению, иногда вы просто не можете избежать этого - в SQL 2000 не было ключевого слова WITH, а использование UDF для инкапсуляции общих подзапросов иногда приводит к снижению производительности, в этом виноват MS ...
Джо Пинеда,

Что ж, надеюсь, они найдут способ добавить его на днях.
EvilTeach

В SQL 2000 вы можете использовать табличные переменные.
рекурсивный

@recursive: у вас не может быть индексов для табличной переменной, что часто делает ее медленнее, чем подзапрос. Однако вы можете использовать временную таблицу с пользовательскими индексами.
Рик

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

7
  • Измененное представление - представление, которое изменяется слишком часто и без уведомления или причины. Изменение будет замечено в самое неподходящее время или, что еще хуже, будет ошибочным и никогда не будет замечено. Возможно, ваше приложение сломается, потому что кто-то придумал более подходящее название для этого столбца. Как правило, представления должны расширять полезность базовых таблиц при сохранении договора с потребителями. Исправляйте проблемы, но не добавляйте функции или не изменяйте поведение, для этого создайте новый вид. Для смягчения не делитесь представлениями с другими проектами и используйте CTE, когда платформы позволяют. Если в вашем магазине есть администратор базы данных, вы, вероятно, не сможете изменить представления, но в этом случае все ваши представления будут устаревшими и бесполезными.

  • Параметр! - может ли запрос иметь более одной цели? Возможно, но следующий человек, который читает это, не узнает до глубокой медитации. Даже если они вам сейчас не нужны, скорее всего, вы это сделаете, даже если это «просто» для отладки. Добавление параметров сокращает время обслуживания и делает вещи СУХИМЫМИ. Если у вас есть предложение where, у вас должны быть параметры.

  • Дело без дела -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  

Любил этот третий. Я уже использую это локально ...
alphadogg

Спасибо за реквизит. :)
Джейсон Салдо

5

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

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

  • Использование коррелированных подзапросов, когда объединение с производной таблицей может выполнить эту работу.


Я согласен, если вы имеете в виду то, что я думаю, вы имеете в виду; хотя коррелированный подзапрос является типом производной таблицы IIRC.
dkretz

1
Производная таблица - это операция набора, в то время как коррелированный подзапрос выполняется для каждой строки во внешнем запросе, что делает ее менее эффективной (9 раз из 10)
Mitch Wheat

Пару лет назад я с удивлением обнаружил, что SQL S. каким-то образом оптимизирован для обработки коррелированных запросов: для простых вы получаете тот же план выполнения, что и для логически эквивалентного запроса с использованием JOIN! Кроме того, коррелированные запросы, которые ставят Oracle на колени, работают медленно на SQL S.!
Джо Пинеда

Вот почему я всегда проверяю это в обоих направлениях. И я <я> делаю </> обычно пробую это обоими способами. На практике, в любом случае, для SQL Server я обнаружил, что коррелированный sq не медленнее.
dkretz

3
ПОЖАЛУЙСТА, понимайте, что коррелированный подзапрос и объединение являются ИДЕНТИЧНЫМИ (в большинстве случаев). Это даже не разные вещи, которые оптимизированы друг для друга, а просто разные текстовые представления одной и той же операции.
Эриккален

5

Помещение вещей во временные таблицы, особенно люди, которые переходят с SQL Server на Oracle, имеют привычку чрезмерно использовать временные таблицы. Просто используйте вложенные операторы select.


5

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

  • стратегии минимизации физического ввода / вывода, учитывая, что узким местом большинства запросов является ввод / вывод, а не процессор
  • Влияние различных видов физического доступа к хранилищу (например, большое количество последовательных операций ввода-вывода будет быстрее, чем множество небольших случайных операций ввода-вывода, хотя и меньше, если физическое хранилище - это SSD!)
  • как вручную настроить запрос, если СУБД выдает плохой план запроса
  • как диагностировать низкую производительность базы данных, как «отладить» медленный запрос и как прочитать план запроса (или EXPLAIN, в зависимости от выбранной вами СУБД)
  • стратегии блокировки для оптимизации пропускной способности и предотвращения тупиков в многопользовательских приложениях
  • важность пакетирования и других приемов для обработки наборов данных
  • дизайн таблиц и индексов для наилучшего баланса между пространством и производительностью (например, покрытие индексов, сохранение индексов небольшим, где это возможно, сокращение типов данных до минимально необходимого размера и т. д.)

3

Использование SQL в качестве прославленного пакета ISAM (Indexed Sequential Access Method). В частности, вложенные курсоры вместо объединения операторов SQL в один, хотя и больший, оператор. Это также считается «злоупотреблением оптимизатором», поскольку на самом деле оптимизатор мало что может сделать. Это может быть объединено с неподготовленными заявлениями для максимальной неэффективности:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

Правильное решение (почти всегда) - объединить два оператора SELECT в один:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

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

Кроме того, сортировка в приложении, как правило, нет-нет.


Мой стиль, хотя и не этот синтаксис, особенно распространен в PHP.
dkretz

Синтаксис на самом деле - IBM Informix-4GL, но он достаточно ясен, чтобы не объяснять (я думаю). И стиль широко распространен во многих программах SQL - независимо от языка программирования.
Джонатан Леффлер

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

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

3

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.