Які найпоширеніші антидіаграми SQL? [зачинено]


232

Усі ми, хто працює з реляційними базами даних, дізналися (або вивчаємо), що SQL відрізняється. Отримати бажані результати та зробити це ефективно, пов'язаний з копітким процесом, який частково характеризується вивченням незнайомих парадигм та виявленням, що деякі з наших найвідоміших моделей програмування тут не працюють. Які поширені антистатистики ви бачили (або ви самі вчиняли)?


Це питання, яке не відповідає новим стандартам щодо того, який тип питань підходить для переповнення стека. Коли його запитали, це, можливо, не було правдою.
Девід Манхейм

@casperOne Чи не існує якогось "історичного значення", який би передавав це питання прийнятності?
Емі Б

26
Мені сумно, що одне з найкорисніших запитань на сайті вухозакрито закрите як не конструктивне.
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 та перейменувати cols, якщо ви цього хочете.
Джо Пінеда

54
Моє улюблене - коли люди вставляють HTML І javascript, наприклад SELECT '<a href=... onclick="">' + name '</a>'
Метт Рогіш

15
З такими запитами ви можете редагувати сітку на веб-сайті за допомогою простого заяви alter. Або змінити вміст експорту або переформатувати дату у звіті. Це робить клієнтів щасливими та економить час. Тож дякую, але ні, дякую, я буду дотримуватися таких запитів.
Андомар

4
@Matt Rogish - Ісус, хтось насправді це робить?
Аксарідакс

118

Ось мій топ-3.

Номер 1. Невказання списку полів. (Редагувати: щоб запобігти плутанині: це правило виробничого коду. Це не застосовується до одноразових сценаріїв аналізу - якщо я не автор.)

SELECT *
Insert Into blah SELECT *

має бути

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Число 2. Використовуючи курсор і цикл 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?
jalf

29
Використовуючи select *, ви отримуєте все, що є в таблиці. Ці стовпці можуть змінювати назви та порядок. Код клієнта часто покладається на імена та замовлення. Кожні 6 місяців мене запитують, як зберегти порядок стовпців під час зміни таблиці. Якщо це правило було б дотримано, це не мало значення.
Емі Б

Я іноді використовував №2, інші я пройшов маршрут курсору (хоча потім спочатку зберігаю результати запиту на таблиці var, відкриваю курсор на цьому). Мені завжди було цікаво, чи хтось зробив тест на ефективність обох.
Джо Пінеда

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

71
  • Людські поле для читання паролів , наприклад, egad. Пояснення самостійно.

  • Використовуючи LIKE проти індексованих стовпців, і я майже спокусився сказати LIKE взагалі.

  • Переробка значень ПК, створених SQL.

  • Сюрприз ще ніхто не згадав про боговий стіл . Ніщо не говорить "органічно", як 100 стовпців бітових прапорів, великих рядків і цілих чисел.

  • Тоді є шаблон "Я сумую за файлами .

  • А для MS SQL-сервера взагалі використання курсорів . Є кращий спосіб виконати будь-яке завдання курсору.

Відредаговано, тому що їх так багато!


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
короткі псевдоніми ОКЕЙ. Якщо ви хочете осмислене ім’я, то взагалі не використовуйте псевдонім.
Joel Coehoorn

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. Cleartext паролі

З усім цим можна корисно впоратися з використанням шару абстрактних баз даних якогось (будь-якого) виду.
дкрец

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

Дивовижний приклад. Якщо розробник шукає, як замінити це хорошим рішенням, вони на півдорозі стають гідними розробниками SQL.
Стів Маклеод

46

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

Зазвичай ця таблиця пошуку виглядає приблизно так:

Ідентифікатор INT,
Ім'я NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Дата1 ДАТЕТИ,
Дата2 ДАТЕТИМ

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


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

Зачекайте - значить, 8-річний син є сином собачого грумера?
barrypicker

28

Мені найбільше не подобаються такі

  1. Використання пробілів під час створення таблиць, проростів тощо. Я добре вживаю з 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
Префікси можуть бути корисними, якщо ви записуєте об'єкти до файлів (наприклад, для керування джерелами, розгортання або міграції)
Rick

1
Чому на землі було б корисно префіксувати кожну збережену процедуру за допомогою sp або usp? Це просто ускладнює сканування списку на той, який ви хочете.
Райан Лунді

25

Надмірне використання тимчасових таблиць і курсорів.


2
Хороші докази того, що "все, що я знаю, - це процедурні мови"
дкрец

2
Зловживання чим-небудь, за визначенням, небажане. Конкретний приклад, коли використання тимчасових таблиць / курсорів не було б корисним, буде корисним.
Jace Rhea

6
Переважно я бачу, що темп-таблиці не використовуються. за допомогою SQL Server часто ви отримуєте підвищення продуктивності, роблячи матеріали з купою темп-таблиць замість одного монолітного запиту.
Серво

24

Для зберігання значень часу слід використовувати лише часовий пояс UTC. Місцевий час не слід використовувати.


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

1
@CsongorHalmai У багатьох місцях практикується економія денного світла, тому значення часу протягом години зсуву часу можуть бути неоднозначними.
Френк Швітерман

Це, безумовно, правильно для сьогодення та минулого, але для майбутнього, особливо досить далекого майбутнього, чіткі часові пояси часто є необхідністю. Якщо у вас є 30-річний варіант, який був щойно написаний і закінчується в 2049-09-27T17: 00: 00 за нью-йоркським часом, то ви не можете просто сліпо припустити, що це буде 21:00. Конгрес США цілком може змінити правила DST. Ви повинні тримати місцевий час та справжній часовий пояс (Америка / Нью-Йорк) окремо.
Джон

23

використовуючи @@ IDENTITY замість SCOPE_IDENTITY ()

Цитується з цієї відповіді :

  • @@ ІДЕНТИЧНІСТЬ повертає останнє значення ідентичності, створене для будь-якої таблиці в поточному сеансі, за всіма областями. Ви повинні бути обережними тут, оскільки це в різних сферах. Ви можете отримати значення за допомогою тригера замість поточного оператора.
  • SCOPE_IDENTITY повертає останнє значення ідентичності, створене для будь-якої таблиці в поточному сеансі та поточній області. Взагалі те, що ви хочете використовувати.
  • IDENT_CURRENT повертає останнє значення ідентичності, створене для конкретної таблиці в будь-якому сеансі та будь-якій області. Це дозволяє вам вказати, з якої таблиці ви хочете значення, якщо два вище не дуже потрібні (дуже рідко). Ви можете використовувати це, якщо хочете отримати поточне значення IDENTITY для таблиці, в яку ви не вставили запис.

+1 дуже правдиво, може призвести до помилок, які важко буде
відрізати

23

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


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

і припускаючи, що результат буде відсортований за деякою колонкою. Я трохи бачив це з Sybase, де припущення є (поки що).


1
піднести пропозицію EVER припускати сортування, просто тому, що це було так, як це виявилося в інструменті запитів, що одного разу
Joel Coehoorn

3
Я навіть не раз бачив, як це повідомлялося про помилку.
dkretz

6
в MySQL це документально підтверджено для сортування. < dev.mysql.com/doc/refman/5.0/uk/select.html >. Тож звинувачуйте MySQL (знову ж таки).
дероберт

1
В Oracle несортовані результати (майже) завжди відповідали групуванню - до версії 10G. Багато переробку для розробників, які раніше залишали ЗАМОВЛЕННЯ!
Тоні Ендрюс

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 DBA завжди скаржаться, що я використовую "ANSI приєднується", тобто те, що ви представляєте як правильний спосіб. Але я продовжую це робити, і підозрюю, що в глибині душі вони знають це краще.
Стів Маклеод

1
Я підозрюю, що Oracle бажає, щоб стандартний SQL пішов. :-) Крім того, ви не можете змішувати неявні та явні JOINS (так само ANSI JOINs) у MySQL 5 - це не працює. Це ще один аргумент для явних JION.
staticsan

3
Я б сказав, що навіть ВНУТРІШНЯ ПРИЄДНАЙТЕСЬ В УКЛЮЧЕННІ - це анти-модель. Я вважаю за краще ВНУТРІШНЕ ПРИЄДНАННЯ В ВИКОРИСТАННЯ Б.
Джон Нільссон

Oracle підтримує синтаксис ANSI зараз, але раніше вони мали цей справді дивний синтаксис для зовнішніх приєднань, і все ще занадто багато людей використовують його.
Серво

ну ... Oracle як і раніше не дозволить вам використовувати приєднання ANSI для швидких
оновлених, ввімкнених

14

Вивчаючи SQL протягом перших шести місяців своєї кар’єри і ніколи нічого не вивчали протягом наступних 10 років. Зокрема, не вивчаю чи ефективно використовуючи вікна / аналітичні функції SQL. Зокрема, використання over () та розділів.

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

Див. O'Reilly SQL Cookbook Додаток A для отримання хорошого огляду функцій вікон.


12

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

Це стосується:

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

І будь-які тести на нетипові або недостатньо дані не враховуються. Якщо це збережена процедура, покладіть тестову заяву в коментар і збережіть її з результатами. В іншому випадку введіть це в коментар до коду з результатами.


Дуже корисна техніка для мінімального тесту на T-SQL: у файлі .SQL, де ви визначаєте свій SP, UDF тощо, відразу після нього створіть блок-тест на зразок IF 1 = 2 BEGIN (зразки випадків для вашого коду з очікуваними результатами як коментарі) END
Joe Pineda

SQL Server аналізує код у тестовому блоці, навіть якщо він ніколи не виконується. Отже, коли ваш об'єкт буде модифікований і отримає більше параметрів, або іншого типу, і т.д., або об'єкти, від яких залежить, зміниться, ви отримаєте помилку, просто запитавши план виконання!
Джо Пінеда

Не завжди можливо перевірити реальні дані. Часто сервер dev / "test" сервер недостатньо оплачується і отримує частину живого сервера. Як правило, тести нахмурені проти живого сервера. Деякі місця краще і мають тестовий або інсценіруючий сервер із живими даними.
Серво

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, я згоден. Хоча я знайшов принаймні один-два випадки, коли ця методика покращила продуктивність - запити, що стосуються, були найменш складними.
1010

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

1
Іноді доводиться це робити, якщо умови дуже складні. Правда це можна зловживати до крайнощів. Але багато разів просте видалення набагато простіше, ніж логіка отримання справи в початковому запиті. Також іноді, якщо стаття не є зручною, початковий запит сповільниться. Але просто зробити це на меншій темп-таблиці ефективніше. В інший час ви продовжуєте додавати випадки, які ділові люди продовжують додавати після факту.
Серво

9

Контраранський погляд: надмірна одержимість нормалізацією.

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

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

(більше думок про це ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )


22
Я думаю, що ненормалізація - це часто передчасна оптимізація.
tuinstoel

Іноді воно є, інколи - ні. На щастя, тестувати це часто легко, і різні варіанти працюють з різними потребами db.
Грегг Лінд

17
Нормалізація не лише для економії місця на диску. Це також створити авторитетне джерело даних. Якщо дані зберігаються лише в одному місці, то послідовність не є побічним продуктом ретельного кодування, а натомість є побічним продуктом дизайну.
Грант Джонсон

Зберігання складних даних у форматі JSON - одне: підтримка для них все більше, і це свідомий компроміс. Використання розділених комами значень (або будь-яких інших значень) у спробі зберегти одне приєднання - це копійчане і нерозумне фунт.
Джон

Рішення noSQL демонструють перевагу продуктивності за рахунок повторюваних даних, усуваючи пошук у кількох таблицях. Покладає на голову всю нормалізаційну річ. У деяких прикладах дані збираються в декількох місцях, щоб забезпечити, щоб один процес мав найшвидший час реакції. Звичайно, питання про авторитетні джерела вступають у гру.
barrypicker

9

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

Дуже серйозно вважати, що тригери - це бази даних, як обробники подій для OOP. Існує таке уявлення, що будь-яку стару логіку можна ввести в тригери, щоб її зняти, коли транзакція (подія) відбувається на столі.

Неправда. Одна з великих відмінностей полягає в тому, що тригери синхронні - з помстою, оскільки вони синхронні для заданої операції, а не для рядкової операції. З боку ООП - навпаки - події є ефективним способом реалізації асинхронних транзакцій.


8

Збережені процедури або функції без коментарів ...


І перегляди;) Функції вірні, за винятком функцій з табличним значенням (= представлення з параметрами).
Стефан Штайгер

7

1) Я не знаю, що це "офіційний" анти-шаблон, але мені не подобається і намагаюся уникати рядкових літералів як магічних значень у стовпці бази даних.

Приклад таблиці "image" таблиці "MediaWiki":

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: Це погано, тому що, коли ви згодом будете змінювати список перерахунків, вам потрібно пам'ятати, щоб змінити його в двох місцях зараз. Це порушує DRY . База даних повинна бути єдиним джерелом істини.
Геррат

7

Ідентичні підзапити в запиті.


10
На жаль, іноді цього просто не уникнути - у SQL 2000 не було ключового слова "З", а використання UDF для інкапсуляції загальних підзапитів колись призводить до покарання за ефективність, звинувачуйте MS у цьому ...
Джо Пінеда

Що ж, сподіваємось, вони наткнуться на додавання цього дня.
EvilTeach

У SQL 2000 можна використовувати змінні таблиці.
рекурсивна

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

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

7
  • Змінений вигляд - вид, який змінюється занадто часто і без попередження чи причини. Зміна буде або помічена в самий невідповідний момент, або, ще гірше, неправильна та ніколи не помічена. Можливо, ваша програма порушиться, оскільки хтось подумав про кращу назву для цього стовпця. Як правило, погляди повинні розширити корисність базових таблиць, зберігаючи договір із споживачами. Виправляйте проблеми, але не додайте функцій або гірші зміни поведінки, для цього створюється новий погляд. Для пом'якшення не діліться думками з іншими проектами та використовуйте CTE, коли платформи дозволяють. Якщо у вашому магазині є DBA, ви, ймовірно, не можете змінити погляди, але всі ваші погляди будуть застарілими або непотрібними в такому випадку.

  • Параметр! Параметр - Чи може запит мати кілька цілей? Можливо, але наступна людина, яка прочитає це, не дізнається до глибокої медитації. Навіть якщо вони вам зараз не потрібні, швидше за все, ви хочете, навіть якщо це "просто" налагодження. Додавання параметрів скорочує час технічного обслуговування та зберігає сухість. Якщо у вас є пункт де, ви повинні мати параметри.

  • Справа про відсутність СПРАВИ -

    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)
Мітч Пшеничний

Пару років тому я здивував, що SQL S. якось оптимізовано для обробки кореляційних запитів: для простих ви отримуєте той же план виконання, що і для логічно еквівалентного запиту, використовуючи ПРИЄДНАЙТЕ! Крім того, кореляційні запити, що ставлять Oracle на коліна, працюють лише повільно на SQL S.
Джо Пінеда

Тому я завжди тестую це обома способами. І я <i> роблю </i> зазвичай пробую це обома способами. На практиці для SQL Server у будь-якому випадку, як правило, я вважаю, що співвіднесений квадрат не повільніше.
dkretz

3
БУДЬ ласка, розумійте, що співвіднесений запит та об'єднання є ІДЕНТИЧНИМ (у більшості випадків). Це навіть не різні речі, оптимізовані один до одного, а просто різні текстові зображення однієї операції.
erikkallen

5

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


5

Розробники, які пишуть запити, не маючи гарного уявлення про те, що робить SQL-додатки (як окремі запити, так і багатокористувацькі системи) швидкими або повільними. Сюди входить і незнання про:

  • фізичні стратегії мінімізації вводу / виводу, враховуючи, що вузьким місцем у більшості запитів є введення / виведення, а не процесор
  • перф-вплив різних видів фізичного доступу до пам’яті (наприклад, багато послідовних вводу-виводу будуть швидшими, ніж безліч малих випадкових вводу-виводу, хоча менше, якщо ваш фізичний накопичувач - це SSD!)
  • як вручну налаштувати запит, якщо СУБД створює поганий план запитів
  • як діагностувати низьку продуктивність бази даних, як "налагодити" повільний запит і як прочитати план запитів (або ПОЯСНІТЬ, залежно від обраної вами СУБД)
  • стратегії блокування для оптимізації пропускної здатності та уникнення тупикових ситуацій у багатокористувацьких додатках
  • важливість пакетної та інших хитрощів для обробки наборів даних
  • дизайн таблиць та індексів для найкращого врівноваження простору та продуктивності (наприклад, охоплення індексів, збереження індексів невеликими, де це можливо, зменшення типів даних до мінімально необхідного розміру тощо)

3

Використання SQL як прославленого пакету ISAM (індексований метод послідовного доступу). Зокрема, вкладення курсорів замість комбінування заяв 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. Практично всі курсори можуть бути переписані як задані на основі операцій. Мало хто з них не може виглядати таким чином, як тільки DBA, які мають багаторічний досвід роботи і які розуміють, як слід писати внутрішні файли бази даних. Жоден розробник додатків ніколи не повинен писати курсор SQL.
HLGEM

3

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.