自然の(人アルファ-numeric)とみなすソートマイクロソフトマイクロソフトSQL2005年

StackOverflow https://stackoverflow.com/questions/34509

質問

いデータベースにしたDB側にページング.これは迅速に復のページの50列から数百万のデータを分。

ユーザーを定義することができ独自の並べ替え、基本的に自由にカラムソートができます。カラムのダイナミック-一部の数値は、一日一部です。

を並べ替えとして予想されるなど、ダムです。もうダムうコンピュータがfrustratesます。

例えば、分別による文字列を記録idのようなもの:

rec1
rec10
rec14
rec2
rec20
rec3
rec4

...などです。

したいことを考慮のう:

rec1
rec2
rec3
rec4
rec10
rec14
rec20

私の制御ができないので、入力(そうでないだけでフォーマットの先000s)やまだひさしをはじめとする依存単一のフォーマットの一部のような"{アルファコード}-{部コード}-{rec id}".

しかしながら、その方法このためにクライアントまで、フルのC#ることができていないプルダウンのすべての記録を整理し、そのために遅くなります。

を使うための早期適用し、自然をうまさにSqlサーバー?


今用:

ROW_NUMBER() over (order by {field name} asc)

しいページングしたことがありました。

当社ブログにアクセスいただきトリガーがいない.すべての入力がparametrisedのようにも変更することができませんのフォーマットの場合に入力された"rec2"と"rec10"に期待して返されるということで、自然です。


して有効なユーザー入力が異なる形式とは異なる。

一つの可能性が生じることrec1,rec2,rec3,...rec100,rec101

がん:grp1rec1,grp1rec2,...grp20rec300,grp20rec301

がんを制御できない入力を意味しできませんので力ユーザーに変更できるものとし、変更された規格-そのような値grp1rec1やまだひさしをはじめとする走ってgrp01rec001どう変化するものを使用のためのルックアップおよびリンクが外部システム。

これらのフォーマット変多いが、多くの混合物を見学しました。

ソートこれらのクライアントまで、フルのC#やすいなどの休憩が入 { "grp", 20, "rec", 301 } とと比較すると配列の値をとっています。

しかしある数百万の記録のデータはページごとに区切ったもののように行われるSQLサーバーです。

SQLサーバなどによる価値のない比較-クライアントまで、フルのC#Iの値を比較し、アプリケーションが必要であるという論理(非常に速く)が単一の値でを一貫してひとつに違いありません。

@メビウス-お答えされるようになるなという実感が醜妥協を追加するソートキーのためのすべてのこれらのテキスト値です。

役に立ちましたか?

解決

のSQLベースのソリューションから休み時のデータが複雑など以上の一つ又は二つの番号です。当初は私の実施NaturalSort機能は、T-SQLという私の要件などを取り扱って任意の数の数字以内の文字列)が、全員がパフォーマンス 方法 も遅くなります。

作り上げている、と感じましたスカラー CLR機能にはC#のための自然種としてもunoptimizedコードの性能を呼び出していることからSQLサーバーはblindinglyます。しているため、以下のような特徴:

  • ソートします最初の1,000字以内で正常に簡単に変更によりコードされたパラメータ)
  • 適切な部で"年"、小数部で"、123.333が前123.45
  • 上記は、となっていない可能性がうようなIPアドレスを正しくご希望の場合は異なる挙動を修正コード
  • 対応ソート文字列と任意の数の数字以内で
  • 正しくソート番号25桁の数字(簡単に変更によりコードされたパラメータ)

このコードはこちら:

using System;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;

public class UDF
{
    [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic=true)]
    public static SqlString Naturalize(string val)
    {
        if (String.IsNullOrEmpty(val))
            return val;

        while(val.Contains("  "))
            val = val.Replace("  ", " ");

        const int maxLength = 1000;
        const int padLength = 25;

        bool inNumber = false;
        bool isDecimal = false;
        int numStart = 0;
        int numLength = 0;
        int length = val.Length < maxLength ? val.Length : maxLength;

        //TODO: optimize this so that we exit for loop once sb.ToString() >= maxLength
        var sb = new StringBuilder();
        for (var i = 0; i < length; i++)
        {
            int charCode = (int)val[i];
            if (charCode >= 48 && charCode <= 57)
            {
                if (!inNumber)
                {
                    numStart = i;
                    numLength = 1;
                    inNumber = true;
                    continue;
                }
                numLength++;
                continue;
            }
            if (inNumber)
            {
                sb.Append(PadNumber(val.Substring(numStart, numLength), isDecimal, padLength));
                inNumber = false;
            }
            isDecimal = (charCode == 46);
            sb.Append(val[i]);
        }
        if (inNumber)
            sb.Append(PadNumber(val.Substring(numStart, numLength), isDecimal, padLength));

        var ret = sb.ToString();
        if (ret.Length > maxLength)
            return ret.Substring(0, maxLength);

        return ret;
    }

    static string PadNumber(string num, bool isDecimal, int padLength)
    {
        return isDecimal ? num.PadRight(padLength, '0') : num.PadLeft(padLength, '0');
    }
}

登録はこう呼んでいからSQLサーバーに以下のコマンドを実行しクエリの解析:

CREATE ASSEMBLY SqlServerClr FROM 'SqlServerClr.dll' --put the full path to DLL here
go
CREATE FUNCTION Naturalize(@val as nvarchar(max)) RETURNS nvarchar(1000) 
EXTERNAL NAME SqlServerClr.UDF.Naturalize
go

その後しますが、使用することは可能ではこのように:

select *
from MyTable
order by dbo.Naturalize(MyTextField)

注意:するにSQLサーバーの 実行のユーザコードできます。NETフレームワークが無効になっています。有効"clr有効"と設定オプションです。, に従って処置してください こちらの すことができるようにする.ただくにはセキュリティに影響を及ただきます。ない場合にデータベース管理者のいるご相談管理する前に変更、サーバの設定をします。

注2:このコードは適切な支援の国際化(例えば、想定した小数点以下の桁数のマーカー"です。", は最適化されます。いくつでも大歓迎!

編集: 名称を"機能 Naturalize の代わりに NaturalSort, ていませんので、実際です。

他のヒント

order by LEN(value), value

完璧なものが作品も多くあります。

そうすることを義務付けられているものだったのでとてもいなかったとしたら、受け入れの答えです。

私は使用方法に類似す:

SELECT [Column] FROM [Table]
ORDER BY RIGHT(REPLICATE('0', 1000) + LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX)))), 1000)

ただ、今回は問題はご列なキャストへのVARCHAR(MAX)の場合、またはLEN([Column])>1000となりますので、御了承くださいが変更になる1000を何かしたい場合は、利用することができ荒れたアで作られています。

またこれはパフォーマンスが悪いより通常の注文の"カラム"が、いい結果を求めのOP.

編集:だ付けをより明確にするため、このまま動作しない場合は小数点の値としての 1, 1.151.5,(もうとして {1, 1.5, 1.15} というのOPですが容易に行えるようになった。:

SELECT [Column] FROM [Table]
ORDER BY REPLACE(RIGHT(REPLICATE('0', 1000) + LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX)))) + REPLICATE('0', 100 - CHARINDEX('.', REVERSE(LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX))))), 1)), 1000), '.', '0')

結果: {1, 1.15, 1.5}

ものすべてすべてのSQL.このことはうIPアドレスが間違えやすい箇所にも特定の番号の組み合わせに対してシンプルなテキスト+番号です。

RedFilterの回答 は合理的なサイズのデータセットが割り出する必要はありませんしたい場合は、インデックスの複数の工夫が必要です。

まず、マークとしての機能だけ電源をオンにする"というデータアクセスが決定論的および精密:

[SqlFunction(DataAccess = DataAccessKind.None,
                          SystemDataAccess = SystemDataAccessKind.None,
                          IsDeterministic = true, IsPrecise = true)]

次に、指定して900バイトに制限のインデックスキーサイズで、場合には帰化した値は、値を指数でなければなりません450文字の長さです。指定されたインデックスを含む複数の戻り値をもたなければな小さくなります。二つの変化:

CREATE FUNCTION Naturalize(@str AS nvarchar(max)) RETURNS nvarchar(450)
    EXTERNAL NAME ClrExtensions.Util.Naturalize

のC#コード:

const int maxLength = 450;

最後に、追加する必要があり、計算した列のテーブルで残すかを指定できないことを証明する Naturalize は決定性と正確に、帰化した値が実際に格納されたテーブルが維持され自動的に:

ALTER TABLE YourTable ADD nameNaturalized AS dbo.Naturalize(name) PERSISTED

では、今までの作のインデックス

CREATE INDEX idx_YourTable_n ON YourTable (nameNaturalized)

私もカップルでの変更RedFilterのコード:使字機能再構成に成功しました。を取り入れ、重複したスペースの除去のメール終了後の結果には限設定の最大長な部分文字列。この結果:

using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;

public static class Util
{
    [SqlFunction(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
    public static SqlString Naturalize(string str)
    {
        if (string.IsNullOrEmpty(str))
            return str;

        const int maxLength = 450;
        const int padLength = 15;

        bool isDecimal = false;
        bool wasSpace = false;
        int numStart = 0;
        int numLength = 0;

        var sb = new StringBuilder();
        for (var i = 0; i < str.Length; i++)
        {
            char c = str[i];
            if (c >= '0' && c <= '9')
            {
                if (numLength == 0)
                    numStart = i;
                numLength++;
            }
            else
            {
                if (numLength > 0)
                {
                    sb.Append(pad(str.Substring(numStart, numLength), isDecimal, padLength));
                    numLength = 0;
                }
                if (c != ' ' || !wasSpace)
                    sb.Append(c);
                isDecimal = c == '.';
                if (sb.Length > maxLength)
                    break;
            }
            wasSpace = c == ' ';
        }
        if (numLength > 0)
            sb.Append(pad(str.Substring(numStart, numLength), isDecimal, padLength));

        if (sb.Length > maxLength)
            sb.Length = maxLength;
        return sb.ToString();
    }

    private static string pad(string num, bool isDecimal, int padLength)
    {
        return isDecimal ? num.PadRight(padLength, '0') : num.PadLeft(padLength, '0');
    }
}

ごきげんよう、トメ子です、少し古いのでこの点、私の検索のためのより良い解決いたようで、私はこの質問です。を使っている機能めます。での動作を目的と分別の記録できる混合アルファ値('1',の"ゆるっと"したアイテム10','2'など)

CREATE FUNCTION [dbo].[fnMixSort]
(
    @ColValue NVARCHAR(255)
)
RETURNS NVARCHAR(1000)
AS

BEGIN
    DECLARE @p1 NVARCHAR(255),
        @p2 NVARCHAR(255),
        @p3 NVARCHAR(255),
        @p4 NVARCHAR(255),
        @Index TINYINT

    IF @ColValue LIKE '[a-z]%'
        SELECT  @Index = PATINDEX('%[0-9]%', @ColValue),
            @p1 = LEFT(CASE WHEN @Index = 0 THEN @ColValue ELSE LEFT(@ColValue, @Index - 1) END + REPLICATE(' ', 255), 255),
            @ColValue = CASE WHEN @Index = 0 THEN '' ELSE SUBSTRING(@ColValue, @Index, 255) END
    ELSE
        SELECT  @p1 = REPLICATE(' ', 255)

    SELECT  @Index = PATINDEX('%[^0-9]%', @ColValue)

    IF @Index = 0
        SELECT  @p2 = RIGHT(REPLICATE(' ', 255) + @ColValue, 255),
            @ColValue = ''
    ELSE
        SELECT  @p2 = RIGHT(REPLICATE(' ', 255) + LEFT(@ColValue, @Index - 1), 255),
            @ColValue = SUBSTRING(@ColValue, @Index, 255)

    SELECT  @Index = PATINDEX('%[0-9,a-z]%', @ColValue)

    IF @Index = 0
        SELECT  @p3 = REPLICATE(' ', 255)
    ELSE
        SELECT  @p3 = LEFT(REPLICATE(' ', 255) + LEFT(@ColValue, @Index - 1), 255),
            @ColValue = SUBSTRING(@ColValue, @Index, 255)

    IF PATINDEX('%[^0-9]%', @ColValue) = 0
        SELECT  @p4 = RIGHT(REPLICATE(' ', 255) + @ColValue, 255)
    ELSE
        SELECT  @p4 = LEFT(@ColValue + REPLICATE(' ', 255), 255)

    RETURN  @p1 + @p2 + @p3 + @p4

END

呼び出して、

select item_name from my_table order by fnMixSort(item_name)

すべてのトリプルの処理時間のための簡単なデータの読み込みできない場合に最適な解決策です。

この溶液に記述のためのSQL2000年.できるでしょ改善のための新しいSQLできます。

/**
 * Returns a string formatted for natural sorting. This function is very useful when having to sort alpha-numeric strings.
 *
 * @author Alexandre Potvin Latreille (plalx)
 * @param {nvarchar(4000)} string The formatted string.
 * @param {int} numberLength The length each number should have (including padding). This should be the length of the longest number. Defaults to 10.
 * @param {char(50)} sameOrderChars A list of characters that should have the same order. Ex: '.-/'. Defaults to empty string.
 *
 * @return {nvarchar(4000)} A string for natural sorting.
 * Example of use: 
 * 
 *      SELECT Name FROM TableA ORDER BY Name
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                        ID  Name
 *  1.  A1.                         1.  A1-1.       
 *  2.  A1-1.                       2.  A1.
 *  3.  R1             -->          3.  R1
 *  4.  R11                         4.  R11
 *  5.  R2                          5.  R2
 *
 *  
 *  As we can see, humans would expect A1., A1-1., R1, R2, R11 but that's not how SQL is sorting it.
 *  We can use this function to fix this.
 *
 *      SELECT Name FROM TableA ORDER BY dbo.udf_NaturalSortFormat(Name, default, '.-')
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                        ID  Name
 *  1.  A1.                         1.  A1.     
 *  2.  A1-1.                       2.  A1-1.
 *  3.  R1              -->         3.  R1
 *  4.  R11                         4.  R2
 *  5.  R2                          5.  R11
 */
ALTER FUNCTION [dbo].[udf_NaturalSortFormat](
    @string nvarchar(4000),
    @numberLength int = 10,
    @sameOrderChars char(50) = ''
)
RETURNS varchar(4000)
AS
BEGIN
    DECLARE @sortString varchar(4000),
        @numStartIndex int,
        @numEndIndex int,
        @padLength int,
        @totalPadLength int,
        @i int,
        @sameOrderCharsLen int;

    SELECT 
        @totalPadLength = 0,
        @string = RTRIM(LTRIM(@string)),
        @sortString = @string,
        @numStartIndex = PATINDEX('%[0-9]%', @string),
        @numEndIndex = 0,
        @i = 1,
        @sameOrderCharsLen = LEN(@sameOrderChars);

    -- Replace all char that have the same order by a space.
    WHILE (@i <= @sameOrderCharsLen)
    BEGIN
        SET @sortString = REPLACE(@sortString, SUBSTRING(@sameOrderChars, @i, 1), ' ');
        SET @i = @i + 1;
    END

    -- Pad numbers with zeros.
    WHILE (@numStartIndex <> 0)
    BEGIN
        SET @numStartIndex = @numStartIndex + @numEndIndex;
        SET @numEndIndex = @numStartIndex;

        WHILE(PATINDEX('[0-9]', SUBSTRING(@string, @numEndIndex, 1)) = 1)
        BEGIN
            SET @numEndIndex = @numEndIndex + 1;
        END

        SET @numEndIndex = @numEndIndex - 1;

        SET @padLength = @numberLength - (@numEndIndex + 1 - @numStartIndex);

        IF @padLength < 0
        BEGIN
            SET @padLength = 0;
        END

        SET @sortString = STUFF(
            @sortString,
            @numStartIndex + @totalPadLength,
            0,
            REPLICATE('0', @padLength)
        );

        SET @totalPadLength = @totalPadLength + @padLength;
        SET @numStartIndex = PATINDEX('%[0-9]%', RIGHT(@string, LEN(@string) - @numEndIndex));
    END

    RETURN @sortString;
END

ここではその他のソリューションにい:http://www.dreamchain.com/sql-and-alpha-numeric-sort-order/

なMicrosoft SQLかった場合はここで探していましたソリューションのためのPostgresと思いを追加することを待ち望んでいます。

以下の varchar データ:

BR1
BR2
External Location
IR1
IR2
IR3
IR4
IR5
IR6
IR7
IR8
IR9
IR10
IR11
IR12
IR13
IR14
IR16
IR17
IR15
VCR

このた私:

ORDER BY substring(fieldName, 1, 1), LEN(fieldName)

ばったりの荷重のデータはDBからソアプリケーションしていらっしゃることだと思いますね失望することはできませアプローチで、そのプログラムのDBです。時にサーバーを並べ替えと計算の"知覚"注文いたりしているのと同じようにい--。

たすことを示唆して追加の追加カラムのpreprocessedソート可能文字列をC#の方法の場合、データが挿入されます。すがされている性能試験の併記を固定幅の範囲は、例えば、"xyz1"思い出"xyz00000001".それを使用できる通常のSQLサーバーです。

のリスクのtooting自分のホーンってCodeProject条の実施に問題としてみたのCodingHorror。お気軽に 盗からコード.

いただきました、読記事のどこかなどの話題です。のポイント:が必要となりますの整数値を並べ替えデータなどをもとに、"rec'文字列に属するポートします。きの情報の分野で、アルゴリズムを並べ替えアルゴリズム(別売)を示す文字列構成によるα+num.を使用してくださ計算列の構文字列、またはビュー。希望します

利用できる次のコードの問題解決のためには:

Select *, 
    substring(Cote,1,len(Cote) - Len(RIGHT(Cote, LEN(Cote) - PATINDEX('%[0-9]%', Cote)+1)))alpha,
    CAST(RIGHT(Cote, LEN(Cote) - PATINDEX('%[0-9]%', Cote)+1) AS INT)intv 
FROM Documents 
   left outer join Sites ON Sites.IDSite = Documents.IDSite 
Order BY alpha, intv

について rabihkahaleh@hotmail.com

でき並べ替え

ORDER BY 
cast (substring(name,(PATINDEX('%[0-9]%',name)),len(name))as int)

 ##

まだまだわからない(しょうが拙い英語)。

きみ:

ROW_NUMBER() OVER (ORDER BY dbo.human_sort(field_name) ASC)

がんの作業百万円で記録しています。

ることから利用トリガーする 充填 カラム ヒューマンバリュー.

また:

  • 内蔵T-SQLの機能をも とMicrosoftらの利用 .ネット機能です。
  • ヒューマンバリュー が一定ではありません点の算出ることがあり がクエリを運行しています。
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top