Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Как да промените информацията в тази таблица в лесен за използване формуляр?

От (леко болезнено) любопитство се опитах да измисля средство за трансформиране на точните входни данни, които сте предоставили.

Много по-добре, разбира се, би било правилно да се структурират оригиналните данни. С наследена система това може да не е възможно, но може да се създаде ETL процес, който да пренесе тази информация в междинно местоположение, така че да не е необходимо грозна заявка като тази да се изпълнява в реално време.

Пример #1

Този пример предполага, че всички идентификатори са последователни и последователни (в противен случай допълнителен ROW_NUMBER() колона или нова колона за самоличност ще трябва да се използва, за да се гарантират правилни остатъчни операции върху 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

Пример #2:Без зависимост от последователни идентификатори

Това е по-практичен пример, тъй като действителните стойности на ID нямат значение, а само последователността на редовете.

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;

Пример #3:Курсор

Отново би било най-добре да избягвате изпълнението на заявка като тази в реално време и да използвате планиран транзакционен ETL процес. Според моя опит полуструктурираните данни като тези са склонни към аномалии.

Докато примери #1 и #2 (и решенията, предоставени от други) демонстрират умни начини за работа с данните, по-практичен начин за трансформиране на тези данни би бил курсор. Защо? всъщност може да работи по-добре (без вложени заявки, рекурсия, завъртане или номериране на редове) и дори да е по-бавно, предоставя много по-добри възможности за обработка на грешки.

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

Ефективност

Създадох примерни изходни данни от 100K реда и трите гореспоменати примера изглеждат приблизително еквивалентни за трансформиране на данни.

Създадох милион реда от изходни данни и заявка, подобна на следната, дава отлична производителност за избиране на подмножество от редове (като например, което ще се използва в мрежа на уеб страница или отчет).

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

Виждам времена за изпълнение от 4-10 ms (i7-3960x) срещу набор от милион записа.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL:Реално транспониране

  2. Намерете дублиращи се записи в таблица с помощта на SQL Server

  3. Как да реферирам един CTE два пъти?

  4. По-висок резултат от заявката с ключовата дума DISTINCT?

  5. Най-лесният начин да намерите IsManager в SQL