これは、過去に何時間も調査してきた問題です。現代のRDBMSソリューションで対処すべきだったように思えますが、データベースバックエンドを備えたWebまたはWindowsアプリケーションで信じられないほど一般的なニーズであると私が見ているものに実際に対処するものはまだ見つかりません。
動的ソートについてお話します。私のファンタジーの世界では、それは次のような単純なものでなければなりません:
ORDER BY @sortCol1, @sortCol2
これは、初心者のSQLおよびストアドプロシージャの開発者がインターネット上のフォーラム全体で行った標準的な例です。「なぜこれが可能ではないのですか?」彼らが聞く。必ず、誰かが最終的にストアドプロシージャのコンパイルされた性質、一般的な実行計画、およびパラメーターを直接ORDER BY
句に入れることができない他のあらゆる種類の理由について彼らに説明しに来ます。
「クライアントに並べ替えをしてもらいましょう」と既に思っている人もいます。当然、これはデータベースから作業をオフロードします。私たちの場合でも、データベースサーバーは99%の確率で汗をかいていませんし、マルチコアでも、6か月ごとに発生するシステムアーキテクチャの他の無数の改善でもありません。この理由だけで、データベースにソートを処理させることは問題になりません。さらに、データベースは非常に選別が得意です。それらはそれに最適化されており、正しく理解するために何年もかかっています。それを行うための言語は信じられないほど柔軟で直感的でシンプルであり、何よりも初心者のSQLライターはそれを行う方法を知っており、さらに重要なことに、編集方法を知っています。変更を加えたり、メンテナンスを行ったりします。データベースに負担がかけられず、開発時間を単純化(および短縮)したい場合、これは当然の選択のようです。
次に、ウェブの問題があります。私はクライアント側でHTMLテーブルの並べ替えを行うJavaScriptをいじってみましたが、それらは必然的に私のニーズに対して十分な柔軟性を備えていません。また、データベースに過度の負担がかからず、並べ替えを非常に簡単に実行できるため、私自身のJavaScriptソーターを書き換えたり、ロールバックしたりするのにかかる時間を正当化するのに苦労しています。同じことはサーバーサイドのソートにも当てはまりますが、おそらくJavaScriptよりもはるかに優先されています。私は特にDataSetsのオーバーヘッドが好きではないので、私を訴えます。
しかし、これはそれが不可能である、またはむしろ、容易ではないという点を取り戻します。以前のシステムでは、動的な並べ替えを行うための信じられないほどハックな方法を実行しました。それはきれいでもなく、直感的でも、単純でも、柔軟でもなかったため、初心者のSQLライターは数秒で失われてしまいます。すでに、これはそれほど「解決策」ではなく「複雑化」になりそうです。
次の例は、あらゆる種類のベストプラクティスや優れたコーディングスタイルなどを公開することを意図したものではなく、T-SQLプログラマとしての私の能力を示すものでもありません。彼らは彼らが何であるかであり、私は彼らが混乱し、悪い形で、そしてただのハックであることを完全に認めます。
整数値をパラメーターとしてストアード・プロシージャーに渡し(パラメーターを単に「ソート」と呼びましょう)、そこから他の変数の束を判別します。たとえば... sortが1(またはデフォルト)だとしましょう:
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
他の列を定義するためにさらに@colX変数を宣言した場合、「sort」の値に基づいて並べ替える列を使って実際に創造的になる方法を確認できます...使用するには、通常、次のようになります。信じられないほど乱雑な条項:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
明らかに、これは非常に簡略化された例です。実際のものは、通常、並べ替えをサポートするために4つまたは5つの列があり、それぞれにさらに2番目または3番目の列を並べ替えることができます(たとえば、日付が降順で、次に名前の昇順で2番目に並べ替えられます)。ケース数を事実上2倍にする方向ソート。ええ...毛深いです
考えは、vehicleidがstoragedatetimeの前にソートされるようにソートケースを「簡単に」変更できるということです...しかし、少なくともこの単純な例では、疑似柔軟性は本当にそこで終わります。基本的に、テストに失敗した各ケース(今回はsortメソッドが適用されないため)はNULL値をレンダリングします。したがって、次のように機能する句が作成されます。
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
あなたはアイデアを得ます。SQL Serverは、by句の順序でnull値を効果的に無視するため、機能します。これは、SQLの基本的な動作知識を持つ人なら誰でも見ることができるように、維持するのが非常に困難です。もし私があなたのどれかを失ったとしても、気分を悪くしないでください。それを機能させるのに長い時間を要し、それを編集したり、そのような新しいものを作成しようとしたりしても混乱します。ありがたいことに、頻繁に変更する必要はありません。そうしないと、すぐに「トラブルに値しない」状態になります。
それでもうまくいきました。
私の質問はそれです: より良い方法はありますか?
ストアドプロシージャ以外のソリューションでも大丈夫です。解決策にはならないかもしれません。できれば、ストアドプロシージャ内でもっとうまくできるかどうか知りたいのですが、できない場合は、ASP.NETを使用してユーザーがデータのテーブルを(双方向でも)動的に並べ替えられるようにするにはどうすればよいですか?
そして、長い質問を読んで(または少なくともスキミングして)くれてありがとう!
PS:私はROWNUMBER()OVER経由でストアドプロシージャそのサポートダイナミックソート、列の動的なフィルタリング/テキスト検索、ページネーションの私の例を示し、なかった喜んでいるとしてみてください...キャッチをトランザクションがエラーにrollbackingで... 「巨大なサイズ」はそれらを説明し始めません。
更新:
- 動的SQLは避けたい。文字列を一緒に解析し、その文字列でEXECを実行すると、そもそもストアドプロシージャを作成する多くの目的が損なわれます。そのようなことをすることの短所は、少なくともこれらの特別な動的ソートの場合には価値がないのではないかと思うことがあります。それでも、そのような動的SQL文字列を実行するときはいつも、いつも汚れていると感じます。たとえば、クラシックASPの世界にまだ住んでいるようなものです。
- そもそもストアドプロシージャが必要な理由の多くは、セキュリティのためです。私はセキュリティ上の懸念について電話をかけることはできず、解決策を提案するだけです。SQL Server 2005では、個々のストアドプロシージャのスキーマレベルで(必要に応じてユーザーごとに)アクセス許可を設定し、テーブルに対するクエリを直接拒否できます。このアプローチの長所と短所を批判することはおそらく別の問題ですが、やはり私の決定ではありません。私はただのコードモンキーです。:)