これは、私が過去に何時間もかけて調査してきた問題です。これは、最新のRDBMSソリューションによって対処されるべきものであるように思えますが、データベース バックエンドを備えた Web または Windows アプリケーションで信じられないほど一般的なニーズであると私が考えるものに実際に対処するものはまだ見つかっていません。
動的ソートについて話します。私のファンタジーの世界では、次のような単純なものにする必要があります。
ORDER BY @sortCol1, @sortCol2
これは、インターネット上のあらゆるフォーラムで、初心者の SQL およびストアド プロシージャの開発者によって与えられた標準的な例です。「なぜこれができないのですか?」彼らが聞く。ORDER BY
必ず、最終的には、ストアド プロシージャのコンパイルされた性質、一般的な実行計画、およびパラメータを句に直接入れることができないその他のあらゆる種類の理由について講義するために誰かがやって来ます。
「それでは、クライアントにソートを任せてください」と、すでに考えている人もいると思います。当然、これによりデータベースから作業がオフロードされます。ただし、私たちの場合、データベース サーバーは 99% の時間で汗をかきさえしておらず、まだマルチコアでもなく、6 か月ごとに行われるシステム アーキテクチャのその他の無数の改善も行っていません。この理由だけでも、データベースでソートを処理することは問題になりません。さらに、データベースは非常に仕分け上手。彼らはそれのために最適化されており、それを正しく行うために何年も費やしてきました.それを行うための言語は信じられないほど柔軟で直感的でシンプルであり、何よりも初心者のSQLライターはそれを行う方法を知っています.変更を加えたり、メンテナンスを行ったりします。データベースに負担がかからず、開発時間を単純化 (および短縮) したいだけの場合、これは当然の選択のように思えます。
次に、Web の問題です。クライアント側で HTML テーブルの並べ替えを行う JavaScript をいじってみましたが、必然的に私のニーズに十分に柔軟に対応できません。 JavaScript ソーターを書き直したり、自作したりするのにかかる時間を正当化するのに苦労しています。同じことがサーバー側のソートにも一般的に当てはまりますが、すでにおそらく JavaScript よりもはるかに好まれています。私は特に DataSet のオーバーヘッドが好きではないので、訴えてください。
しかし、これは、それが不可能である、というより、簡単ではないという点を思い起こさせます。私は、以前のシステムで、動的ソートを取得する信じられないほどハックな方法を実行しました。それは美しくもなく、直観的でもなく、単純でも柔軟でもなく、初心者の 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 変数を宣言した場合、「並べ替え」の値に基づいて並べ替える列で実際にクリエイティブになる方法を既に確認できます...それを使用すると、通常は次のようになります非常に厄介な節:
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 倍にする方向性ソート。うん…毛むくじゃらになるのは本当に早い。
アイデアは、vehicleid が storagedatetime の前にソートされるようにソートケースを「簡単に」変更できるということです...しかし、少なくともこの単純な例では、擬似的な柔軟性は本当にそこで終わります。基本的に、テストに失敗した各ケース (今回は並べ替えメソッドが適用されないため) は NULL 値をレンダリングします。したがって、次のように機能する句になります。
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
あなたはアイデアを得る。これが機能するのは、SQL Server が order by 句で null 値を効果的に無視するためです。これは、SQL の基本的な作業知識を持っている人なら誰でもわかるように、維持するのが非常に困難です。私があなたの誰かを失ったとしても、気にしないでください。機能するようになるまでには長い時間がかかりましたが、それを編集したり、同様の新しいものを作成したりしようとして、いまだに混乱しています。ありがたいことに、頻繁に変更する必要はありません。
それでもうまくいきました。
私の質問は次のとおり です。より良い方法はありますか?
ストアド プロシージャ以外のソリューションでも問題ありません。できれば、誰かがストアド プロシージャ内でより適切に実行できるかどうかを知りたいのですが、そうでない場合は、ASP.NET を使用してユーザーがデータ テーブルを動的に (双方向にも) ソートできるようにするにはどうすればよいでしょうか?
そして、このような長い質問を読んでくれて (または少なくともスキミングして) ありがとうございました!
PS: 動的ソート、列の動的フィルタリング/テキスト検索、ROWNUMBER() OVER によるページネーション、およびtry...エラー時のトランザクション ロールバックによるキャッチをサポートするストアド プロシージャの例を示していないことを嬉しく思います... 「巨大なサイズ」は、それらを説明し始めることすらありません.
アップデート:
- 動的 SQL は避けたいです。文字列を一緒に解析してそれに対して EXEC を実行すると、そもそもストアド プロシージャを使用する目的の多くが無効になります。少なくともこれらの特別な動的ソートのケースでは、そのようなことをすることの短所は価値がないのではないかと思うことがあります。それでも、このような動的な SQL 文字列を処理するときは常に汚いと感じます — まだクラシック ASP の世界に住んでいるように。
- そもそもストアド プロシージャが必要な理由の多くは、セキュリティのためです。私はセキュリティ上の懸念について電話をかけることはできません。解決策を提案するだけです。SQL Server 2005 では、個々のストアド プロシージャのスキーマ レベルでアクセス許可を (必要に応じてユーザーごとに) 設定し、テーブルに対するクエリを直接拒否できます。このアプローチの長所と短所を批判することは、おそらく別の質問ですが、これも私の決定ではありません。私はリードコードモンキーです。:)