Mysql
將列拆分為規範化表
我有一個非規範化表
原始去規範化表
id text 23 first,second|third,fourth,fifth|sixth
我想規範化數據庫以創建以下表
表一:(用“|”分割)
id parent_id value 1 23 first,second 2 23 third,fourth,fifth 3 23 sixth
然後,
表2(以“,”分隔)
id parent_id value 1 1 first 2 1 second 3 2 third 4 2 fourth 5 2 fifth 6 3 sixth
最簡單直接的方法是使用子查詢(
SELECT
),但我關心的是性能,因為這個過程應該針對一個很長的表並且定期(新數據到來)進行。有沒有一種有效的方法可以高速規範化這個表?
我創建了一個儲存過程解決方案來執行以下操作:
- 將 t1 的 txt 欄位轉換為由 ‘|’ 分隔的擴展 INSERT 列表
- 執行擴展的 INSERT
- 將 t2 的 value 欄位轉換為由 ‘,’ 分隔的擴展 INSERT 列表
- 執行擴展的 INSERT
這是範例數據
DROP DATABASE IF EXISTS all_denorm; CREATE DATABASE all_denorm; USE all_denorm DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; DROP TABLE IF EXISTS t3; CREATE TABLE t1 ( id INT NOT NULL, txt TEXT ) ENGINE=InnoDB; INSERT INTO t1 (id,txt) VALUES (23,'first,second|third,fourth,fifth|sixth'), (24,'seventh,eighth|ninth|tenth,eleven|twelfth'), (25,'thirteenth|fourteenth|fifteenth|sixteen|seventeenth'), (26,'eighteenth,nineteenth|twentieth'); CREATE TABLE t2 ( id INT NOT NULL AUTO_INCREMENT, parent_id INT NOT NULL, VALUE VARCHAR(255), PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE TABLE t3 LIKE t2;
這是儲存過程
DELIMITER $$ DROP PROCEDURE IF EXISTS `all_denorm`.`process_denorm` $$ CREATE PROCEDURE `all_denorm`.`process_denorm` () BEGIN DECLARE given_id,current_id,last_id,processing INT; DECLARE delimiter_char CHAR(1); SET current_id = 0; SET processing = 1; SET delimiter_char = '|'; WHILE processing = 1 DO SELECT MIN(id) INTO given_id FROM t1 WHERE id > current_id; IF ISNULL(given_id) THEN SET processing = 0; ELSE SET current_id = given_id; SELECT REPLACE(REPLACE(CONCAT('(''',REPLACE(txt,delimiter_char,'''),('''),''')'),'(','(QQQ,'),'QQQ',FLOOR(given_id)) INTO @insert_list FROM t1 WHERE id = given_id; SELECT CONCAT('insert into t2 (parent_id,value) values ',@insert_list) INTO @insert_SQL; PREPARE stmt FROM @insert_SQL; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; END WHILE; SET current_id = 0; SET processing = 1; SET delimiter_char = ','; WHILE processing = 1 DO SELECT MIN(id) INTO given_id FROM t2 WHERE id > current_id; IF ISNULL(given_id) THEN SET processing = 0; ELSE SET current_id = given_id; SELECT REPLACE(REPLACE(CONCAT('(''',REPLACE(value,delimiter_char,'''),('''),''')'),'(','(QQQ,'),'QQQ',FLOOR(given_id)) INTO @insert_list FROM t2 WHERE id = given_id; SELECT CONCAT('insert into t3 (parent_id,value) values ',@insert_list) INTO @insert_SQL; PREPARE stmt FROM @insert_SQL; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; END WHILE; SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; END $$ DELIMITER ;
在這裡執行
mysql> DROP DATABASE IF EXISTS all_denorm; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> CREATE DATABASE all_denorm; Query OK, 1 row affected (0.00 sec) mysql> USE all_denorm Database changed mysql> DROP TABLE IF EXISTS t1; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> DROP TABLE IF EXISTS t2; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> DROP TABLE IF EXISTS t3; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> CREATE TABLE t1 -> ( -> id INT NOT NULL, -> txt TEXT -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.07 sec) mysql> INSERT INTO t1 (id,txt) VALUES -> (23,'first,second|third,fourth,fifth|sixth'), -> (24,'seventh,eighth|ninth|tenth,eleven|twelfth'), -> (25,'thirteenth|fourteenth|fifteenth|sixteen|seventeenth'), -> (26,'eighteenth,nineteenth|twentieth'); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> CREATE TABLE t2 -> ( -> id INT NOT NULL AUTO_INCREMENT, -> parent_id INT NOT NULL, -> VALUE VARCHAR(255), -> PRIMARY KEY (id) -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.05 sec) mysql> CREATE TABLE t3 LIKE t2; Query OK, 0 rows affected (0.05 sec) mysql> DELIMITER $$ mysql> mysql> DROP PROCEDURE IF EXISTS `all_denorm`.`process_denorm` $$ Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> CREATE PROCEDURE `all_denorm`.`process_denorm` () -> BEGIN -> -> DECLARE given_id,current_id,last_id,processing INT; -> DECLARE delimiter_char CHAR(1); -> -> SET current_id = 0; -> SET processing = 1; -> SET delimiter_char = '|'; -> WHILE processing = 1 DO -> SELECT MIN(id) INTO given_id FROM t1 WHERE id > current_id; -> IF ISNULL(given_id) THEN -> SET processing = 0; -> ELSE -> SET current_id = given_id; -> SELECT -> REPLACE(REPLACE(CONCAT('(''',REPLACE(txt,delimiter_char,'''),('''),''')'),'(','(QQQ,'),'QQQ',FLOOR(given_id)) -> INTO @insert_list FROM t1 WHERE id = given_id; -> SELECT CONCAT('insert into t2 (parent_id,value) values ',@insert_list) INTO @insert_SQL; -> PREPARE stmt FROM @insert_SQL; -> EXECUTE stmt; -> DEALLOCATE PREPARE stmt; -> END IF; -> END WHILE; -> -> SET current_id = 0; -> SET processing = 1; -> SET delimiter_char = ','; -> WHILE processing = 1 DO -> SELECT MIN(id) INTO given_id FROM t2 WHERE id > current_id; -> IF ISNULL(given_id) THEN -> SET processing = 0; -> ELSE -> SET current_id = given_id; -> SELECT -> REPLACE(REPLACE(CONCAT('(''',REPLACE(value,delimiter_char,'''),('''),''')'),'(','(QQQ,'),'QQQ',FLOOR(given_id)) -> INTO @insert_list FROM t2 WHERE id = given_id; -> SELECT CONCAT('insert into t3 (parent_id,value) values ',@insert_list) INTO @insert_SQL; -> PREPARE stmt FROM @insert_SQL; -> EXECUTE stmt; -> DEALLOCATE PREPARE stmt; -> END IF; -> END WHILE; -> -> SELECT * FROM t1; -> SELECT * FROM t2; -> SELECT * FROM t3; -> -> END $$ Query OK, 0 rows affected (0.00 sec) mysql> mysql> DELIMITER ;
這是輸出:
mysql> CALL process_denorm(); +----+-----------------------------------------------------+ | id | txt | +----+-----------------------------------------------------+ | 23 | first,second|third,fourth,fifth|sixth | | 24 | seventh,eighth|ninth|tenth,eleven|twelfth | | 25 | thirteenth|fourteenth|fifteenth|sixteen|seventeenth | | 26 | eighteenth,nineteenth|twentieth | +----+-----------------------------------------------------+ 4 rows in set (0.01 sec) +----+-----------+-----------------------+ | id | parent_id | VALUE | +----+-----------+-----------------------+ | 1 | 23 | first,second | | 2 | 23 | third,fourth,fifth | | 3 | 23 | sixth | | 4 | 24 | seventh,eighth | | 5 | 24 | ninth | | 6 | 24 | tenth,eleven | | 7 | 24 | twelfth | | 8 | 25 | thirteenth | | 9 | 25 | fourteenth | | 10 | 25 | fifteenth | | 11 | 25 | sixteen | | 12 | 25 | seventeenth | | 13 | 26 | eighteenth,nineteenth | | 14 | 26 | twentieth | +----+-----------+-----------------------+ 14 rows in set (0.01 sec) +----+-----------+-------------+ | id | parent_id | VALUE | +----+-----------+-------------+ | 1 | 1 | first | | 2 | 1 | second | | 3 | 2 | third | | 4 | 2 | fourth | | 5 | 2 | fifth | | 6 | 3 | sixth | | 7 | 4 | seventh | | 8 | 4 | eighth | | 9 | 5 | ninth | | 10 | 6 | tenth | | 11 | 6 | eleven | | 12 | 7 | twelfth | | 13 | 8 | thirteenth | | 14 | 9 | fourteenth | | 15 | 10 | fifteenth | | 16 | 11 | sixteen | | 17 | 12 | seventeenth | | 18 | 13 | eighteenth | | 19 | 13 | nineteenth | | 20 | 14 | twentieth | +----+-----------+-------------+ 20 rows in set (0.04 sec) Query OK, 0 rows affected (0.10 sec) mysql>
試一試 !!!