Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Làm thế nào để thay đổi thông tin trong bảng này thành một biểu mẫu dễ sử dụng?

Vì tò mò (hơi bệnh hoạn), tôi đã cố gắng tìm ra một phương pháp chuyển đổi dữ liệu đầu vào chính xác mà bạn đã cung cấp.

Tất nhiên, tốt hơn nhiều là cấu trúc dữ liệu gốc đúng cách. Với một hệ thống kế thừa, điều này có thể không thực hiện được, nhưng một quy trình ETL có thể được tạo để đưa thông tin này vào một vị trí trung gian để một truy vấn xấu xí như thế này không cần phải chạy trong thời gian thực.

Ví dụ # 1

Ví dụ này giả định rằng tất cả các ID đều nhất quán và tuần tự (nếu không, một ROW_NUMBER() bổ sung cột hoặc cột nhận dạng mới sẽ cần được sử dụng để đảm bảo các hoạt động còn lại chính xác trên 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

Ví dụ # 2:Không phụ thuộc vào ID tuần tự

Đây là một ví dụ thực tế hơn vì các giá trị ID thực tế không quan trọng, chỉ là trình tự hàng.

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;

Ví dụ # 3:Con trỏ

Một lần nữa, tốt nhất là bạn nên tránh chạy một truy vấn như thế này trong thời gian thực và sử dụng quy trình ETL giao dịch theo lịch trình. Theo kinh nghiệm của tôi, dữ liệu bán cấu trúc như thế này dễ bị dị thường.

Trong khi các ví dụ # 1 và # 2 (và các giải pháp do những người khác cung cấp) thể hiện các cách làm việc thông minh với dữ liệu, thì một cách thực tế hơn để biến đổi dữ liệu này sẽ là con trỏ. Tại sao? nó thực sự có thể hoạt động tốt hơn (không có truy vấn lồng nhau, đệ quy, xoay vòng hoặc đánh số hàng) và ngay cả khi nó chậm hơn, nó cung cấp nhiều cơ hội tốt hơn để xử lý lỗi.

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

Hiệu suất

Tôi đã tạo dữ liệu nguồn mẫu gồm 100K hàng và ba ví dụ nói trên có vẻ gần tương đương để chuyển đổi dữ liệu.

Tôi đã tạo một triệu hàng dữ liệu nguồn và một truy vấn tương tự như sau mang lại hiệu suất tuyệt vời để chọn một tập hợp con các hàng (chẳng hạn như sẽ được sử dụng trong lưới trên trang web hoặc báo cáo).

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

Tôi thấy thời gian thực thi là 4-10ms (i7-3960x) so với tập hợp một triệu bản ghi.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thứ tự cài đặt thích hợp cho Visual Studio 2010 với SQL Server 2008 và Office 2007?

  2. các lựa chọn thay thế để THAY THẾ trên kiểu dữ liệu văn bản hoặc ntext

  3. Cách tạo bảng từ kết quả truy vấn chọn trong SQL Server 2008

  4. Biểu thức IN biến trong SQL

  5. Máy chủ Sql phải làm gì để tạo khóa đối chiếu từ một giá trị chuỗi