Чому я не можу використовувати змінні в T-SQL, як я думаю, що можу?


18

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

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO

Для цього вам потрібно використовувати динамічний sql. mssqltips.com/sqlservertip/1160/…
Stijn Wynants

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

2
Тому що використовувати USEкоманду з параметром неможливо .
ТТ.

2
На додаток до вже наданих відповідей, але їх недостатньо для власної відповіді, змінні розміщуються до максимуму поточної партії. (Я не знаю, чи можна застосовувати змінні більш вузько, ніж у SQL Server.) Отже, щойно у вас з'явилася GOраніше, заявлена ​​змінна зникає. Ви можете вивчити змінні SQLCMD, які можуть бути або не застосовуватися до вашого сценарію.
CVn

1
Ми схильні вважати назви баз даних та назви стовпців як рядкові значення, але в контексті SQL вони є Ідентифікаторами. Те, що ви намагаєтесь, було б таким самим, як очікувати x = 7; бути таким же, як 'x' = 7; якоюсь іншою мовою. Так само, як могла бути створена комп'ютерна мова, яка має справу з 'x' = 7 тим же, що і x = 7, може бути створена RDBMS, яка трактує Create Table X так само, як Create Table 'X'. Але це не було б SQL.
користувач1008646

Відповіді:


20

На веб-сторінці Книги для змінних

Змінні можна використовувати лише у виразах, а не замість імен об’єктів чи ключових слів. Для побудови динамічних операторів SQL використовуйте EXECUTE.

Це буде працювати так, як ви очікували, якби, наприклад, ви використовували свою змінну в пункті де. Щодо того, чому, я думаю, це має щось спільне з тим, що аналізатор не в змозі оцінити змінну і, таким чином, перевірити наявність. При виконанні запит розбирається спочатку на синтаксис та об'єкти, а потім, якщо аналіз буде успішним, запит виконує, у який момент буде встановлена ​​змінна.

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;

3
Зауважте, що динамічного SQL слід уникати, коли це можливо. Це аналог SQL до використання evalфункцій у процедурних мовах, таких як JavaScript та Python. Це швидкий спосіб створити отвори в безпеці.
jpmc26

1
@ jpmc26: Який безпечніший спосіб зробити це, що не включає динамічний SQL?
Роберт Харві

1
@RobertHarvey Просто тому, що цього найкраще уникати, не означає, що завжди є альтернатива з точно таким же функціоналом. ;) Часто, частина відповіді - «Використовуйте зовсім інше рішення проблеми». Іноді це найкраще робити, але не без гарного роздумування і переконання, що ви не нехтували альтернативами, і навіть тоді це повинно бути здоровою дозою обережності.
jpmc26

2
@ jpmc26: Приклад ОП виглядає як те, що може зробити ORM «Код-Перший» для встановлення таблиць у базі даних. Хоча динамічний SQL в принципі небезпечний, кінцевий користувач ніколи не торкнеться цього конкретного коду.
Роберт Харві

@RobertHarvey Залежить від того, кого ви вважаєте "кінцевим користувачем". Для сценарію, що розгортає БД, я вважаю розробника і, можливо, деяких системних адміністраторів "кінцевим користувачем". Я б все-таки задумав систему відкидати незахищений вхід у такому випадку, якщо не з іншої причини, аби уникнути аварій. Також, що стосується "ніколи не чіпай", ОП торкається цього коду, тому ...
jpmc26

17

Обмеження у використанні змінних у операторах SQL виникає в архітектурі SQL.

Існує три фази в обробці оператора SQL:

  1. Підготовка - Оператор аналізується і складається план виконання , вказуючи, до яких об'єктів бази даних звертаються, як вони отримують доступ та як вони пов'язані. План виконання зберігається в кеші плану .
  2. Прив’язка - будь-які змінні в операторі замінюються фактичними значеннями.
  3. Виконання - кешований план виконується з прив’язаними значеннями.

Сервер SQL приховує етап програмування від програміста і виконує його набагато швидше, ніж більш традиційні бази даних, такі як Oracle і DB2. З-за ефективності SQL витрачає потенційно багато часу на визначення оптимального плану виконання, але це робиться лише вперше після того, як оператор стикається після перезавантаження.

Так, у статичному SQL змінні можуть використовуватися лише в тих місцях, де вони не можуть визнати недійсним план виконання, тому не для імен таблиць, назв стовпців (включаючи назви стовпців в умовах WHERE) тощо.

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


7

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

Ця вичерпна стаття SQL MVP Ерланда Соммарського намагається надати деякі обгрунтування разом з механікою:

Прокляття та благословення динамічного SQL :

Планування запитів кешування

Кожен запит, запущений у SQL Server, вимагає план запитів. Коли ви запускаєте запит вперше, SQL Server будує план запитів для нього - або в міру термінології - він збирає запит. SQL Server зберігає план у кеші, і при наступному запуску запиту план буде повторно використаний.

Це (та безпека, див. Нижче), мабуть, найбільша причина.

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

Так, не кожен запит, який ми запускаємо, буде повторно використаний, але це операційна передумова SQL за замовчуванням , тому "винятки" мають бути винятковими.

Кілька інших причин Ерланд перераховує (зауважте, що він чітко перераховує переваги використання збережених процедур , але багато з них є також перевагами параметризованих (нединамічних) запитів):

  • Система дозволів : двигун SQL не може передбачити, чи є у вас права на виконання запиту, якщо він не знає таблиці (або бази даних), над якою будете працювати. "Ланцюги дозволів", що використовують динамічний SQL, - це біль в області прикладу.
  • Зменшення мережевого трафіку : передавання імені збереженого протоколу та кількох значень параметрів по мережі коротше, ніж тривале повідомлення запиту.
  • Інкапсуляція логіки : Вам слід ознайомитись з перевагами логіки інкапсуляції з інших середовищ програмування.
  • Відстеження того, що використовується : Якщо мені потрібно змінити визначення стовпця, як я можу знайти весь код, який його викликає? Системні процедури існують для пошуку залежностей у базі даних SQL, але лише якщо код знаходиться в збережених процедурах.
  • Простота написання коду SQL : Перевірка синтаксису відбувається під час створення або зміни збереженої процедури, тому, сподіваємось, зменшиться кількість помилок.
  • Вирішення помилок та проблем : DBA може відстежувати та вимірювати ефективність окремих зберігаються процедур набагато легше, ніж динамічний SQL, що постійно змінюється.

Знову ж таки, у кожного з них є сто нюансів, я тут не потрапляю.


2

Вам потрібно використовувати динамічний sql

DECLARE @DatabaseName varchar(150) = 'dbamaint'
declare @sqltext nvarchar(max) = N''

set @sqltext = N'CREATE DATABASE '+quotename(@DatabaseName)+ ';'

print @sqltext 

-- once you are happy .. uncomment below
--exec sp_executesql @sqltext
set @sqltext = ''
set @sqltext = N'use '+quotename(@DatabaseName)+ ';'
print @sqltext 
-- once you are happy .. uncomment below
--exec sp_executesql @sqltext

нижче - вихід друку. Після того, як ви скаментуєте, exec sp_executesql @sqltextзаяви фактично будуть виконані ...

CREATE DATABASE [dbamaint];
use [dbamaint];

1
Так, дякую, я це знаю, але я хочу знати, якщо хтось знає, чому ти не можеш просто використовувати цю змінну?
Гарет

Аналізатор T-SQL видасть синтаксичні помилки. Аналізатор не є дійсним T-SQL, який розпізнає аналізатор.
Кін Шах

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

1
Так, вони можуть містити @, і я думаю, що це головна причина. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs

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