Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Comment transformer les informations de ce tableau en un formulaire facile à utiliser ?

Par curiosité (un peu morbide), j'ai essayé de trouver un moyen de transformer les données d'entrée exactes que vous avez fournies.

Le mieux, bien sûr, serait de structurer correctement les données d'origine. Avec un ancien système, cela peut ne pas être possible, mais un processus ETL pourrait être créé pour amener ces informations dans un emplacement intermédiaire afin qu'une requête laide comme celle-ci n'ait pas besoin d'être exécutée en temps réel.

Exemple #1

Cet exemple suppose que tous les identifiants sont cohérents et séquentiels (sinon, un ROW_NUMBER() supplémentaire colonne ou une nouvelle colonne d'identité devrait être utilisée pour garantir des opérations de reste correctes sur l'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

Exemple n° 2 :aucune dépendance vis-à-vis des identifiants séquentiels

Il s'agit d'un exemple plus pratique car les valeurs d'ID réelles n'ont pas d'importance, seule la séquence de lignes.

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;

Exemple #3 :Curseur

Encore une fois, il serait préférable d'éviter d'exécuter une requête comme celle-ci en temps réel et d'utiliser un processus ETL transactionnel planifié. D'après mon expérience, des données semi-structurées comme celle-ci sont sujettes à des anomalies.

Alors que les exemples #1 et #2 (et les solutions fournies par d'autres) montrent des façons intelligentes de travailler avec les données, un moyen plus pratique de transformer ces données serait un curseur. Pourquoi? il peut en fait mieux fonctionner (pas de requêtes imbriquées, de récursivité, de pivotement ou de numérotation des lignes) et même s'il est plus lent, il offre de bien meilleures possibilités de gestion des erreurs.

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

Performances

J'ai créé des exemples de données source de 100 000 lignes et les trois exemples susmentionnés semblent à peu près équivalents pour la transformation des données.

J'ai créé un million de lignes de données source et une requête similaire à la suivante donne d'excellentes performances pour sélectionner un sous-ensemble de lignes (comme celles qui seraient utilisées dans une grille sur une page Web ou un rapport).

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

Je vois des temps d'exécution de 4 à 10 ms (i7-3960x) contre un ensemble d'un million d'enregistrements.