Aus (leicht morbider) Neugier habe ich versucht, eine Möglichkeit zu finden, die genauen Eingabedaten, die Sie bereitgestellt haben, umzuwandeln.
Viel besser wäre es natürlich, die Originaldaten richtig zu strukturieren. Mit einem Altsystem ist dies möglicherweise nicht möglich, aber es könnte ein ETL-Prozess erstellt werden, um diese Informationen an einen Zwischenspeicherort zu bringen, sodass eine hässliche Abfrage wie diese nicht in Echtzeit ausgeführt werden muss.
Beispiel #1
Dieses Beispiel geht davon aus, dass alle IDs konsistent und sequentiell sind (ansonsten eine zusätzliche ROW_NUMBER()
). -Spalte oder es müsste eine neue Identitätsspalte verwendet werden, um korrekte Restoperationen für die ID zu gewährleisten).
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
Beispiel #2:Keine Abhängigkeit von fortlaufenden IDs
Dies ist ein praktischeres Beispiel, da die tatsächlichen ID-Werte keine Rolle spielen, sondern nur die Zeilenfolge.
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;
Beispiel #3:Cursor
Auch hier wäre es am besten, eine Abfrage wie diese nicht in Echtzeit auszuführen und einen geplanten, transaktionalen ETL-Prozess zu verwenden. Meiner Erfahrung nach neigen halbstrukturierte Daten wie diese zu Anomalien.
Während die Beispiele Nr. 1 und Nr. 2 (und die von anderen bereitgestellten Lösungen) clevere Methoden zum Arbeiten mit den Daten demonstrieren, wäre eine praktischere Methode zum Transformieren dieser Daten ein Cursor. Wieso den? es kann tatsächlich eine bessere Leistung erbringen (keine verschachtelten Abfragen, Rekursion, Pivotierung oder Zeilennummerierung) und selbst wenn es langsamer ist, bietet es viel bessere Möglichkeiten zur Fehlerbehandlung.
-- 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;
Leistung
Ich habe Beispielquelldaten mit 100.000 Zeilen erstellt und die drei oben genannten Beispiele scheinen für die Transformation von Daten ungefähr gleichwertig zu sein.
Ich habe eine Million Zeilen mit Quelldaten erstellt, und eine Abfrage ähnlich der folgenden bietet eine hervorragende Leistung für die Auswahl einer Teilmenge von Zeilen (wie sie beispielsweise in einem Raster auf einer Webseite oder einem Bericht verwendet würden).
-- 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;
Ich sehe Ausführungszeiten von 4-10 ms (i7-3960x) bei einer Menge von einer Million Datensätzen.