2009-05-14 25 views
8

Tôi đang viết một quy trình lưu trữ các hàng từ một bảng SQL Server dựa trên một cột ngày giờ. Tôi muốn di chuyển tất cả các hàng với một ngày trước X, nhưng vấn đề là có hàng triệu hàng cho mỗi ngày, do đó, thực hiện BEGIN GIAO DỊCH ... INSERT ... DELETE ... COMMIT cho mỗi ngày mất quá nhiều thời gian và khóa cơ sở dữ liệu cho những người dùng khác.Di chuyển dữ liệu SQL Server trong các khối giới hạn (1000 hàng)

Có cách nào để tôi có thể thực hiện trong các đoạn nhỏ hơn không? Có thể sử dụng ROWCOUNT hoặc một cái gì đó như thế?

tôi muốn ban đầu được coi là một cái gì đó như thế này:

SET ROWCOUNT 1000 

DECLARE @RowsLeft DATETIME 
DECLARE @ArchiveDate DATETIME 

SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate) 

WHILE @ROWSLEFT IS NOT NULL 
BEGIN 

    INSERT INTO EventsBackups 
    SELECT top 1000 * FROM Events 

    DELETE Events 

    SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate) 

END 

Nhưng sau đó tôi nhận ra rằng tôi không thể đảm bảo rằng các hàng tôi đang xóa là những người tôi chỉ sao lưu. Hay tôi có thể ...?

UPDATE: Một tùy chọn Tôi đã coi được thêm một bước:

  1. SELECT TOP 1000 hàng đáp ứng tiêu chí ngày tôi vào một bảng temp
  2. Bắt đầu giao dịch
  3. Chèn từ tạm thời bảng vào bảng lưu trữ
  4. Xóa khỏi bảng nguồn, tham gia bảng tạm thời trên mỗi cột
  5. Cam kết chuyển tiếp ction
  6. Lặp lại 1-5 cho đến khi không có hàng vẫn đáp ứng được các tiêu chí ngày

Có ai có ý tưởng về cách các chi phí của loạt bài này có thể so sánh với một số tùy chọn khác được thảo luận dưới đây?

CHI TIẾT: Tôi đang sử dụng SQL 2005, vì ai đó đã hỏi.

+0

mệnh đề OUTPUT và INTO là bạn của bạn, tra cứu hoặc xem câu trả lời của tôi ... –

Trả lời

16

chỉ cần chèn kết quả của DELETE:

WHILE 1=1 
BEGIN 

    WITH EventsTop1000 AS (
    SELECT TOP 1000 * 
     FROM Events 
     WHERE <yourconditionofchoice>) 
    DELETE EventsTop1000 
     OUTPUT DELETED.* 
     INTO EventsBackup; 

    IF (@@ROWCOUNT = 0) 
     BREAK; 
END 

Đây là nguyên tử và nhất quán.

+0

Đây phải là câu trả lời được chấp nhận. –

0

Làm thế nào về:

INSERT INTO EventsBackups 
SELECT TOP 1000 * FROM Events ORDER BY YourKeyField 

DELETE Events 
WHERE YourKeyField IN (SELECT TOP 1000 YourKeyField FROM Events ORDER BY YourKeyField) 
+0

Ngoài ra, đây là trường hợp hoàn hảo để phân vùng cửa sổ trượt, nếu bạn có thể sử dụng nó: http : //weblogs.sqlteam.com/dang/archive/2008/08/30/Sliding-Window-Table-Partitioning.aspx Đó là một chuyển đổi siêu dữ liệu, do đó, toàn bộ tải có thể được thực hiện sau vài giây. –

0

Làm thế nào về không làm điều đó tất cả cùng một lúc?

INSERT INTO EventsBackups 
SELECT * FROM Events WHERE date criteria 

Rồi sau đó,

DELETE FROM Events 
SELECT * FROM Events INNER JOIN EventsBackup on Events.ID = EventsBackup.ID 

hoặc tương đương.

Không có gì bạn nói cho đến nay cho thấy bạn cần một giao dịch.

+0

Đó là quá nhiều nguồn lực để làm một chèn lớn như thế trong một bảng rất tích cực. Nó cần phải được "chunked" để ngăn chặn chờ đợi tài nguyên lớn. –

+0

Nhưng đó là bảng Sao lưu sẽ bị khóa chứ không phải bảng Sự kiện. Vì vậy, khóa một vấn đề? Sau đó, bạn có thể thực hiện xóa trong các khối nếu chúng nằm trong bản sao lưu của bạn. –

+0

Tôi đang sử dụng giao dịch để tôi có thể khôi phục chèn nếu xóa không thành công. Tôi không muốn có bất kỳ bản ghi xuất hiện trong bảng lưu trữ vẫn còn trong bảng trực tiếp, vì điều đó có thể dẫn đến các bản sao sau này. Tôi thực sự đang cố gắng giải quyết quy trình lưu trữ nội bộ cực kỳ phức tạp của ứng dụng, điều này không bao giờ có ý nghĩa đối với nhiều dữ liệu như chúng tôi và tôi muốn tránh mọi thứ có thể phá vỡ nó. – SqlRyan

0

Bạn có chỉ mục trên trường ngày tháng không? Nếu bạn không có sql có thể bị buộc phải nâng cấp lên một khóa bảng sẽ khóa tất cả người dùng của bạn trong khi các lệnh lưu trữ của bạn chạy.

Tôi nghĩ bạn sẽ cần chỉ mục cho hoạt động này để hoạt động tốt! Đặt chỉ mục vào trường ngày của bạn và thử lại thao tác của bạn!

+0

Tôi đang sử dụng SQL 2005 và không có chỉ mục nào trên bảng, điều này làm cho các câu lệnh SELECT tốn kém để bắt đầu. – SqlRyan

0

Bạn có thể tạo bản sao Sự kiện hay không, di chuyển tất cả các hàng có ngày > = x vào đó, thả Sự kiện và đổi tên bản sao Sự kiện? Hoặc sao chép, cắt ngắn và sau đó sao chép lại? Nếu bạn có thể đủ khả năng một chút thời gian chết này có lẽ sẽ là cách tiếp cận nhanh nhất.

4

sử dụng một INSERT với một OUTPUT VÀO khoản để lưu trữ các ID của các hàng chèn, sau đó DELETE tham gia để bảng temp này để loại bỏ chỉ những ID

DECLARE @TempTable (YourKeyValue KeyDatatype not null) 

INSERT INTO EventsBackups 
    (columns1,column2, column3) 
    OUTPUT INSERTED.primaryKeyValue 
    INTO @TempTable 
    SELECT 
     top 1000 
     columns1,column2, column3 
     FROM Events 

DELETE Events 
    FROM Events 
     INNER JOIN @TempTable t ON Events.PrimaryKey=t.YourKeyValue 
+0

Tôi thích giải pháp này. Lưu ý rằng lần tham gia cuối cùng của bạn sẽ là: TRÊN Sự kiện.PrimaryKey = t.primaryKeyValue thay vì TRÊN Sự kiện.PrimaryKey = t.YourKeyValue Chỉ để giữ ví dụ nhất quán ;-) –

+0

@Aaron Alton, t.YourKeyValue đến từ @tempTable, mà tôi xác định trong mã của tôi, không có @TempTable .primaryKeyValue. Các OUTPUT INSERTED.primaryKeyValue nhu cầu thay đổi để được INSERTED.his giá trị quan trọng. –

+0

Tôi thực sự thích giải pháp này, ngoại trừ việc không có cột nào là chìa khóa. Có thể có các hàng lặp lại trong bảng có dấu thời gian giống nhau: ( Tôi thực sự thích điều này, tuy nhiên, nó đáng để nâng cấp. – SqlRyan

0

Dưới đây là những gì tôi đã kết thúc thực hiện:

SET @CleanseFilter = @startdate 
WHILE @CleanseFilter IS NOT NULL 
BEGIN 
    BEGIN TRANSACTION 

     INSERT INTO ArchiveDatabase.dbo.MyTable 
     SELECT * 
      FROM dbo.MyTable 
     WHERE startTime BETWEEN @startdate AND @CleanseFilter 

     DELETE dbo.MyTable 
     WHERE startTime BETWEEN @startdate AND @CleanseFilter 

    COMMIT TRANSACTION 

    SET @CleanseFilter = (SELECT MAX(starttime) 
       FROM (SELECT TOP 1000 
          starttime 
        FROM dbo.MyTable 
         WHERE startTime BETWEEN @startdate AND @enddate 
        ORDER BY starttime) a) 
END 

Tôi không kéo chính xác 1000, chỉ 1000 năm, vì vậy nó xử lý lặp lại trong cột thời gian một cách thích hợp (điều tôi lo lắng khi xem xét sử dụng ROWCOUNT). Vì thường xuyên lặp lại trong cột thời gian, tôi thấy nó thường xuyên di chuyển 1002 hoặc 1004 hàng/lặp lại, vì vậy tôi biết nó đang nhận được mọi thứ.

Tôi đang gửi câu trả lời này làm câu trả lời để có thể đánh giá câu trả lời dựa trên các giải pháp khác mà mọi người đã cung cấp. Hãy cho tôi biết nếu có điều gì đó rõ ràng là sai với phương pháp này. Cảm ơn sự giúp đỡ của bạn, mọi người, và tôi sẽ chấp nhận câu trả lời nào có nhiều phiếu bầu nhất trong vài ngày tới.

+0

nếu bạn không có khóa và không muốn thêm, hãy sử dụng câu trả lời của tôi, nhưng thay đổi nó . thực hiện việc xóa bằng OUTPUT INTO, chỉ cần capture tất cả các cột vào bảng tạm thời và sau đó chèn từ một bảng chọn temp đó. –

+0

Bạn cần phải rất cẩn thận với SQL bạn đã đăng. Nếu bạn không sử dụng cách ly giao dịch SERIALIZABLE, DELETE của bạn sẽ không được bảo đảm chỉ xóa các hàng mà SELECT đã chọn. Tra cứu các lần đọc không lặp lại được, và đọc ảo. Nếu bạn đi với SQL bạn đăng, cách duy nhất để SERIALIZABLE được đảm bảo bởi máy chủ SQL (không có chỉ mục trên cột ngày tháng) là khóa bảng, điều này sẽ giết hiệu suất như nó chưa bao giờ bị giết trước đó! –

+0

không thể chỉnh sửa ý kiến ​​ * cho mức cô lập có thể lập trình được thực thi –

0

Một tùy chọn khác sẽ là thêm quy trình kích hoạt vào bảng Sự kiện không làm gì ngoài việc thêm bản ghi tương tự vào bảng EventsBackup.

Bằng cách đó, EventBackup luôn được cập nhật và tất cả những gì bạn làm là định kỳ xóa hồ sơ khỏi bảng Sự kiện của bạn.

Các vấn đề liên quan