sql >> Databáze >  >> RDS >> Sqlserver

Jak změnit informace v této tabulce do snadno použitelného formuláře?

Z (trochu morbidní) zvědavosti jsem se pokusil vymyslet způsob, jak transformovat přesná vstupní data, která jste poskytli.

Daleko lepší by samozřejmě bylo správně strukturovat původní data. Se starším systémem to nemusí být možné, ale mohl by být vytvořen proces ETL, který by tyto informace přenesl na mezilehlé místo, takže ošklivý dotaz, jako je tento, by nemusel být spouštěn v reálném čase.

Příklad č. 1

Tento příklad předpokládá, že všechna ID jsou konzistentní a sekvenční (jinak další ROW_NUMBER() Pro zajištění správných zbývajících operací na ID by bylo nutné použít nový sloupec identity).

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

Příklad č. 2:Žádná závislost na sekvenčních ID

Toto je praktičtější příklad, protože nezáleží na skutečných hodnotách ID, pouze na sekvenci řádků.

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;

Příklad č. 3:Kurzor

Opět by bylo nejlepší vyhnout se spouštění dotazu jako je tento v reálném čase a použít naplánovaný transakční proces ETL. Podle mých zkušeností jsou polostrukturovaná data, jako je tato, náchylná k anomáliím.

Zatímco příklady #1 a #2 (a řešení poskytnutá jinými) demonstrují chytré způsoby práce s daty, praktičtější způsob transformace těchto dat by byl kurzor. Proč? může ve skutečnosti fungovat lépe (žádné vnořené dotazy, rekurze, pivotování nebo číslování řádků) a i když je pomalejší, poskytuje mnohem lepší příležitosti pro zpracování chyb.

-- 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;

Výkon

Vytvořil jsem ukázková zdrojová data o 100 000 řádcích a tři výše uvedené příklady se zdají být pro transformaci dat zhruba ekvivalentní.

Vytvořil jsem milion řádků zdrojových dat a dotaz podobný následujícímu poskytuje vynikající výkon pro výběr podmnožiny řádků (jako by byly použity v mřížce na webové stránce nebo v sestavě).

-- 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;

Vidím doby provedení 4-10 ms (i7-3960x) proti sadě milionu záznamů.



  1. Mysql dotaz pomocí where a group by clause

  2. Jak uložit Euro Symbol do databáze mysql?

  3. Jak funguje Div() v PostgreSQL

  4. 6 způsobů, jak zřetězit řetězec a číslo na serveru SQL