Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como alterar as informações desta tabela em um formulário fácil de usar?


Por curiosidade (um pouco mórbida), tentei encontrar um meio de transformar os dados de entrada exatos que você forneceu.

Muito melhor, é claro, seria estruturar adequadamente os dados originais. Com um sistema legado, isso pode não ser possível, mas um processo ETL pode ser criado para trazer essas informações para um local intermediário, de modo que uma consulta feia como essa não precise ser executada em tempo real.

Exemplo nº 1


Este exemplo assume que todos os IDs são consistentes e sequenciais (caso contrário, um ROW_NUMBER() adicional coluna ou uma nova coluna de identidade precisaria ser usada para garantir operações de resto corretas no 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

Exemplo nº 2:sem dependência de IDs sequenciais


Este é um exemplo mais prático porque os valores reais de ID não importam, apenas a sequência de linhas.
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;

Exemplo nº 3:Cursor


Novamente, seria melhor evitar executar uma consulta como essa em tempo real e usar um processo ETL transacional agendado. Na minha experiência, dados semiestruturados como esse são propensos a anomalias.

Enquanto os exemplos nº 1 e nº 2 (e as soluções fornecidas por outros) demonstram maneiras inteligentes de trabalhar com os dados, uma maneira mais prática de transformar esses dados seria um cursor. Por quê? ele pode realmente ter um desempenho melhor (sem consultas aninhadas, recursão, pivô ou numeração de linhas) e, mesmo que seja mais lento, oferece oportunidades muito melhores para tratamento de erros.
-- 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;

Desempenho


Eu criei dados de origem de amostra de 100 mil linhas e os três exemplos mencionados acima parecem aproximadamente equivalentes para transformar dados.

Criei um milhão de linhas de dados de origem e uma consulta semelhante à seguinte oferece excelente desempenho para selecionar um subconjunto de linhas (como seria usado em uma grade em uma página da Web ou em um relatório).
-- 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;

Estou vendo tempos de execução de 4-10ms (i7-3960x) em um conjunto de um milhão de registros.