web-dev-qa-db-ja.com

想像できるように、T-SQLで変数を使用できないのはなぜですか?

私を許して、私はSQLの世界に移動した開発者です。変数を追加していくつかのSQLを改善できると思いましたが、期待したように機能しませんでした。なぜこれがうまくいかないのか誰かに教えてもらえますか?私は回避策を望んでいません。これは、正当な理由があると確信しているので、私が想像するようにこれが機能しない理由を知りたいのですが、現在、私には飛び出しません。

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

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
18
gareth

変数 のBooksオンラインページごと

変数は式でのみ使用でき、オブジェクト名やキーワードの代わりには使用できません。動的SQLステートメントを作成するには、EXECUTEを使用します。

これは、たとえば、where句で変数を使用した場合に期待したとおりに機能します。理由については、パーサーが変数を評価できず、存在を確認できないことに関係していると思います。クエリを実行すると、まず構文とオブジェクトについてクエリが解析され、次に解析が成功した場合、クエリが実行され、その時点で変数が設定されます。

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

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

insert into #tmp
values('test')

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

SQLステートメントでの変数の使用に関する制限は、SQLのアーキテクチャーから生じます。

SQLステートメントの処理には3つのフェーズがあります。

  1. 準備-ステートメントが解析され、実行計画がコンパイルされ、アクセスされるデータベースオブジェクト、アクセス方法、および関連性が指定されます。実行プランはプランキャッシュに保存されます。
  2. バインド-ステートメント内の変数は実際の値に置き換えられます。
  3. 実行-キャッシュされたプランは、バインドされた値で実行されます。

SQLサーバーは準備段階をプログラマーから隠し、OracleやDB2などの従来のデータベースよりもはるかに高速に実行します。 SQLが最適な実行計画を決定するのに潜在的に多くの時間を費やすのはパフォーマンス上の理由からですが、再起動後に初めてステートメントが検出されたときだけです。

したがって、静的SQLでは、変数は実行プランを無効にしない場所でのみ使用できます。テーブル名、列名(WHERE条件の列名を含む)などでは使用できません。

動的SQLは、制限を回避できない場合に存在し、プログラマは実行に少し時間がかかることを知っています。動的SQLは悪意のあるコードインジェクションに対して脆弱になる可能性があるため、注意してください!

17
grahamj42

ご覧のとおり、「なぜ」の質問には、言語の歴史的根拠や根本的な仮定など、さまざまな種類の回答が必要です。その正義が本当にできるかどうかはわかりません。

SQL MVP Erland Sommarskogによるこの包括的な記事は、メカニズムとともにいくつかの理論的根拠を提供しようとしています。

動的SQLの呪いと祝福

クエリプランのキャッシュ

SQL Serverで実行するすべてのクエリには、クエリプランが必要です。クエリを初めて実行すると、SQL Serverはクエリのクエリプランを作成します(用語が進むにつれて)、クエリをコンパイルします。 SQL Serverはプランをキャッシュに保存し、次にクエリを実行すると、プランが再利用されます。

これ(およびセキュリティ、以下を参照)がおそらく最大の理由です。

SQLは、クエリが1回限りの操作ではなく、何度も使用されるという前提の下で動作します。テーブル(またはデータベース!)がクエリで実際に指定されていない場合、将来使用するために実行プランを生成して保存する方法はありません。

はい、実行するすべてのクエリが再利用されるわけではありませんが、これはSQLのデフォルトの動作前提であるため、「例外」は例外的なものです。

Erlandがリストする他のいくつかの理由(ストアドプロシージャを使用する利点を明示的に示していることに注意してください。ただし、これらの多くはパラメーター化(非動的)クエリの利点でもあります)

  • アクセス許可システム:SQLエンジンは、テーブル(またはデータベース)を知らない場合、クエリを実行する権限があるかどうかを予測できませんあなたは反対するでしょう。動的SQLを使用した「パーミッションチェーン」は、お尻の痛みです。
  • ネットワークトラフィックの削減:ストアドプロシージャの名前といくつかのパラメーター値をネットワーク経由で渡すと、長いクエリステートメントよりも短くなります。
  • ロジックのカプセル化:他のプログラミング環境からロジックをカプセル化することの利点をよく理解している必要があります。
  • 使用されているものの追跡:列定義を変更する必要がある場合、それを呼び出すすべてのコードをどのようにして見つけることができますか?システムプロシージャは、SQLデータベース内の依存関係を見つけるために存在しますが、コードがストアドプロシージャ内にある場合のみです。
  • SQLコードの書きやすさ:ストアドプロシージャを作成または変更するときに構文チェックが行われるため、エラーが少なくなることが期待されます。
  • バグと問題への対応:DBAは、絶え間なく変化する動的SQLよりもはるかに簡単に、個々のストアドプロシージャのパフォーマンスを追跡および測定できます。

繰り返しますが、これらにはそれぞれ、ここでは触れない100のニュアンスがあります。

7
BradC

動的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

以下はprintの出力です.. exec sp_executesql @sqltextのコメントを解除すると、ステートメントが実際に実行されます...

CREATE DATABASE [dbamaint];
use [dbamaint];
2
Kin Shah