Якщо це можливо, не робіть цього.
Це відповідь - це анти-шаблон. Якщо клієнт знає таблицю, з якої йому потрібні дані, тоді SELECT FROM ThatTable
. Якщо база даних розроблена таким чином, що це потрібно, вона, здається, створена неоптимально. Якщо рівень доступу до даних повинен знати, чи існує значення в таблиці, легко скласти SQL в цьому коді, і введення цього коду в базу даних не є хорошим.
Для мене це здається встановленням пристрою всередині ліфта, де можна ввести номер потрібного поверху. Після натискання кнопки Go, вона рухається механічною рукою до потрібної кнопки для потрібного поверху та натискає її. Це вводить багато потенційних проблем.
Зверніть увагу: тут немає наміру знущатися. Моїм безглуздим прикладом ліфта був * найкращий пристрій, який я міг собі уявити *, щоб лаконічно вказати на проблеми з цією технікою. Він додає марний рівень опосередкованості, переміщуючи вибір імені таблиці з простору абонента (використовуючи надійний і добре зрозумілий DSL, SQL) у гібрид, використовуючи незрозумілий / химерний код SQL на стороні сервера.
Таке розподіл відповідальності через переміщення логіки побудови запитів у динамічний SQL робить код важчим для розуміння. Це порушує стандартну та надійну домовленість (як запит SQL вибирає, що вибрати) в імені користувацького коду, багатого можливістю помилок.
Ось докладні пункти щодо деяких потенційних проблем такого підходу:
Динамічний SQL пропонує можливість ін'єкції SQL, яку важко розпізнати в коді інтерфейсу або коді інтерфейсу (потрібно перевірити їх разом, щоб побачити це).
Збережені процедури та функції можуть отримувати доступ до ресурсів, на які має право власник SP / функції, але виклик не має. Наскільки я розумію, без особливого догляду, за замовчуванням, коли ви використовуєте код, який виробляє динамічний SQL і запускає його, база даних виконує динамічний SQL під правами абонента. Це означає, що ви або взагалі не зможете використовувати привілейовані об’єкти, або вам доведеться відкривати їх для всіх клієнтів, збільшуючи площу потенційної атаки на привілейовані дані. Встановлення SP / функції під час створення завжди працювати як конкретний користувач (у SQL Server, EXECUTE AS
) може вирішити цю проблему, але ускладнює ситуацію. Це посилює ризик ін'єкції SQL, згаданий у попередньому пункті, роблячи динамічний SQL дуже привабливим вектором атаки.
Коли розробник повинен зрозуміти, що робить код програми, щоб змінити його або виправити помилку, йому буде дуже важко отримати точний SQL-запит, що виконується. Можна використовувати SQL-профайлер, але це вимагає особливих привілеїв і може мати негативний вплив на продуктивність виробничих систем. Виконаний запит може реєструватися SP, але це збільшує складність із сумнівними перевагами (вимагає розміщення нових таблиць, очищення старих даних тощо) і є абсолютно неочевидним. Насправді, деякі додатки сконструйовані таким чином, що розробник не має облікових даних бази даних, тому для нього стає практично неможливим побачити поданий запит.
Коли виникає помилка, наприклад, коли ви намагаєтесь вибрати таблицю, яка не існує, ви отримаєте повідомлення в рядку "неправильне ім'я об'єкта" з бази даних. Це трапиться точно так само, незалежно від того, чи складаєте ви SQL на задній стороні або в базі даних, але різниця полягає в тому, що якийсь поганий розробник, який намагається усунути несправності системи, повинен заглибитися на один рівень глибше в іншу печеру нижче тієї, де Проблема існує, копатись у дивовижній процедурі, яка робить це все, щоб спробувати з’ясувати, в чому проблема. Журнали не відображатимуть "Помилка в GetWidget", а "Помилка в OneProcedureToRuleThemAllRunner". Ця абстракція, як правило , зробити систему ще гірше .
Приклад у псевдо-C # переключення імен таблиць на основі параметра:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
Хоча це не усуває всі можливі проблеми, які тільки можна собі уявити, недоліки, які я окреслив за допомогою іншої техніки, відсутні в цьому прикладі.