Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak zmienić informacje w tej tabeli w łatwy w użyciu formularz?

Z (nieco chorobliwej) ciekawości próbowałem wymyślić sposób na przekształcenie dokładnych danych wejściowych, które podałeś.

O wiele lepiej byłoby oczywiście odpowiednio ustrukturyzować oryginalne dane. W przypadku starszego systemu może to nie być możliwe, ale można utworzyć proces ETL, który przenosi te informacje do pośredniej lokalizacji, aby takie brzydkie zapytanie nie musiało być uruchamiane w czasie rzeczywistym.

Przykład #1

W tym przykładzie założono, że wszystkie identyfikatory są spójne i sekwencyjne (w przeciwnym razie dodatkowy ROW_NUMBER() kolumna lub nowa kolumna tożsamości musiałaby zostać użyta, aby zagwarantować prawidłowe operacje na resztach na 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

Przykład nr 2:brak zależności od identyfikatorów sekwencyjnych

Jest to bardziej praktyczny przykład, ponieważ rzeczywiste wartości identyfikatora nie mają znaczenia, tylko kolejność wierszy.

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;

Przykład #3:Kursor

Ponownie, najlepiej byłoby unikać uruchamiania takiego zapytania w czasie rzeczywistym i korzystać z zaplanowanego, transakcyjnego procesu ETL. Z mojego doświadczenia wynika, że ​​takie częściowo ustrukturyzowane dane są podatne na anomalie.

Podczas gdy przykłady 1 i 2 (oraz rozwiązania dostarczone przez innych) pokazują sprytne sposoby pracy z danymi, bardziej praktycznym sposobem przekształcania tych danych byłby kursor. Czemu? może faktycznie działać lepiej (bez zapytań zagnieżdżonych, rekurencji, przestawiania lub numerowania wierszy), a nawet jeśli jest wolniejszy, zapewnia znacznie lepsze możliwości obsługi błędów.

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

Wydajność

Stworzyłem przykładowe dane źródłowe o wielkości 100 tys. wierszy, a trzy wyżej wymienione przykłady wydają się z grubsza równoważne do przekształcania danych.

Utworzyłem milion wierszy danych źródłowych, a zapytanie podobne do poniższego zapewnia doskonałą wydajność przy wybieraniu podzbioru wierszy (takich, jakie byłyby używane w siatce na stronie internetowej lub w raporcie).

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

Widzę czasy wykonania 4-10 ms (i7-3960x) na zestawie miliona rekordów.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. EXEC do użycia bazy danych

  2. Sprawdź, czy do tabeli odwołuje się klucz obcy w SQL Server za pomocą OBJECTPROPERTY()

  3. zapytanie sql o różnicę między bieżącym wierszem a poprzednim wierszem na podstawie daty i godziny

  4. Czy należy wykonywać wiele wywołań wstawiania lub przekazywać XML?

  5. SQL Server ODBC Driver nie zgłasza błędów