2017-07-31 9 views
0

私は、毎日の注文、顧客、営業担当者を格納するテーブルをいくつか持っています。しかし、列が不適切なデータの値と型、欠落した索引やパーティションなどを持っているため、スキーマがうまく設計されていませんでした。新しいスキーマを再設計し、破損した表を新しい表に移入しました。私は今、毎日の受注テーブル(約10Mレコード)を投入することに固執しています。MySQL挿入のスピードアップは、10百万のレコードで選択します。

添付されたデータ定義と、テーブルを移入するSQLスクリプト。 3倍のkレコードとの6xx KレコードとDRIテーブルとパグテーブル:

テーブル定義

CREATE TABLE IF NOT EXISTS `testing`.`Orders` (
    `order_ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, 
    `ord_id` BIGINT UNSIGNED NOT NULL, 
    `create_time` DATETIME NOT NULL, 
    `create_date` DATE NOT NULL, 
    `cust_id` MEDIUMINT UNSIGNED NOT NULL, 
    `cust_mob` BIGINT UNSIGNED NULL, 
    `sales_id` MEDIUMINT UNSIGNED NULL, 
    `sales_mob` BIGINT UNSIGNED NULL, 
    `sales_flag` TINYINT UNSIGNED NULL, 
    `comm_flag` TINYINT UNSIGNED NULL, 
    `extraprice` TINYINT UNSIGNED NULL, 
    PRIMARY KEY (`order_ID`), 
    INDEX `Date_cust_id` (`create_date` ASC, `cust_id` ASC), 
    INDEX `Date_cust_mob` (`create_date` ASC, `cust_mob` ASC), 
    INDEX `Date_dri_id` (`create_date` ASC, `sales_id` ASC), 
    INDEX `Date_dri_mob` (`create_date` ASC, `sales_mob` ASC), 
    INDEX `Date_cust` (`create_date` ASC, `cust_id` ASC, `cstu_mob` ASC), 
    INDEX `Date_dri` (`create_date` ASC, `sales_id` ASC, `sales_mob` ASC), 
    INDEX `cust` (`cust_id` ASC, `cust_mob` ASC), 
    INDEX `dri` (`sales_id` ASC, `sales_mob` ASC), 
    UNIQUE INDEX `ord_id_UNIQUE` (`ord_id` ASC) 
) 
ENGINE = InnoDB 
DEFAULT CHARACTER SET = utf8; 

このスクリプトは、2つのテーブルを結合する左の関与、テーブルを移入することです。

SET SQL_SAFE_UPDATES=0; 
SET SQL_MODE=''; 

DROP PROCEDURE IF EXISTS testing.populate_ord1; 
DELIMITER $$ 

CREATE PROCEDURE testing.populate_ord1() 
BEGIN 
    PREPARE stmt 
     FROM " 
      INSERT INTO testing.Orders 
      SELECT 
      1 
      ,ord_id 
      ,CASE WHEN TRIM(create_time) ='NULL' THEN NULL ELSE STR_TO_DATE(substring(create_time,1,19), '%Y-%m-%d %H:%i:%s') END AS create_time 
      ,CASE WHEN TRIM(create_time) ='NULL' THEN NULL ELSE DATE(STR_TO_DATE(substring(create_time,1,19), '%Y-%m-%d %H:%i:%s')) END AS create_date 
      ,CASE WHEN TRIM(ord.cust_id) = 'NULL' THEN NULL else pag.cust_id END as cust_id 
      ,CASE WHEN TRIM(ord.mob) = 'NULL' THEN NULL else pag.cust_mob END as cust_mob 
      ,CASE WHEN TRIM(ord.sales_id) = 'NULL' THEN NULL else dri.sales_id END as sales_id 
      ,CASE WHEN TRIM(ord.mob1) = 'NULL' THEN NULL else dri.sales_mob END as sales_mob 
      ,CASE WHEN TRIM(sales_flag) ='NULL' THEN NULL ELSE CONVERT(TRIM(sales_flag),UNSIGNED INTEGER) end AS sales_flag 
      ,CASE WHEN TRIM(comm_flag) ='NULL' THEN NULL ELSE CONVERT(TRIM(comm_flag),UNSIGNED INTEGER) end AS comm_flag 
      ,CASE WHEN TRIM(extraprice) ='NULL' THEN NULL ELSE CONVERT(TRIM(extraprice),UNSIGNED INTEGER) end AS extraprice 

      FROM testing.ord_table ord 
       LEFT JOIN 
       (SELECT cust_id,customer_id,cust_mob FROM testing.Passenger) pag 
       ON TRIM(ord.customer_id) = TRIM(pag.pag_id) 
       AND TRIM(ord.mob) = TRIM(pag.passenger_mob) 
       LEFT JOIN 
       (SELECT sales_id,salesperson_id,sales_mob FROM testing.sales) dri 
       ON TRIM(ord.salesperson_id) = TRIM(dri.sales_id) 
       AND TRIM(ord.mob1) = TRIM(dri.sales_mob) 
      WHERE ord_id != 'NULL' AND create_time IS NOT NULL AND create_time != 'NULL' AND YEAR(create_time) = ? AND MONTH(create_time) = ? AND DAY(create_time) = ? 
      GROUP BY ord_id 
      ON DUPLICATE KEY UPDATE ord_id = ord_id 
      ; 

      "; 


    SET @y = 2014, @m = 9, @d = 1; 

    WHILE @y<= 2014 DO 
     WHILE @m<= 12 DO 
      SET @d = 1; 
      WHILE @d<= 31 DO 
       EXECUTE stmt USING @y, @m, @d; 
       SET @d = @d + 1; 
      END WHILE; 
      SET @m = @m + 1; 
     END WHILE; 
     SET @y = @y + 1; 
     SET @m = 1; 
    END WHILE; 
    DEALLOCATE PREPARE stmt; 


END$$ 
DELIMITER ; 

set autocommit=0; 
call testing.populate_ord1(); 
COMMIT; 

テーブルにレコードを設定できませんでした。ロック待機タイムアウトエラーやデータタイプエラーが発生したり、時間がかかりすぎると(2日間)、何らかの仕事をしていると思われます。

ウェブを少し検索し、my.cnfに次の設定を追加しました。

innodb_autoinc_lock_mode = 2 
innodb_lock_wait_time_out = 150 
innodb_flush_log_at_trx_commit =2 
innodb_buffer_pool_size = 14G 

どのように効率的に同じタスクを達成できるかについてアドバイスしますか?上記のコードは構文エラーなしで実行されます。また、名前の混乱がある場合は、それらの変数テーブルを少し微調整しているので、それが明確になることが重要であるかどうかを教えてください。後続の問合せ一部をスピードアップし、それらを簡素化します

UPDATE ... SET 
    comm_flag = TRIM(comm_flag), 
    sales_flag = TRIM(sales_flag), 
    ... 

を行うことで

+0

実際には、一部の列にテキスト文字列 '' NULL ''がありますか?これは 'NULL'と同じではなく、' NULL'は '='でテストできません。 –

+0

これは一回限りのアクションですか、それとも一種の予定された仕事ですか? – Sal

+0

はいカラム値として 'NULL'が存在し、1回限りのタスクです。 – yukclam9

答えて

0

スタート。

LEFT JOIN (SELECT ... FROM x WHERE ...)を使用しないでください。代わりに、それをLEFT JOIN x ON ... WHERE ...に変換できるかどうかを確認してください。それが役に立ちそうです。

DATEとTIMEを2つの列に分割することは、通常はお勧めできません。それとも、あなたはそのようなことについて良い議論をしていますか?その列のペアに接触するクエリを見てみましょう。

文字列が既に適切な形式のDATEまたはDATETIMEの場合は、STR_TO_DATE()の必要はありません。つまり、文字列はうまく動作します。

TRIMが途切れると、CONVERT(TRIM(comm_flag),UNSIGNED INTEGER)は簡単にcomm_flagになります。

一度に1日に何かをループしないでください - あなたはそれが構造化された方法で、完全なテーブルスキャンを実行します!約1000回!!

+0

ありがとう私はしようとしています...私は日付と時間を2つの列に分割していませんでした。非常に頻繁に日付データを照会するため、余分な日付列を追加します。 – yukclam9

関連する問題