2009-10-21 7 views
5

Tôi có một vài bảng lớn (hàng 188m và 144m) tôi cần điền từ các khung nhìn, nhưng mỗi khung nhìn chứa một vài trăm triệu hàng (kết hợp với nhau dữ liệu mô hình giả theo dạng phẳng). Các phím trên mỗi bảng là hơn 50 byte tổng hợp của các cột. Nếu dữ liệu nằm trong các bảng, tôi luôn có thể nghĩ về việc sử dụng sp_rename để tạo một bảng mới khác, nhưng đó không thực sự là một lựa chọn.Hàng loạt cam kết về hoạt động INSERT lớn trong SQL nguyên gốc?

Nếu tôi thực hiện một thao tác INSERT duy nhất, quy trình sử dụng một lượng lớn không gian nhật ký giao dịch, điển hình là gửi nó lên và nhắc nhở một loạt các rắc rối với các DBA. (Và có, đây có thể là công việc mà các quản trị viên cần xử lý/thiết kế/kiến ​​trúc sư)

Tôi có thể sử dụng SSIS và truyền dữ liệu vào bảng đích với các lần commit hàng loạt (nhưng điều này yêu cầu dữ liệu phải được truyền qua mạng , vì chúng tôi không được phép chạy các gói SSIS trên máy chủ).

Bất kỳ điều nào khác ngoài việc chia quá trình thành nhiều thao tác INSERT bằng cách sử dụng một số loại khóa để phân phối các hàng thành các lô khác nhau và thực hiện một vòng lặp?

+1

Nếu phân vùng bảng là một tùy chọn, bạn có thể chia các giá trị chèn bằng giá trị phân vùng. Có thể làm cho nó nhanh hơn để kết hợp lại với nhau các tập con kết quả. –

Trả lời

4

Bạn có thể phân đoạn dữ liệu của mình và chèn dữ liệu vào vòng lặp con trỏ. Điều đó sẽ gần như giống như SSIS batchinserting. Nhưng chạy trên máy chủ của bạn.

create cursor .... 
select YEAR(DateCol), MONTH(DateCol) from whatever 

while .... 
    insert into yourtable(...) 
    select * from whatever 
    where YEAR(DateCol) = year and MONTH(DateCol) = month 
end 
+0

Đây là chiến lược cuối cùng tôi đã sử dụng. –

1

Bạn có thể sử dụng lệnh BCP để tải dữ liệu và sử dụng các thông số hàng loạt Kích

http://msdn.microsoft.com/en-us/library/ms162802.aspx

Hai quá trình bước

  • BCP OUT dữ liệu khỏi Chế độ xem vào tập tin văn bản
  • BCP IN dữ liệu từ tệp Văn bản vào Bảng có thông số kích thước hàng loạt
+0

13 giây! * lắc nắm tay nhỏ * –

+0

Làm thế nào bạn nhận được phần giây? Nó chỉ hiển thị cho tôi phút –

+1

@Raj: di chuột qua dấu thời gian tương đối để nhận được dấu thời gian thực tế với giây –

1

Điều này trông giống như một công việc cho ol tốt 'BCP.

2

Không có bụi bẩn, bạn biết điều đó.

Nếu không biết chi tiết cụ thể về lược đồ thực tế đang được chuyển, giải pháp chung sẽ chính xác như bạn mô tả: phân chia xử lý thành nhiều lần chèn và theo dõi (các) khóa. Đây là loại mã giả T-SQL:

create table currentKeys (table sysname not null primary key, key sql_variant not null); 
go 

declare @keysInserted table (key sql_variant); 
declare @key sql_variant; 
begin transaction 
do while (1=1) 
begin 
    select @key = key from currentKeys where table = '<target>'; 
    insert into <target> (...) 
    output inserted.key into @keysInserted (key) 
    select top (<batchsize>) ... from <source> 
    where key > @key 
    order by key; 

    if (0 = @@rowcount) 
     break; 

    update currentKeys 
    set key = (select max(key) from @keysInserted) 
    where table = '<target>'; 
    commit; 
    delete from @keysInserted; 
    set @key = null; 
    begin transaction; 
end 
commit 

Sẽ phức tạp hơn nếu bạn muốn cho phép các lô song song và phân chia khóa.

+3

Trên thực tế, mặt đất đạn bạc làm cho bụi pixie tuyệt vời –

+1

ty mr Kelley, tôi sẽ ghi nhớ điều đó trong dự án tiếp theo của mình;) –

5

Chế độ xem có BẤT K kind loại khóa nhận dạng/khóa ứng viên duy nhất nào không? Nếu vậy, bạn có thể chọn những hàng vào một bảng làm việc sử dụng:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView; 

(nếu nó có ý nghĩa, có thể đưa bảng này thành một cơ sở dữ liệu khác nhau, có lẽ với mô hình phục hồi SIMPLE, để ngăn chặn các hoạt động đăng nhập từ can thiệp với Điều này sẽ tạo ra ít hơn nhiều đăng nhập anyway, và bạn có thể giải phóng không gian trong cơ sở dữ liệu khác trước khi bạn tiếp tục, trong trường hợp vấn đề là bạn có đủ không gian đĩa xung quanh.)

Sau đó, bạn có thể làm một cái gì đó như thế này, chèn 10.000 hàng tại một thời điểm, và sao lưu các bản ghi ở giữa:

SET NOCOUNT ON; 

DECLARE 
    @batchsize INT, 
    @ctr INT, 
    @rc INT; 

SELECT 
    @batchsize = 10000, 
    @ctr = 0; 

WHILE 1 = 1 
BEGIN 
    WITH x AS 
    (
     SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column) 
     FROM dbo.temp 
    ) 
    INSERT dbo.PrimaryTable(a, b, c, etc.) 
     SELECT v.a, v.b, v.c, etc. 
     FROM x 
     INNER JOIN dbo.HugeView AS v 
     ON v.key_column = x.key_column 
     WHERE x.rn > @batchsize * @ctr 
     AND x.rn <= @batchsize * (@ctr + 1); 

    IF @@ROWCOUNT = 0 
     BREAK; 

    BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT; 

    SET @ctr = @ctr + 1; 
END 

Đó là tất cả ra khỏi đỉnh đầu của tôi, do đó, không cắt/dán/chạy, nhưng tôi nghĩ ý tưởng chung là có.

Lưu ý rằng nếu bạn đang sử dụng cơ sở dữ liệu thông thường và sao lưu nhật ký, có thể bạn sẽ muốn bắt đầu một lần nữa để bắt đầu chuỗi đăng nhập của mình.

+0

Thật buồn cười, bởi vì trước khi tôi xây dựng hầu hết hệ thống này trong máy chủ và vẫn đang sử dụng hộp của tôi để phát triển, tôi đã chia các loại bảng cho các mục tiêu phục hồi và đặt chúng trong các lược đồ riêng biệt với hy vọng rằng các DBA sẽ cung cấp cho tôi nhiều cơ sở dữ liệu, nhưng không phải ... –

+0

Và thật không may các phím là tất cả hơn 50 byte cột tổng hợp, hãy đoán đã đến lúc hoàn thành ... –

+1

@AaronBertrand Tôi biết đây là cũ, nhưng tôi đã sử dụng nó trong một thử nghiệm. Lý thuyết hoạt động tốt, bạn chỉ thiếu 'SET @ctr = @ctr + 1' trong vòng lặp để tăng bộ đếm số lô. Tôi hy vọng bạn không nhớ - Tôi đã chỉnh sửa nó trong (sau khi kiểm tra kết quả). – Bridge

2

Tôi biết đây là một chủ đề cũ, nhưng tôi đã thực hiện một phiên bản chung của giải pháp con trỏ Arthur:

--Split a batch up into chunks using a cursor. 
--This method can be used for most any large table with some modifications 
--It could also be refined further with an @Day variable (for example) 

DECLARE @Year INT 
DECLARE @Month INT 

DECLARE BatchingCursor CURSOR FOR 
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>) 
FROM <Sometable>; 


OPEN BatchingCursor; 
FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
WHILE @@FETCH_STATUS = 0 
BEGIN 

--All logic goes in here 
--Any select statements from <Sometable> need to be suffixed with: 
--WHERE Year(<SomeDateField>)[email protected] AND Month(<SomeDateField>)[email protected] 


    FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
END; 
CLOSE BatchingCursor; 
DEALLOCATE BatchingCursor; 
GO 

này đã giải quyết được vấn đề trên vô số bảng lớn của chúng tôi.