2015-03-02 28 views
1

我有三個表:如何將行從一個表移動到另一個表中不存在的行中?

grade   (grade_id,  grade_value,  grade_date)  ~100M rows 
grade_archive (grade_id,  grade_value,  grade_date)   0 rows 
peer_review (grade_id, peer_review_value, peer_review_date)  ~10M rows 

我想所有行從表grade移動到grade_archive這一個多月的老年人和未列入表peer_review

這些表被積極使用,因此任何插入都必須低優先級,以避免在運行時中斷現有和新進程。

預計表格行看起來應該像這樣做的時候:

grade   ~10M rows 
grade_archive ~90M rows 
peer_review ~10M rows 

我想這對一些接近:

INSERT 
    LOW_PRIORITY 
    INTO grade_archive 
     (grade_id,grade_value,grade_date) 
    SELECT 
     grade_id,grade_value,grade_date 
    FROM 
     grade 
    WHERE 
      grade_date < DATE_ADD(NOW(), INTERVAL -1 MONTH) 
     AND grade_id NOT IN 
      (
       SELECT grade_id FROM peer_review 
      ); 

然後通過在檔案中刪除所有行清理grade表表格:

DELETE LOW_PRIORITY FROM grade WHERE grade_id IN (SELECT grade_id FROM grade_archive); 

但是這些子選項非常慢w大桌子,我對結果很緊張。尋找更好的方向。

回答

1

我在過去有一個類似的問題,將一部分數據從大型活動表遷移到歸檔表中。我使用的方法(修改你的使用情況)如下:

/* Set time for calculation basis */ 
[email protected]_time = NOW(); 
/* Create empty copy of grade table */ 
CREATE TABLE grade_temp LIKE grade; 
/* Add rows you want to save from grade into temp table */ 
INSERT INTO grade_temp 
SELECT 
    g.grade_id AS grade_id, 
    g.grade_value AS grade_value, 
    g.grade_date AS grade_date 
FROM grade AS g 
LEFT JOIN peer_review AS pr 
    ON g.grade_id = pr.grade_id 
WHERE 
/* 
To keep the record it must either have an entry in peer review 
or it is less than a month old 
*/ 
    pr.grade_id IS NOT NULL 
    OR g.grade_date >= DATE_SUB(@calc_time, INTERVAL 1 MONTH); 
/* 
Switch new temp table for active table. 
This happens really fast (it is just file name switching on the system). 
*/ 
RENAME TABLE grade TO grade_old, grade_temp TO grade; 
/* 
You are now taking new records into new version of grade table 
and free to do your much slower operations against the grade_old table 
*/ 
/* Delete more recent rows */ 
DELETE FROM grade_old 
WHERE grade_date >= DATE_SUB(@calc_time, INTERVAL 1 MONTH); 
/* Delete rows that exist in peer review */ 
DELETE FROM grade old 
WHERE grade_id IN (
    SELECT grade_id 
    FROM peer_review 
    WHERE grade_date < DATE_SUB(@calc_time, INTERVAL 1 MONTH) 
); 
/* 
As an alternate to the above action, you could also try deleting across join as shown below. Which is faster will likely depend upon number of records that are returned from that subquery shown above. You can try both out and see what works best 
*/ 
DELETE go FROM grade_old AS go 
INNER JOIN peer_review AS pr 
    ON go.grade_id = pr.grade_id 
WHERE pr.grade_date < DATE_SUB(@calc_time, INTERVAL 1 MONTH); 
/* Add all rows from grade_old to grade_archive */ 
INSERT INTO grade_archive 
SELECT 
    grade_id, 
    grade_value, 
    grade_date 
FROM grade_old; 
/* Drop date_old table */ 
DROP TABLE date_old; 

這裏的關鍵是獲得等級表的新版本,其中只包含在地方儘快所需的行,然後理出事實之後,什麼會進入檔案表。您不希望對該大小的表進行任何批量刪除操作。這樣可以保證您的成績表在這些檔案操作中所佔的時間達到最小。

然而,我會說,你的數據庫模式似乎可以爲這種操作進行優化。例如,您可以在您的成績表上使用同級審查標誌,以便您可以使用它來更快地過濾,而不必通過連接進行過濾。我實際上質疑整個同行評議表的需要,除非它與年級表有多對一的關係(這似乎沒有在你的問題中指出)。如果每個grade_id只有一個同行評審條目,我認爲這些專欄應該標準化到成績表中。這將大大簡化此維護過程。

+0

有關等級表上的peer_review_flag的好主意。如果我補充說,我是否會跳過上面的連接並按照書面進行,還是有更快的方法? – Ryan 2015-03-02 19:38:33

+1

在這種情況下,您可以僅替換與peer_review表和相關連接/子查詢相關的條件,以支持grade和grade_old表中的條件。在閱讀你的問題時,我完全不瞭解peer_review表的必要性。您在每個表格的行周圍提供的數字似乎表明在等級和peer_review表之間存在一對一的關係。如果是這種情況,那麼建議您最好將同行評議專欄添加到成績表上,以使一切變得更簡單。 – 2015-03-02 19:42:10

1

由於NOT IN (SELECT ...)是慘敗慢,使用LEFT JOIN .. IS NULL得到同樣的效果:

SELECT g.grade_id, g.grade_value, g.grade_date 
    FROM grade AS g 
    LEFT JOIN peer_review AS p USING(grade_id) 
    WHERE g.grade_date < DATE_ADD(NOW(), INTERVAL -1 MONTH) 
     AND gi.grade_id IS NULL ; 

沒有明確的TMP表所需。

相關問題