私はSQL関連の質問をしたところ、最初の答えは「これは動的SQLが適している状況です」でした。
以前に動的SQLについて聞いたことがなかったので、私はすぐにこのサイトとWebでそれが何であるかを検索しました。ウィキペディアにこのタイトルの記事はありません。最初のGoogleの結果はすべて、人々が多かれ少なかれ関連する質問をするユーザーフォーラムを指しています。
しかし、「動的SQL」とは何かの明確な定義が見つかりませんでした。 ベンダー固有のものですか?MySQLを使用していて、使用しませんでしたMySQLハンドブックで参照を見つけます(MySQLユーザーフォーラムで質問のみ(ほとんどが未回答))。
一方、ストアドプロシージャへの多くの参照を見つけました。ストアドプロシージャとは少し理解しましたが、使用したことはありません。 2つの概念はどのように関連していますか?それらは同じものですか、それとも一方がもう一方を使用していますか?
基本的に、必要なのは、その概念に不慣れな人のための動的SQLの簡単な紹介です。
追伸:気に入った場合は、前の質問に答えてみてください: SQL:テーブル1のフィールドで指定されたテーブルにテーブル1をテーブル2に結合するにはどうすればよいですか?
動的SQLは、クエリがオンザフライで作成された場所です-一部のベンダーでは、1つのストアドプロシージャ内で動的クエリのテキストを作成し、生成されたSQLを実行できます。他の場合では、この用語は単にクライアントのコードによって行われた決定を指します(これは少なくともベンダーに中立です)
他の回答ではwhat動的SQLが定義されていますが、理由を説明しようとする他の回答は見つかりませんでした時々それを使用する必要があります。 (私の経験はSQL Serverですが、他の製品もこの点では一般的に似ていると思います。)
動的SQLは、他の方法を使用して置き換えることができないクエリの一部を置き換える場合に役立ちます。
たとえば、次のようなクエリを呼び出すたびに:
SELECT OrderID, OrderDate, TotalPrice FROM Orders WHERE CustomerID = ??
customerIDに別の値を渡します。これは最も単純なケースであり、パラメーター化されたクエリ、またはパラメーターを受け入れるストアドプロシージャなどを使用して解決できます。
一般的に言えば、パフォーマンスとセキュリティ上の理由から、パラメータ化されたクエリを優先して動的SQLを回避する必要があります。 (ただし、パフォーマンスの違いは、おそらくベンダー間で、またおそらくは製品バージョン間で、またはサーバー構成でさえも、かなり異なります)。
他のクエリはパラメーターを使用して可能ですが、simpler動的SQLとして:
SELECT OrderID, OrderDate, TotalPrice FROM Orders
WHERE CustomerID IN (??,??,??)
常に3つの値があった場合、これは最初の値と同じくらい簡単です。しかし、これが可変長リストである場合はどうでしょうか。パラメータで行うことは可能ですが、非常に難しい場合があります。どうですか:
SELECT OrderID, OrderDate, TotalPrice FROM Orders WHERE CustomerID = ??
ORDER BY ??
これを直接置き換えることはできません。ORDERBYの巨大で複雑なCASEステートメントを使用して、可能なすべてのフィールドを明示的にリストすることができます。
最後に、一部のクエリは他の方法では実行できません。
Ordersテーブルがたくさんあるとしましょう(これが優れたデザインであるとは言えません)が、次のようなことができると思っているかもしれません。
SELECT OrderID, OrderDate, TotalPrice FROM ?? WHERE CustomerID = ??
これは、他の方法を使用して行うことはできません。私の環境では、次のようなクエリが頻繁に発生します。
SELECT (programatically built list of fields)
FROM table1 INNER JOIN table2
(Optional INNER JOIN to table3)
WHERE (condition1)
AND (long list of other optional WHERE clauses)
繰り返しになりますが、これは必ずしも優れた設計であるとは言えませんが、これらのタイプのクエリには動的SQLがほとんど必要です。
お役に立てれば。
動的SQLは、実行前にその場で構成されるSQLステートメントです。たとえば、次のC#(パラメーター化されたクエリを使用):
var command = new SqlCommand("select * from myTable where id = @someId");
command.Parameters.Add(new SqlParameter("@someId", idValue));
動的SQLを使用して次のように書き直すことができます。
var command = new SqlCommand("select * from myTable where id = " + idValue);
ただし、動的SQLはSQLインジェクション攻撃を容易に許可するため危険です。
動的SQLは、実行時に文字列から構築されるSQLです。フィルターなどを動的に設定すると便利です。
例:
declare @sql_clause varchar(1000)
declare @sql varchar(5000)
set @sql_clause = ' and '
set @sql = ' insert into #tmp
select
*
from Table
where propA = 1 '
if @param1 <> ''
begin
set @sql = @sql + @sql_clause + ' prop1 in (' + @param1 + ')'
end
if @param2 <> ''
begin
set @sql = @sql + @sql_clause + ' prop2 in (' + @param2 + ')'
end
exec(@sql)
それはまさに Rowland が言及したものです。少し詳しく説明するには、次のSQLを使用します。
Select * from table1 where id = 1
データベースへの接続に使用している言語はわかりませんが、C#を使用する場合、動的SQLクエリの例は次のようになります。
string sqlCmd = "Select * from table1 where id = " + userid;
クエリが大きくなりすぎると、コードの整合性を維持するのが少し面倒になるため、動的SQLの使用は避けたいと思います。また、非常に重要なのは、動的SQLがSQLインジェクション攻撃を受けやすいことです。
上記のステートメントを記述するより良い方法は、SQL Serverを使用している場合はパラメーターを使用することです。
Rowland は正解であり、パラメータとして適切に使用している場合(提供されたテキストからインラインでパラメータ値を連結する場合など)を除いて、これはセキュリティリスクになる可能性もあります。また、デバッグする必要があります。
最後に、動的SQLを賢く使用しないと、事態は解き放たれ、子供たちが食べられます。
ほとんどのデータベースにとって、すべてのSQLクエリは「動的」です。つまり、入力SQL文字列とパラメーターバインディング(「バインド変数」)が与えられた場合、クエリオプティマイザーによって解釈されるプログラムです。
ただし、ほとんどの場合、そのSQL文字列はPL/SQLなどの手続き型言語でも動的ではなく静的に構築されます。
FOR rec IN (SELECT * FROM foo WHERE x = 1) LOOP
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "static SQL"
..
END LOOP;
または、JDBCを使用してJavaのようなクライアント/ホスト言語で:
try (ResultSet rs = stmt.executeQuery("SELECT * FROM foo WHERE x = 1")) {
// "static SQL" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..
}
どちらの場合も、SQL文字列はそれを埋め込む言語では「静的」です。技術的には、SQL文字列がどのように構成されているのか、または静的SQL文字列であるのかを認識していないSQLエンジンに対しては、依然として「動的」です。
場合によっては、いくつかの入力パラメーターを指定して、SQL文字列を動的に構築する必要があります。例えば。上記のクエリでは、述語がまったく必要ない場合があります。
次に、動的に文字列を構築することを選択する場合があります。 PL/SQLの場合:
DECLARE
TYPE foo_c IS REF CURSOR;
v_foo_c foo_c;
v_foo foo%ROWTYPE;
sql VARCHAR2(1000);
BEGIN
sql := 'SELECT * FROM foo';
IF something THEN
sql := sql || ' WHERE x = 1'; -- Beware of syntax errors and SQL injection!
END IF;
OPEN v_foo_c FOR sql;
LOOP
FETCH v_foo_c INTO v_foo;
EXIT WHEN v_foo_c%NOTFOUND;
END LOOP;
END;
またはJava/JDBC:
String sql = "SELECT * FROM foo";
if (something)
sql += " WHERE x = 1"; // Beware of syntax errors and SQL injection!
try (ResultSet rs = stmt.executeQuery(sql)) {
..
}
// No syntax error / SQL injection risk here
Condition condition = something ? FOO.X.eq(1) : DSL.trueCondition();
for (FooRecord foo : DSL.using(configuration)
.selectFrom(FOO)
.where(condition)) {
..
}
多くの言語には、上記のようなクエリビルダーライブラリがあり、動的SQLを実行するときに最も効果的です。
(免責事項:私はjOOQの背後にある会社で働いています)
ベンダー固有のものですか?
SQL-92標準には、動的SQL(第17章)に関する章全体がありますが、これは完全なSQL-92にのみ適用され、それを実装したベンダーは知りません。
つまり、クエリを実行する前に動的に作成する必要があるということです。他の質問の場合、これは、最初に必要なテーブル名を選択し、プログラミング言語を使用して、やりたいことを実行するための2番目のクエリを作成する必要があることを意味します)。