私は SQL を書くのが初めてで、MySQL データベースにデータを追加するための手順をいくつか作成しました。問題は、クエリの数が多いため、非常に遅いことです。ここで行うことは、並べ替えられていない生データを含むテーブル内の各レコードをループし、そのデータ ポイントを取得してデータベースに追加することです。対処しなければならない FK が多数あるため、これは複雑になります。
これを最適化するのを手伝ってもらえますか?
例として、指定したテーブルを追加するには、次のようにします。CALL add_table1(112,15);
データを追加する
手順 -- 主な手順
CREATE PROCEDURE `add_table1`(
IN c_id INT UNSIGNED;
IN t_id INT UNSIGNED;
)
BEGIN
-- Table variables
DECLARE r_id INT UNSIGNED;
DECLARE dh_name VARCHAR(50);
DECLARE d_value DECIMAL(20,10);
-- Loop variables
DECLARE done BOOLEAN;
-- Cursor for measurement table
DECLARE m_cur CURSOR FOR
SELECT Run_ID, DataHeader_Name, Data_Value
FROM `measurements`.`measurement_20131029_152902`;
-- Handlers for exceptions
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Set start time
UPDATE `measurements`.`queue`
SET Start_Time = NOW()
WHERE Experiment_ID = 112 AND Procedure_Name = 'add_table1';
-- Loop through measurement table
OPEN m_cur;
m_loop: LOOP
FETCH m_cur INTO r_id, dh_name, d_value;
IF done THEN
CLOSE m_cur;
LEAVE m_loop;
END IF;
CALL add_measurement(dh_name, d_value, t_id, c_id, r_id);
END LOOP m_loop;
END
測定を追加する
手順 -- から呼び出される 2 次手順add_table1
CREATE PROCEDURE `add_measurement`(
IN measurement_header VARCHAR(50),
IN measurement_value DECIMAL(20,10),
IN tool_id_var INT UNSIGNED,
IN config_id_var INT UNSIGNED,
IN run_id_var INT UNSIGNED
)
BEGIN
-- Variables representing FKs
DECLARE data_header_id INT UNSIGNED;
DECLARE tool_header_link_id INT UNSIGNED;
DECLARE tool_data_id INT UNSIGNED;
DECLARE tool_data_link_id INT UNSIGNED;
-- Add header
INSERT IGNORE INTO data_headers(DataHeader_Name)
VALUES(measurement_header);
SET data_header_id = (SELECT DataHeader_ID
FROM data_headers WHERE DataHeader_Name = measurement_header);
-- Link header to tool
INSERT IGNORE INTO tool_header_link(DataHeader_ID, Tool_ID)
VALUES(data_header_id, tool_id_var);
SET tool_header_link_id = (SELECT ToolHeaderLink_ID
FROM tool_header_link
WHERE DataHeader_ID = data_header_id AND Tool_ID = tool_id_var);
-- Add measurement
INSERT IGNORE INTO tool_data(Data_Value) VALUES(measurement_value);
SET tool_data_id = (SELECT ToolData_ID
FROM tool_data WHERE Data_Value = measurement_value);
-- Link measurement to header and configuration
INSERT IGNORE INTO
tool_data_link(ToolHeaderLink_ID, ToolData_ID, Run_ID)
VALUES(tool_header_link_id, tool_data_id, run_id_var);
SET tool_data_link_id = (SELECT ToolDataLink_ID FROM tool_data_link
WHERE ToolHeaderLink_ID = tool_header_link_id
AND ToolData_ID = tool_data_id AND Run_ID = run_id_var);
-- Link measurement to experiment configuration
INSERT IGNORE INTO tool_link(ToolDataLink_ID, Config_ID)
VALUES(tool_data_link_id, config_id_var);
END
現在の解決策同様の問題について、この
解決策
を見つけました。コードの中身を a で囲んだところ、速度が大幅に向上したことがすぐにわかりました。クエリの推定完了時間は約 36 時間でしたが、実際の完了時間は約 5 分に短縮されました。また、データベースの設計を少し変更し、不要な FK を削除しました。誰かがこのコードを改善するためのさらなる方法を見つけたとしても、私はまだ興味があります. 私たちのアプリケーションのパフォーマンスは許容範囲内ですが、常に改善することに関心があります。TRANSACTION
変更を表示するには:
START TRANSACTION;
-- Loop through measurement table
OPEN m_cur;
m_loop: LOOP
FETCH m_cur INTO r_id, dh_name, d_value;
IF done THEN
CLOSE m_cur;
LEAVE m_loop;
END IF;
CALL add_measurement(dh_name, d_value, t_id, c_id, r_id);
END LOOP m_loop;
COMMIT;
代替ソリューション
以下の回答に基づいて、新しいソリューションを以下のソリューションに更新することができました。私のテストでは、この新しいソリューションは期待どおりに機能しているようです。また、以前のソリューションよりも 2 倍以上高速です。このルーチンを使用すると、約 2.5 分で 100 万個の一意のデータを追加できます。
ご協力ありがとうございました!
CREATE PROCEDURE `add_table`(
IN config_id_var INT UNSIGNED
)
BEGIN
START TRANSACTION;
-- Add header
INSERT IGNORE INTO data_headers(DataHeader_Name)
SELECT DataHeader_Name
FROM `measurements`.`measurement_20131114_142402`;
-- Add measurement
INSERT IGNORE INTO tool_data(Data_Value)
SELECT Data_Value
FROM `measurements`.`measurement_20131114_142402`;
-- Link measurement to header and configuration
-- INSERT Non-Unique Values
INSERT IGNORE INTO tool_data_link(DataHeader_ID, ToolData_ID, Run_ID)
SELECT h.DataHeader_ID, d.ToolData_ID, m.Run_ID
FROM `measurements`.`measurement_20131114_142402` AS m
JOIN data_headers AS h ON h.DataHeader_Name = m.DataHeader_Name
JOIN tool_data AS d ON d.Data_Value = m.Data_Value;
-- INSERT Unique Values
INSERT IGNORE INTO tool_data_link(DataHeader_ID, ToolData_ID, Run_ID)
SELECT h.DataHeader_ID, d.ToolData_ID, m.Run_ID
FROM `measurements`.`measurement_20131114_142402` AS m
LEFT OUTER JOIN data_headers AS h ON h.DataHeader_Name = m.DataHeader_Name
LEFT OUTER JOIN tool_data AS d ON d.Data_Value = m.Data_Value
WHERE ((h.DataHeader_Name IS NULL) OR (d.Data_Value IS NULL));
-- Link measurement to experiment configuration
-- INSERT Non-Unique Values
INSERT IGNORE INTO tool_link(ToolDataLink_ID, Config_ID)
SELECT tdl.ToolDataLink_ID, config_id_var
FROM tool_data_link AS tdl
JOIN data_headers AS h ON h.DataHeader_ID = tdl.DataHeader_ID
JOIN tool_data AS d ON d.ToolData_ID = tdl.ToolData_ID;
-- INSERT Unique Values
INSERT IGNORE INTO tool_link(ToolDataLink_ID, Config_ID)
SELECT tdl.ToolDataLink_ID, config_id_var
FROM tool_data_link AS tdl
LEFT OUTER JOIN data_headers AS h ON h.DataHeader_ID = tdl.DataHeader_ID
LEFT OUTER JOIN tool_data AS d ON d.ToolData_ID = tdl.ToolData_ID
WHERE ((h.DataHeader_ID IS NULL) OR (d.ToolData_ID IS NULL));
COMMIT;
END
結論
カーソルを使用しないソリューションでさらにテストを行いました。最初は間違いなく高速です。ただし、データベースのサイズが大きくなると、実行時間が大幅に増加します。
データベースに数百万のデータポイントを追加しました。次に、約数百のデータ ポイントの小さなデータ セットを追加してみました。カーソル ソリューションよりも 400 倍近く時間がかかりました。これは、カーソルが必要なデータ ポイントのみを参照したためだと思いますが、結合ではすべてのデータを参照する必要がありました。
これらの結果に基づくと、私のアプリケーションにはカーソル ソリューションの方が適しているようです。