sql >> データベース >  >> RDS >> Sqlserver

SQL:文字と数字が混在する部分文字列による並べ替え

    これは実際には答えではなく、さまざまなアプローチの比較であるため、これを新しい答えとして配置します。

    結論:

    • XML を除いて、すべてのアプローチはほぼ直線的にスケーリングします
    • XML は行数が少ないと最速ですが、行数が多いと悪化します

    テスト シナリオを作成する

    CREATE TABLE #tbl (ID INT IDENTITY,sortColumn VARCHAR(100));
    INSERT INTO #tbl VALUES
     ('A-1')
    ,('A-10')
    ,('A-2')
    ,('A-3')
    ,('A-4')
    ,('A-5')
    ,('A-6')
    ,('A-7')
    ,('A-8')
    ,('A-9')
    ,('A-3a')
    ,('A-3b')
    ,('A-3c')
    ,('B-1')
    ,('B-10')
    ,('B-11')
    ,('B-12')
    ,('B-12a')
    ,('B-12b')
    ,('B-13')
    ,('B-2')
    ,('B-3')
    ,('B-4')
    ,('B-5')
    ,('B-6')
    ,('B-7')
    ,('B-8')
    ,('A-8a')
    ,('B-8')
    ,('B-9'); --30 rows
    GO 1000  -- x 1.000 = 30.000 rows
    

    マットのアプローチ (必要に応じてクリーンアップ)

    • 300万行で46秒
    • 300.000 行で 4.5 秒
    • 30,000 行で 1.3 秒
    • 3,000 行で 0.7 秒

    コード

    SELECT ID,sortColumn
    FROM
        #tbl
    ORDER BY
    LEFT(sortColumn,CHARINDEX('-',sortColumn) -1)
    ,CAST((CASE
        WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,3)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,3)
        WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,2)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,2)
        WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,1)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,1)
        ELSE NULL
    END) AS INT)
    ,RIGHT(sortColumn,
        LEN(sortColumn) - 
        LEN(LEFT(sortColumn,CHARINDEX('-',sortColumn) -1)) 
        - LEN(CASE
                WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,3)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,3)
                WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,2)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,2)
                WHEN ISNUMERIC(SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,1)) = 1 THEN SUBSTRING(sortColumn,CHARINDEX('-',sortColumn) + 1,1)
                ELSE NULL
        END)
        - 1 --the '-'
    ),ID;
    

    CROSS APPLY での段階的な計算 s、計算列での並べ替え

    • 300万行で44秒
    • 300.000 行で 4.4 秒
    • 30.000 行で 0.9 秒
    • 3,000 行で 0.3 秒

    コード

    SELECT ID,sortColumn
    FROM #tbl
    CROSS APPLY(SELECT CHARINDEX('-',sortColumn) AS posMinus) AS pos
    CROSS APPLY(SELECT SUBSTRING(sortColumn,1,posMinus-1) AS part1
                      ,SUBSTRING(sortColumn,posMinus+1,1000) AS part2
                ) AS parts
    CROSS APPLY(SELECT ISNUMERIC(part2) AS p2isnum) AS checknum
    CROSS APPLY(SELECT CASE WHEN p2isnum=1 THEN '' ELSE RIGHT(part2,1) END AS part3
                      ,CASE WHEN p2isnum=1 THEN part2 ELSE SUBSTRING(part2,1,LEN(part2)-1) END AS part2New
               ) AS partsNew
    ORDER BY part1,part2new,part3,ID;
    

    CROSS APPLY での段階的な計算 s、連結された埋め込み文字列でソート

    • 300万行で42秒
    • 300.000 行で 4.2 秒
    • 30.000 行で 0.7 秒
    • 3,000 行で 0.4 秒

    コード

    SELECT ID,sortColumn
    FROM #tbl
    CROSS APPLY(SELECT CHARINDEX('-',sortColumn) AS posMinus) AS pos
    CROSS APPLY(SELECT SUBSTRING(sortColumn,1,posMinus-1) AS part1
                      ,SUBSTRING(sortColumn,posMinus+1,1000) AS part2
                ) AS parts
    ORDER BY RIGHT('.....' + part1,5) + RIGHT('.....' + part2,5 - ISNUMERIC(RIGHT(part2,1)))
            ,ID;
    

    XML で分割し、連結された埋め込み文字列で並べ替える

    • 300万行で67秒
    • 300.000 行で 6.2 秒
    • 30.000 行で 0.7 秒
    • 3,000 行で 0.3 秒

    コード

    SELECT ID,sortColumn
    FROM
    (
        SELECT CAST('<r>' + REPLACE(sortColumn,'-','</r><r>') + '</r>' AS XML) AS SortColumnSplitted
              ,*
        FROM #tbl
    ) AS tbl
    ORDER BY RIGHT('.....' + SortColumnSplitted.value('r[1]','varchar(max)'),5) + RIGHT('.....' + SortColumnSplitted.value('r[2]','varchar(max)'),5 - ISNUMERIC(RIGHT(SortColumnSplitted.value('r[2]','varchar(max)'),1)))
            ,ID;
    


    1. Laravelはmysql_real_escape_string()を使用できません

    2. インメモリデータベースを使用したユニットテスト

    3. javascriptをmysqlデータベースに挿入する方法は?

    4. サブタイプのテーブル関係