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

この表の情報を使いやすい形式に変更するにはどうすればよいですか?

    (やや病的な) 好奇心から、あなたが提供した正確な入力データを変換する手段を考え出そうとしました.

    もちろん、元のデータを適切に構造化する方がはるかに優れています。従来のシステムでは、これは不可能かもしれませんが、ETL プロセスを作成してこの情報を中間の場所に移動し、このような醜いクエリをリアルタイムで実行する必要がないようにすることができます。

    例 #1

    この例では、すべての ID が一貫しており、連続していることを前提としています (そうでない場合、追加の ROW_NUMBER() ID に対する正しい剰余演算を保証するには、列または新しい ID 列を使用する必要があります)。

    SELECT
        Name = REPLACE( Name, 'name: ', '' ),
        Age = REPLACE( Age, 'age: ', '' )
    FROM
    (
        SELECT
            Name = T2.Data,
            Age = T1.Data,
            RowNumber = ROW_NUMBER() OVER( ORDER BY T1.Id ASC )
    
        FROM @t T1 
            INNER JOIN @t T2 ON T1.id = T2.id +1 -- offset by one to combine two rows
        WHERE T1.id % 3 != 0 -- skip delimiter records
    ) Q1
     -- skip every other record (minus delimiters, which have already been stripped)
    WHERE RowNumber % 2 != 0
    

    例 #2:シーケンシャル ID に依存しない

    実際の ID 値は重要ではなく、行の順序のみが重要であるため、これはより実用的な例です。

    DECLARE @NumberedData TABLE( RowNumber INT, Data VARCHAR( 100 ) );
    
    INSERT @NumberedData( RowNumber, Data )
        SELECT 
            RowNumber = ROW_NUMBER() OVER( ORDER BY id ASC ),
            Data
        FROM @t;
    
    SELECT 
        Name = REPLACE( N2.Data, 'name: ', '' ),
        Age = REPLACE( N1.Data, 'age: ', '' ) 
    FROM @NumberedData N1 
        INNER JOIN @NumberedData N2 ON N1.RowNumber = N2.RowNumber + 1
    WHERE ( N1.RowNumber % 3 ) = 2;
    
    DELETE @NumberedData;
    

    例 #3:カーソル

    繰り返しになりますが、このようなクエリをリアルタイムで実行することは避け、スケジュールされたトランザクション ETL プロセスを使用することをお勧めします。私の経験では、このような半構造化データは異常になりやすいです。

    例 1 と 2 (および他の人が提供する解決策) は、データを操作する巧妙な方法を示していますが、このデータを変換するより実用的な方法はカーソルです。なんで?実際にはパフォーマンスが向上する可能性があり (ネストされたクエリ、再帰、ピボット、または行番号付けがない)、低速であってもエラー処理の機会が大幅に改善されます。

    -- this could be a table variable, temp table, or staging table
    DECLARE @Results TABLE ( Name VARCHAR( 100 ), Age INT );
    
    DECLARE @Index INT = 0, @Data VARCHAR( 100 ), @Name VARCHAR( 100 ), @Age INT;
    
    DECLARE Person_Cursor CURSOR FOR SELECT Data FROM @t;
    OPEN Person_Cursor;
    FETCH NEXT FROM Person_Cursor INTO @Data;
    
    WHILE( 1 = 1 )BEGIN -- busy loop so we can handle the iteration following completion
        IF( @Index = 2 ) BEGIN
            INSERT @Results( Name, Age ) VALUES( @Name, @Age );
            SET @Index = 0;
        END
        ELSE BEGIN
                -- optional: examine @Data for integrity
    
            IF( @Index = 0 ) SET @Name = REPLACE( @Data, 'name: ', '' );
            IF( @Index = 1 ) SET @Age = CAST( REPLACE( @Data, 'age: ', '' ) AS INT );
            SET @Index = @Index + 1;
        END
    
        -- optional: examine @Index to see that there are no superfluous trailing 
        -- rows or rows omitted at the end.
    
        IF( @@FETCH_STATUS != 0 ) BREAK;
        FETCH NEXT FROM Person_Cursor INTO @Data;
    END
    
    CLOSE Person_Cursor;
    DEALLOCATE Person_Cursor;
    

    パフォーマンス

    100K 行のサンプル ソース データを作成しましたが、前述の 3 つの例は、データの変換に関してはほぼ同等のようです。

    100 万行のソース データを作成しました。次のようなクエリを使用すると、行のサブセットを選択する際に優れたパフォーマンスが得られます (Web ページまたはレポートのグリッドで使用される場合など)。

    -- INT IDENTITY( 1, 1 ) numbers the rows for us
    DECLARE @NumberedData TABLE( RowNumber INT IDENTITY( 1, 1 ), Data VARCHAR( 100 ) );
    
    -- subset selection; ordering/filtering can be done here but it will need to preserve
    -- the original 3 rows-per-result structure and it will impact performance
    INSERT @NumberedData( Data )
        SELECT TOP 1000 Data FROM @t;
    
    SELECT
        N1.RowNumber,
        Name = REPLACE( N2.Data, 'name: ', '' ),
        Age = REPLACE( N1.Data, 'age: ', '' ) 
    FROM @NumberedData N1 
        INNER JOIN @NumberedData N2 ON N1.RowNumber = N2.RowNumber + 1
    WHERE ( N1.RowNumber % 3 ) = 2;
    
    DELETE @NumberedData;
    

    100 万件のレコード セットに対して 4 ~ 10 ミリ秒 (i7-3960x) の実行時間が見られます。



    1. クラスにメソッドが見つかりません

    2. PostgreSQLからデータベースにダンプをマージする方法は?

    3. MySQLmy.cnfファイル-先行するグループのないオプションが見つかりました

    4. MySqlConnection.StateChangeがフェッチされることはありません