通常は「自然に」ソートする必要がある数字と文字の混合ストリングである値について、いくつかのレポートを作成する必要があります。たとえば、 「P7B18」または「P12B3」。 @文字列は、ほとんどが文字のシーケンスであり、次に数字が交互になります。ただし、これらのセグメントの数とそれぞれの長さは異なる場合があります。
これらの数値部分を数値順にソートしてください。明らかに、これらの文字列値をORDER BY
で直接処理する場合、「P12B3」は「P7B18」の前に来るはずです。 「P7」は当然「P12」に先行します。
範囲の比較もできるようにしたいと思います。 @bin < 'P13S6'
など。浮動小数点数や負の数を処理する必要はありません。これらは厳密に私たちが扱っている負でない整数になります。文字列の長さとセグメント数は、上限が固定されていないため、潜在的に任意である可能性があります。
私たちのケースでは、文字列の大文字小文字の区別は重要ではありませんが、照合に対応した方法でこれを行う方法がある場合、他の人が便利だと思うかもしれません。これらすべての最も醜い部分は、WHERE
句で順序付けと範囲フィルタリングの両方を実行できるようにすることです。
これをC#で実行している場合、これは非常に単純なタスクです。いくつかの解析を行ってアルファを数値から分離し、IComparableを実装すれば、基本的にはこれで完了です。もちろん、SQL Serverは、少なくとも私の知る限り、同様の機能を提供していないようです。
誰かがこれを機能させるための良いトリックを知っていますか? IComparableを実装し、これを期待どおりに動作させるカスタムCLR型を作成する、あまり公表されていない機能はありますか?私はまた、愚かなXMLトリック(リストの連結も参照)に反対していません。また、サーバーでCLR正規表現のマッチング/抽出/置換ラッパー関数も使用できます。
編集:もう少し詳細な例として、データが次のように動作するようにしたいと思います。
SELECT bin FROM bins ORDER BY bin
bin
--------------------
M7R16L
P8RF6JJ
P16B5
PR7S19
PR7S19L
S2F3
S12F0
つまり、文字列をすべての文字またはすべての数字のトークンに分割し、それらをそれぞれアルファベット順または数値順に並べ替えます。左端のトークンが最も重要な並べ替え条件です。先ほど触れたように、IComparableを実装した場合の.NETの簡単な説明ですが、SQL Serverでそのようなことをどのように(または可能か)行うことができるかはわかりません。それは確かに、私が10年程度の作業で遭遇したものではありません。
これを行うための組み込みの簡単な方法はありませんが、可能性は次のとおりです。
文字列を固定長セグメントに再フォーマットして正規化します。
VARCHAR(50) COLLATE Latin1_General_100_BIN2
のソート列を作成します。最大長の50は、セグメントの最大数とそれらの潜在的な最大長に基づいて調整する必要がある場合があります。AFTER [or FOR] INSERT, UPDATE
_トリガーに配置して、値を適切に設定することが保証されますすべてのレコード、アドホッククエリなどから受信したレコードも含まれます。もちろん、そのスカラーUDFはSQLCLRを介して処理することもできますが、実際にどちらがより効率的かを判断するためにテストする必要があります。 **UPPER()
関数をすべてのセグメントの最終結果に適用します(そのため、1回だけ実行する必要があり、セグメント)。これにより、並べ替え列のバイナリ照合順序で適切な並べ替えが可能になります。AFTER INSERT, UPDATE
_トリガーを作成します。パフォーマンスを向上させるには、UPDATE()
関数を使用して、このコード列がSET
ステートメントのUPDATE
句にさえあるかどうかを判断します(falseの場合はRETURN
のみ) )、次にコード列のINSERTED
およびDELETED
疑似テーブルを結合して、コード値が変更された行のみを処理します。変更があるかどうかを正確に判断するには、そのJOIN条件で必ず_COLLATE Latin1_General_100_BIN2
_を指定してください。例:
_P7B18 -> "P 000007B 000018"
P12B3 -> "P 000012B 000003"
P12B3C8 -> "P 000012B 000003C 000008"
_
このアプローチでは、次の方法でソートできます。
_ORDER BY tbl.SortColumn
_
また、次の方法で範囲フィルタリングを実行できます。
_WHERE tbl.SortColumn BETWEEN dbo.MyUDF('P7B18') AND dbo.MyUDF('P12B3')
_
または:
_DECLARE @RangeStart VARCHAR(50),
@RangeEnd VARCHAR(50);
SELECT @RangeStart = dbo.MyUDF('P7B18'),
@RangeEnd = dbo.MyUDF('P12B3');
WHERE tbl.SortColumn BETWEEN @RangeStart AND @RangeEnd
_
_ORDER BY
_フィルターとWHERE
フィルターの両方で、照合優先順位により、SortColumn
に定義されたバイナリ照合を使用する必要があります。
等価比較は、元の値列で引き続き行われます。
他の考え:
SQLCLR UDTを使用します。これは機能する可能性がありますが、上記のアプローチと比較して純利益が得られるかどうかは不明です。
はい。SQLCLRUDTでは、比較演算子をカスタムアルゴリズムでオーバーライドできます。これは、値が既に同じカスタム型である別の値、または暗黙的に変換する必要がある値のいずれかと比較される状況を処理します。これはすべきWHERE
条件で範囲フィルターを処理します。
UDTを(計算列ではなく)通常の列タイプとしてソートすることに関しては、これはUDTが「バイト順」の場合にのみ可能です。 「バイト順」とは、UDTのバイナリ表現(UDTで定義可能)が適切な順序で自然にソートされることを意味します。パディングされる固定長セグメントを持つVARCHAR(50)列について、上記のアプローチと同様にバイナリ表現が処理されると仮定すると、条件を満たすことになります。または、バイナリ表現が適切な方法で自然に順序付けられるようにするのが容易でない場合は、適切に順序付けされる値を出力するUDTのメソッドまたはプロパティを公開してから、PERSISTED
そのメソッドまたはプロパティの計算列。メソッドは確定的で、_IsDeterministic = true
_としてマークされている必要があります。
このアプローチの利点は次のとおりです。
Parse
メソッドが_P7B18
_値を受け取り、それを変換すると仮定すると、単純に値を_P7B18
_として自然に挿入できるはずです。また、UDTで暗黙的な変換方法を設定すると、WHERE条件でも単純にP7B18`を使用できるようになります。このアプローチの結果は次のとおりです。
PERSISTED
計算列を使用する場合、プロパティまたはメソッドによって返される表現を取得します。元の_P7B18
_値が必要な場合は、その表現を返すようにコーディングされているUDTのメソッドまたはプロパティを呼び出す必要があります。とにかくToString
メソッドをオーバーライドする必要があるので、これはこれを提供するための良い候補です。バイナリ表現に変更を加えるのがどれほど簡単/難しいかは、(少なくとも今のところ、私はこの部分をテストしていないので私には)はっきりしていません。格納されているソート可能な表現を変更するには、フィールドをドロップして再度追加する必要がある場合があります。また、UDTを含むアセンブリの削除は、どちらの方法で使用しても失敗するため、このUDT以外にアセンブリに何もないことを確認する必要があります。 _ALTER Assembly
_を使用して定義を置き換えることができますが、これにはいくつかの制限があります。
一方、VARCHAR()
フィールドはアルゴリズムから切り離されたデータであるため、列の更新のみが必要です。また、数千万行(またはそれ以上)の行がある場合は、バッチアプローチで実行できます。
[〜#〜] icu [〜#〜] ライブラリを実装すると、実際にこの英数字の並べ替えが可能になります。このライブラリは非常に機能的ですが、C/C++とJavaの2つの言語しかありません。つまり、Visual C++で機能させるために微調整を行う必要があるか、Javaコードを [〜を使用してMSILに変換できる可能性があります。 #〜] ikvm [〜#〜] 。そのサイトには、マネージコードでアクセスできるCOMインターフェイスを提供する.NETサイドプロジェクトが1つまたは2つリンクされていますが、しばらく更新されていないと思いますそして、私はそれらを試していません。ここでの最善策は、並べ替えキーを生成する目的でアプリレイヤーでこれを処理することです。並べ替えキーは、新しい並べ替え列に保存されます。
これは最も実用的なアプローチではないかもしれません。しかし、そのような能力が存在することはまだ非常にクールです。次の回答で、この例の詳細なウォークスルーを提供しました。
次の文字列を1、2、3、6、10、10A、10B、11の順序で並べ替える照合順序はありますか?
しかし、その質問で扱われているパターンは少し単純です。この質問で扱われているパターンのタイプも機能することを示す例については、次のページにアクセスしてください。
[設定]で、[数値]オプションを[オン]に設定します。その他のオプションはすべて[デフォルト]に設定する必要があります。次に、[並べ替え]ボタンの右側で、[差分強度]のオプションをオフにし、[並べ替えキー]のオプションをオンにします。次に、「入力」テキスト領域のアイテムのリストを次のリストに置き換えます。
_P12B22
P7B18
P12B3
as456456hgjg6786867
P7Bb19
P7BA19
P7BB19
P007B18
P7Bb20
P7Bb19z23
_
「ソート」ボタンをクリックします。 「出力」テキスト領域には次のように表示されます。
_as456456hgjg6786867
29 4D 0F 7A EA C8 37 35 3B 35 0F 84 17 A7 0F 93 90 , 0D , , 0D .
P7B18
47 0F 09 2B 0F 14 , 08 , FD F1 , DC C5 DC 05 .
P007B18
47 0F 09 2B 0F 14 , 08 , FD F1 , DC C5 DC 05 .
P7BA19
47 0F 09 2B 29 0F 15 , 09 , FD FF 10 , DC C5 DC DC 05 .
P7Bb19
47 0F 09 2B 2B 0F 15 , 09 , FD F2 , DC C5 DC 06 .
P7BB19
47 0F 09 2B 2B 0F 15 , 09 , FD FF 10 , DC C5 DC DC 05 .
P7Bb19z23
47 0F 09 2B 2B 0F 15 5B 0F 19 , 0B , FD F4 , DC C5 DC 08 .
P7Bb20
47 0F 09 2B 2B 0F 16 , 09 , FD F2 , DC C5 DC 06 .
P12B3
47 0F 0E 2B 0F 05 , 08 , FD F1 , DC C5 DC 05 .
P12B22
47 0F 0E 2B 0F 18 , 08 , FD F1 , DC C5 DC 05 .
_
ソートキーは、コンマで区切られた複数のフィールドの構造であることに注意してください。各フィールドは個別に並べ替える必要があるため、SQL Serverでこれを実装する必要がある場合は、別の小さな問題を解決する必要があります。
** ユーザー定義関数の使用に関するパフォーマンスについて懸念がある場合は、提案されたアプローチがそれらの使用を最小限にすることに注意してください。実際、正規化された値を保存する主な理由は、各クエリの各行ごとにUDFを呼び出さないようにするためでした。主なアプローチでは、UDFを使用してSortColumn
の値を設定します。これは、トリガーを介してINSERT
およびUPDATE
に対してのみ実行されます。値の選択は、挿入および更新よりもはるかに一般的であり、一部の値は決して更新されません。 SELECT
句の範囲フィルターにSortColumn
を使用する各WHERE
クエリごとに、UDFは、range_startおよびrange_endの値ごとに1回だけ必要です。正規化された値; UDFは行ごととは呼ばれません。
UDTに関しては、実際の使用方法はスカラーUDFと同じです。つまり、挿入および更新では、値を設定するために各行ごとに1回正規化メソッドが呼び出されます。次に、正規化メソッドは、範囲フィルターの各range_startおよびrange_valueごとに、クエリごとに1回呼び出されますが、行ごとには呼び出されません。
SQLCLR UDFで正規化を完全に処理することに有利な点は、データアクセスを行わず、確定的である場合、それが_IsDeterministic = true
_としてマークされている場合、並列プランに参加できることです(これは、 INSERT
およびUPDATE
操作)のに対し、T-SQL UDFは並列プランの使用を防止します。