132

これは、私が過去に何時間もかけて調査してきた問題です。これは、最新の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 では、個々のストアド プロシージャのスキーマ レベルでアクセス許可を (必要に応じてユーザーごとに) 設定し、テーブルに対するクエリを直接拒否できます。このアプローチの長所と短所を批判することは、おそらく別の質問ですが、これも私の決定ではありません。私はリードコードモンキーです。:)
4

15 に答える 15

103

ええ、それは苦痛です、そしてあなたがやっている方法は私がしていることと似ています:

order by
case when @SortExpr = 'CustomerName' and @SortDir = 'ASC' 
    then CustomerName end asc, 
case when @SortExpr = 'CustomerName' and @SortDir = 'DESC' 
    then CustomerName end desc,
...

これは、DBA にとってスケーラビリティとメンテナンスの悪夢となる、コードから動的 SQL を構築するよりもはるかに優れていると思います。

私がコードから行うことは、ページングとソートをリファクタリングすることです。そのため、少なくとも と の値を入力することで多くの繰り返しはありませ@SortExpr@SortDir

SQL に関する限り、さまざまなストアド プロシージャ間でデザインとフォーマットを同じに保つことで、変更を加えたときに少なくともきちんと認識できるようになります。

于 2008-09-29T16:42:13.197 に答える
23

このアプローチは、並べ替え可能な列が順序で 2 回重複するのを防ぎ、もう少し読みやすい IMO です。

SELECT
  s.*
FROM
  (SELECT
    CASE @SortCol1
      WHEN 'Foo' THEN t.Foo
      WHEN 'Bar' THEN t.Bar
      ELSE null
    END as SortCol1,
    CASE @SortCol2
      WHEN 'Foo' THEN t.Foo
      WHEN 'Bar' THEN t.Bar
      ELSE null
    END as SortCol2,
    t.*
  FROM
    MyTable t) as s
ORDER BY
  CASE WHEN @dir1 = 'ASC'  THEN SortCol1 END ASC,
  CASE WHEN @dir1 = 'DESC' THEN SortCol1 END DESC,
  CASE WHEN @dir2 = 'ASC'  THEN SortCol2 END ASC,
  CASE WHEN @dir2 = 'DESC' THEN SortCol2 END DESC
于 2008-09-30T04:35:39.333 に答える
6

特定のジョブで動的 SQL を回避するために私が使用したスト​​アド プロシージャ手法 (ハック?) は、一意の並べ替え列を持つことです。すなわち、

SELECT
   name_last,
   name_first,
   CASE @sortCol WHEN 'name_last' THEN [name_last] ELSE 0 END as mySort
FROM
   table
ORDER BY 
    mySort

これは簡単に送信できます。mySort 列のフィールドを連結したり、数学関数や日付関数で順序を逆にしたりすることができます。

できれば、Sql-Server からデータを取得した後に、asp.net グリッドビューまたは組み込みの並べ替えを備えた他のオブジェクトを使用して、並べ替えを行います。または、組み込みではない場合でも-たとえば、asp.netのデータテーブルなど。

于 2010-08-02T17:17:19.353 に答える
6

動的 SQL は引き続きオプションです。そのオプションが現在持っているものよりも口に合うかどうかを判断する必要があります.

これを示す記事があります: http://www.4guysfromrolla.com/webtech/010704-1.shtml .

于 2008-09-29T16:28:43.043 に答える
6

私のアプリケーションはこれを頻繁に行っていますが、それらはすべて動的に SQL を構築しています。ただし、ストアド プロシージャを扱うときは、次のようにします。

  1. ストアド プロシージャを、値のテーブルを返す関数にします - 並べ替えはありません。
  2. 次に、アプリケーション コードで a を実行してselect * from dbo.fn_myData() where ... order by ...、ソート順を動的に指定できるようにします。

次に、少なくとも動的な部分はアプリケーションにありますが、データベースはまだ重い仕事をしています。

于 2008-09-29T19:08:38.103 に答える
4

これをハッキングする方法はいくつかあります。

前提条件:

  1. sp 内の 1 つの SELECT ステートメントのみ
  2. ソートを省略します(またはデフォルトを設定します)

次に、一時テーブルに挿入します。

create table #temp ( your columns )

insert #temp
exec foobar

select * from #temp order by whatever

方法 #2: リンク サーバーをセットアップして、それ自体に戻してから、openquery を使用してこれから選択します: http://www.sommarskog.se/share_data.html#OPENQUERY

于 2008-09-29T22:17:55.760 に答える
4

サーバーには予備のサイクルがたくさんあるため、3 番目のオプションがあるかもしれません。ヘルパー プロシージャを使用して、一時テーブルを介して並べ替えを行います。何かのようなもの

create procedure uspCallAndSort
(
    @sql varchar(2048),        --exec dbo.uspSomeProcedure arg1,'arg2',etc.
    @sortClause varchar(512)    --comma-delimited field list
)
AS
insert into #tmp EXEC(@sql)
declare @msql varchar(3000)
set @msql = 'select * from #tmp order by ' + @sortClause
EXEC(@msql)
drop table #tmp
GO

警告: これはテストしていませんが、SQL Server 2005 で動作するはずです (列を事前に指定しなくても、結果セットから一時テーブルが作成されます)。

于 2008-09-29T16:49:52.617 に答える
2

同意します。クライアント側を使用します。しかし、それはあなたが聞きたい答えではないようです。

だから、それはそれがそうであるように完璧です。なぜあなたがそれを変えたいのか、あるいは「もっと良い方法はありますか」と尋ねるのか分かりません。本当に、それは「道」と呼ばれるべきです。その上、それはうまく機能し、プロジェクトのニーズにうまく適合しているようであり、おそらく今後数年間は十分に拡張可能です。データベースには課税がなく、並べ替えは非常に簡単なので、今後数年間はそのままにしておく必要があります。

汗をかかない。

于 2008-09-29T18:00:51.887 に答える
2

クライアント側で並べ替えを行うことに反対する理由は、大量のデータとページネーションです。行数が簡単に表示できる数を超えると、多くの場合、SQL で実行したいスキップ/テイクの一部としてソートされます。

Entity Framework の場合、ストアド プロシージャを使用してテキスト検索を処理できます。同じ並べ替えの問題が発生した場合、私が見た解決策は、検索にストアド プロシージャを使用し、一致する ID キー セットのみを返すことです。次に、リスト (contains) 内の ID を使用して、データベースに対して (並べ替えを使用して) 再クエリを実行します。EF は、ID セットがかなり大きい場合でも、これをうまく処理します。はい、これは 2 回の往復ですが、状況によっては重要になる可能性のある並べ替えを常に DB に保持することができ、ストアド プロシージャに大量のロジックを書き込むことを防ぐことができます。

于 2014-01-07T18:56:25.250 に答える
2

ある時点で、ストアド プロシージャから離れて、パラメーター化されたクエリだけを使用して、この種のハッカーを回避する価値があると思いませんか?

于 2008-09-29T16:27:11.690 に答える
1

SQL ではなく、結果を表示するもの (グリッド、レポートなど) でソートを処理するのはどうですか?

編集:

この回答が以前に反対票を投じられて以来、物事を明確にするために、少し詳しく説明します...

クライアント側の並べ替えについては知っていたが、それを避けたいと述べました。もちろん、それはあなたの呼びかけです。

ただし、私が指摘したいのは、クライアント側でそれを行うことで、一度データをプルしてから、必要に応じて操作できるということです。毎回サーバーに何度も行き来するのではありませんソートが変更されます。

あなたの SQL Server は今のところ課税されていません。それはすばらしいことです。そうであってはなりません。しかし、まだ過負荷になっていないからといって、それがずっと続くわけではありません。

Web 上での表示に新しい ASP.NET を使用している場合、その多くは既に組み込まれています。

並べ替えを処理するためだけに、各ストアド プロシージャに多くのコードを追加する価値はありますか? 繰り返しますが、あなたの電話です。

最終的にサポートするのは私ではありません。ただし、ストアド プロシージャで使用されるさまざまなデータセット内で列が追加/削除される (CASE ステートメントの変更が必要) 場合や、ユーザーが突然 2 列でソートする代わりに 3 列が必要であると判断した場合に、何が関係するかを考えてみてください。このメソッドを使用するすべてのストアド プロシージャを更新する必要があります。

私にとっては、機能するクライアント側のソリューションを取得し、それをユーザー向けの少数のデータ表示に適用して、それで完了することは価値があります。新しい列が追加された場合、それは既に処理されています。ユーザーが複数の列で並べ替えたい場合は、2 つまたは 20 の列で並べ替えることができます。

于 2008-09-29T16:06:57.477 に答える
-1

このソリューションは .NET でのみ機能する可能性がありますが、わかりません。

SQL の order by 句の最初の並べ替え順序でデータを C# にフェッチし、そのデータを DataView に入れ、それを Session 変数にキャッシュし、それを使用してページを作成します。

ユーザーが列見出しをクリックして並べ替え (またはページ、またはフィルター) しても、データベースには戻りません。代わりに、キャッシュされた DataView に戻り、その「Sort」プロパティを動的に作成する式に設定します。これは、動的 SQL と同様です。(「RowFilter」プロパティを使用して、同じ方法でフィルタリングを行います)。

http://ifdefined.com/btnet/bugs.aspxにある私のアプリ、BugTracker.NET のデモで動作を確認できます。

于 2008-09-30T04:11:00.500 に答える
-8

必要な場合を除き、SQL Server の並べ替えは避ける必要があります。アプリサーバーまたはクライアント側で並べ替えてみませんか? また、.NET Generics は例外的なソートを行います

于 2008-09-30T04:16:48.020 に答える