web-dev-qa-db-ja.com

テーブルの値に基づいて列の値を並べ替える

データを取得しながら、列を昇順または降順に並べ替える方法を教えてください。

テーブルに主キー/一意キー制約のない以下のデータが含まれているとします。

 Col1 Col2 Col3 Col4 
 ---- ---- ---- ---- 
 BDCA 
 CYMT 
 3 5 2 4 
 5 2 10 7 

以下の形式で出力したい:VarcharかNumericかにかかわらず、各行データは昇順である必要があります。値セットのASCII値を取得することで実行できると思いますか?なぜかというと、達成可能ですか?

 ColA ColB ColC ColD 
 ---- ---- ---- ---- 
 ABCD 
 CMTY 
 2 3 4 5 
 2 5 7 10 

任意の数の列に対応できるソリューションを提供してください。

データを取得している間、すべての行を昇順で再配置する必要があります。列ではなく、行に基づいて、並べ替えなどを実行しようとしています。それは達成可能ですか?アマゾンの取材でした。私はピボットとアンピボットを試しましたが、これを達成できませんでした。

詳しくは:

  1. すべての列のデータ型はVARCHAR2
  2. 値の特定の順序/パターンはありません。
  3. データを取得する間、列名は同じである必要はありません。列名はエイリアスにすることも、好みに応じて取得することもできます。取得されるデータは、行ごとに昇順である必要があります。
  4. 主キー/一意キーの制約はありません。

そのような解決策が達成可能である場合、非効率的であるかどうかにかかわらず、私は理解したいと思います。 Oracle 11g以上を使用して回答を探しています。

3
user3688422

解決

select  ColA,ColB,ColC,ColD

from   (select  t.n,t.val

               ,row_number () over 
                (
                    partition by n 
                    order by     case when regexp_like(val,'^-?\d+$') then to_number(val) end  
                                ,val
                ) as rn

        from   (select      rownum as n
                           ,t.* 

                from        t
                ) t unpivot (val for col in (COL1,COL2,COL3,COL4)) t

        ) t pivot (min(val) for rn in ('1' as ColA,'2' as ColB,'3' as ColC,'4' as ColD))

order by n                        
;

このデモでは、数値は整数であると想定されています。

ウォークスルー

  • 各行に行番号を追加します。

    select      rownum as n
               ,t.* 
    
    from        t
    ;
    

    行番号は、ピボットされていない行がピボットされたときに後で使用される識別子として機能します。生成される出力は次のとおりです。

    +---+------+------+------+------+
    | N | COL1 | COL2 | COL3 | COL4 |
    +---+------+------+------+------+
    | 1 | B    | D    | C    | A    |
    +---+------+------+------+------+
    | 2 | C    | Y    | M    | T    |
    +---+------+------+------+------+
    | 3 | 3    | 5    | 2    | 4    |
    +---+------+------+------+------+
    | 4 | 5    | 2    | 10   | 7    |
    +---+------+------+------+------+
    
  • 行をアンピボットします。

    unpivot (val for col in (COL1,COL2,COL3,COL4)) t
    
  • 該当する場合は、まず数値でソートされ、次に文字列としてアルファベット順にソートされた順序に従って、各行内の列をランク付けします。

    row_number () over 
    (
        partition by n 
        order by     case when regexp_like(val,'^-?\d+$') then to_number(val) end  
                    ,val
    ) as rn
    

    数値アイテムの場合、最初のソート基準は対応する数値に評価されるため、アイテムの順序が決まります。文字列アイテムの場合、最初の項はnullになるため、順序は2番目のソート基準によって決定されます。

    アンピボットしてランク付けした後の例で得られる出力は次のようになります。

    +---+-----+----+
    | N | VAL | RN |
    +---+-----+----+
    | 1 | A   | 1  |
    +---+-----+----+
    | 1 | B   | 2  |
    +---+-----+----+
    | 1 | C   | 3  |
    +---+-----+----+
    | 1 | D   | 4  |
    +---+-----+----+
    | 2 | C   | 1  |
    +---+-----+----+
    | 2 | M   | 2  |
    +---+-----+----+
    | 2 | T   | 3  |
    +---+-----+----+
    | 2 | Y   | 4  |
    +---+-----+----+
    | 3 | 2   | 1  |
    +---+-----+----+
    | 3 | 3   | 2  |
    +---+-----+----+
    | 3 | 4   | 3  |
    +---+-----+----+
    | 3 | 5   | 4  |
    +---+-----+----+
    | 4 | 2   | 1  |
    +---+-----+----+
    | 4 | 5   | 2  |
    +---+-----+----+
    | 4 | 7   | 3  |
    +---+-----+----+
    | 4 | 10  | 4  |
    +---+-----+----+ 
    
  • ランキングを新しい列名として使用し、行をピボットして(オプションで、上記のクエリでは、任意のパターンに従って別名を付けます。上記のクエリではColA、ColBなどとして)、最初に行識別子として行番号を割り当てます。

    結果は期待される出力になります:

    +------+------+------+------+
    | COLA | COLB | COLC | COLD |
    +------+------+------+------+
    | A    | B    | C    | D    |
    +------+------+------+------+
    | C    | M    | T    | Y    |
    +------+------+------+------+
    | 2    | 3    | 4    | 5    |
    +------+------+------+------+
    | 2    | 5    | 7    | 10   |
    +------+------+------+------+
    

ライブデモは Rextester で入手できます。