我正在尝试规范化一个表,前一个开发人员设计的表包含一个包含管道分隔ID的列,该列链接到同一个表中的其他行.
客户表
id | aliases (VARCHAR) ---------------------------- 1 | |4|58|76 2 | 3 | 4 | |1|58|76 ... | 58 | |1|4|76 ... | 76 | |1|4|58
因此,客户1,4,58和76都是彼此的“别名”.客户2和3没有别名,因此该字段包含空字符串.
我想取消整个“别名”系统,并规范化数据,以便将其他所有客户映射到一条记录.所以我希望客户1,58和76的相关表数据都只映射到客户1.
我想我会填充一个新表,然后我可以加入并在其他表上执行更新.
加入表格
id | customer_id | alias_id ------------------------------- 1 | 1 | 4 2 | 1 | 58 3 | 1 | 76
如何从第一个表中获取上述格式的数据?如果这将是纯SQL中的绝对噩梦,我将编写一个PHP脚本,尝试执行此工作并插入数据.
解决方法
当我开始回答这个问题时,我认为这将是快速而简单的,因为我在SQL Server中做过一次非常类似的事情,但证明了翻译中的概念已经发展到这个完整的解决方案中.
您的问题中不明确的一个警告是,您是否有条件声明主要ID与别名ID.例如,此解决方案将允许1具有别名4以及4具有别名1,这与简化示例问题中提供的数据一致.
要为此示例设置数据,我使用了以下结构:
CREATE TABLE notnormal_customers ( id INT NOT NULL PRIMARY KEY,aliases VARCHAR(10) ); INSERT INTO notnormal_customers (id,aliases) VALUES (1,'|4|58|76'),(2,''),(3,(4,'|1|58|76'),(58,'|1|4|76'),(76,'|1|4|58');
首先,为了表示一个客户到多个别名的一对多关系,我创建了这个表:
CREATE TABLE customer_aliases ( primary_id INT NOT NULL,alias_id INT NOT NULL,FOREIGN KEY (primary_id) REFERENCES notnormal_customers(id),FOREIGN KEY (alias_id) REFERENCES notnormal_customers(id),/* clustered primary key prevents duplicates */ PRIMARY KEY (primary_id,alias_id) )
最重要的是,我们将使用custom SPLIT_STR
function:
CREATE FUNCTION SPLIT_STR( x VARCHAR(255),delim VARCHAR(12),pos INT ) RETURNS VARCHAR(255) RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x,delim,pos),LENGTH(SUBSTRING_INDEX(x,pos -1)) + 1),'');
然后我们将创建一个存储过程来完成所有工作.代码注释了对源引用的注释.
DELIMITER $$ CREATE PROCEDURE normalize_customers() BEGIN DECLARE cust_id INT DEFAULT 0; DECLARE al_id INT UNSIGNED DEFAULT 0; DECLARE alias_str VARCHAR(10) DEFAULT ''; /* set the value of the string delimiter */ DECLARE string_delim CHAR(1) DEFAULT '|'; DECLARE count_aliases INT DEFAULT 0; DECLARE i INT DEFAULT 1; /* use cursor to iterate through all customer recordsHow To Loop Through A Result Set in MySQL Strored Procedure*/ DECLARE done INT DEFAULT 0; DECLARE cur CURSOR FOR SELECT `id`,`aliases` FROM `notnormal_customers`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP /* Fetch one record from CURSOR and set to customer id and alias string. If not found then `done` will be set to 1 by continue handler. */ FETCH cur INTO cust_id,alias_str; IF done THEN /* If done set to 1 then exit the loop,else continue. */ LEAVE read_loop; END IF; /* skip to next record if no aliases */ IF alias_str = '' THEN ITERATE read_loop; END IF; /* get number of aliases Count Occurrence of Character in a String Using MySQL */ SET count_aliases = LENGTH(alias_str) - LENGTH(REPLACE(alias_str,string_delim,'')); /* strip off the first pipe to make it compatible with our SPLIT_STR function */ SET alias_str = SUBSTR(alias_str,2); /* iterate and get each alias from custom split string function https://stackoverflow.com/questions/18304857/split-delimited-string-value-into-rows */ WHILE i <= count_aliases DO /* get the next alias id */ SET al_id = CAST(SPLIT_STR(alias_str,i) AS UNSIGNED); /* REPLACE existing values instead of insert to prevent errors on primary key */ REPLACE INTO customer_aliases (primary_id,alias_id) VALUES (cust_id,al_id); SET i = i+1; END WHILE; SET i = 1; END LOOP; CLOSE cur; END$$ DELIMITER ;
最后,您可以通过调用以下命令运行它:
CALL normalize_customers();
然后你可以在控制台中检查数据:
mysql> select * from customer_aliases; +------------+----------+ | primary_id | alias_id | +------------+----------+ | 4 | 1 | | 58 | 1 | | 76 | 1 | | 1 | 4 | | 58 | 4 | | 76 | 4 | | 1 | 58 | | 4 | 58 | | 76 | 58 | | 1 | 76 | | 4 | 76 | | 58 | 76 | +------------+----------+ 12 rows in set (0.00 sec)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。