2

我的数据库中有一个未规范化的表,其名称details结构和示例数据如下(为图像道歉,只是认为它会更容易理解): **架构**

我的挑战是拆分列 -assignee, inventor and ipcsubclass使用分隔符|到新表 {detail_invinventors}、{detail_asgassignees} 和 {detail_ipcipcsubclasses}。

在所有三种情况下,表模式都是相似的。例如,发明者表和上的列以及detail_inv 表和上的id列。每行必须只有一个名称,所有名称在发明者表中都是唯一的,并且 id 以保持 detail_inv 表中的关系。namedetail_idinventor_id

我为发明者尝试了使用以下代码的存储过程-我为 3 列创建了 3 个过程:(

drop procedure if exists normalise_details;

delimiter #

create procedure normalise_details()
proc_main:begin

declare v_cursor_done int unsigned default 0;
declare v_post_id int unsigned;
declare v_tags varchar(2048);
declare v_keyword varchar(50);

declare v_keyword_id mediumint unsigned;

declare v_tags_done int unsigned;
declare v_tags_idx int unsigned;

declare v_cursor cursor for select id, inventor from details order by id;
declare continue handler for not found set v_cursor_done = 1;

set autocommit = 0; 

open v_cursor;
repeat

  fetch v_cursor into v_post_id, v_tags;
  set v_tags_done = 0;       
  set v_tags_idx = 1;

  while not v_tags_done do

    set v_keyword = substring(v_tags, v_tags_idx, 
      if(locate('|', v_tags, v_tags_idx) > 0, 
        locate('|', v_tags, v_tags_idx) - v_tags_idx, 
        length(v_tags)));

      if length(v_keyword) > 0 then

        set v_tags_idx = v_tags_idx + length(v_keyword) + 1;

        set v_keyword = trim(v_keyword);
        insert into inventors (name) values (v_keyword);

        select id into v_keyword_id from inventors where name = v_keyword;
        insert into details_inv (inventor_id, detail_id) values (v_keyword_id, v_post_id);

      else
        set v_tags_done = 1;
      end if;

  end while;

until v_cursor_done end repeat;

close v_cursor;

commit;

end proc_main #


delimiter ;

当我在一些随机测试数据上尝试这个时,它工作正常。当我在实际桌子上执行此操作时,效果不佳。仅插入部分数据。SQL 不会抛出任何错误(除了某些情况:“#1172 - 结果包含多于一行”或“inventor_id 列不能为空”)

我尝试在 MySQL 上修改代码 - 通过存储过程将逗号分隔列表插入规范化表以满足我的需要,但我失败了。

请帮助我,我的数据库表已经变得一团糟,大约有 500,000 行,这让我很难在每个项目上展开和管理巨大的数组(最近的项目有 ~200,000 行)。

4

2 回答 2

3

查看 RolandoMySQLDBA 对这个dba.stackexchange 问题的帖子,我觉得在我最初对触发存储过程的保留中得到了证实。但是,如果您确定在任何给定时间只有几行被用户输入更改,那么应该可以组合一个快速执行的过程。

但是,如果有许多用户并行工作,他们可能仍会相互锁定。我不知道这是否真的会发生,因为存储过程不会改变details表中的任何内容。如有必要,您可以查看此页面以获取想法。

编辑:触发

我刚刚将上一篇文章的 SQLfiddle 扩展为带有 trigger的 SQLfiddle ,其中包含以下内容:

CREATE TRIGGER normdet AFTER INSERT ON detail FOR EACH ROW
BEGIN
  DECLARE n int; DECLARE word VARCHAR(64)

 ;SET n=cntparts(NEW.inventor)
 ;WHILE n>0 DO
   SET word=part(new.inventor,n)
   ;IF NOT EXISTS (SELECT * FROM inv WHERE invname=word) THEN
     INSERT INTO inv (invname) VALUES (word)
   ;END IF
   ;INSERT INTO det2inv (didid,diiid) 
    SELECT NEW.id,invid FROM inv WHERE invname=word
   ;SET n=n-1
 ;END WHILE
  -- and similar loops for assignee and cls ...
;END;

我还定义了另一个函数

CREATE FUNCTION cntparts (var varchar(1024)) RETURNS int
RETURN 1+LENGTH(var)-LENGTH(REPLACE(var,'|',''));

计算给定的单词varchar。这也可以用来创建循环,而不是我UNION在第一篇文章中的基本转换的固定构造。

触发器现在处理所有新INSERT的 s。仍然需要编写一个类似的触发器来对UPDATEs 执行相同的操作。这应该不会太难做到......

在我的 SQLfiddle 中,我在触发器定义detail 之后插入了另一行。结果由两个比较 SELECT 语句列出,请参见fiddle

回复最后一条评论

好吧,正如我在原始答案中建议的那样,您应该首先导入所有数据(没有安装任何触发器!!!!)然后使用语句遍历detail-table 。SELECT/UNION在你这样做之前,你应该找出每列中的最大单词数assigneeinventoripsubclass使用

SELECT MAX(cntparts(inventor)) invcnt,
       MAX(cntparts(assignee)) asscnt,
       MAX(cntparts(ipsubclass)) clscnt 
FROM detail

然后,您可以调整SELECT/UNION每列所需的语句数。然后填写链接表,如 SQLfiddle 所示。

也许整个过程需要一段时间,但您可以安全地处理一个接一个的表(首先是实际属性表,然后是关联的链接表)。

之后,您可以激活您的触发器,该触发器只能单独添加的行上工作。

于 2013-09-03T06:23:00.897 回答
2

首先,在我看来,您应该将表格分成四个单独的表格:

  1. detail(主表,包含:id, projectid, publicationnumber, prioritycountry, prioritydatestatus
  2. inv (发明人表,包含:invid, invname以及可能更多与发明人相关的数据)
  3. cls(ipsubclass 表,包含:clsid, clsname可能还有每个类的描述)
  4. assignee(包含受让人公司的数据,例如assid, assname ...:)

由于主表和之间存在关系n:m,因此您还应该设置链接表来保存这些关系,例如inv, clsassignee

  • det2inv
  • det2cls
  • det2ass

重组任务可以分解为几个步骤:

首先,您需要应用用户定义的函数来拆分组合值。您可以使用此处描述的功能来做到这一点

我进一步简化了它,因为在您的示例中,我们只遇到一个分隔符|

CREATE FUNCTION part( x VARCHAR(255), pos INT) 
RETURNS VARCHAR(255) BEGIN
 DECLARE delim char(1)
 ;SET delim='|'
 ;RETURN TRIM(REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
                   LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
       delim, ''))
 ;END;

(注意TRIM删除任何不需要的空白的功能......)

接下来,您应该定义包含您的发明者和可能的 ipsubclasses 的目标表(...和受让人,我还没有这样做):

CREATE TABLE inv (invid int auto_increment PRIMARY KEY, invname nvarchar(64));
CREATE TABLE cls (clsid int auto_increment PRIMARY KEY, clsname nvarchar(6));

随意用额外的列来扩展表格,就像你需要它们一样。

现在我们用唯一值填充表格。首先表中的发明人inv

INSERT INTO inv (invname) 
SELECT inv FROM (
 SELECT part(inventor,1) inv from detail
 UNION 
 SELECT part(inventor,2) from detail
 UNION 
 SELECT part(inventor,3) from detail
 UNION 
 SELECT part(inventor,4) from detail
 UNION 
 SELECT part(inventor,5) from detail
 UNION 
 SELECT part(inventor,6) from detail
 UNION 
 SELECT part(inventor,7) from detail
 UNION 
 SELECT part(inventor,8) from detail
) t WHERE inv>'' ORDER BY inv;

接下来是 ipsubclasses:

INSERT INTO cls (clsname)
SELECT icls FROM (
 SELECT part(iclass,1) icls from detail
 UNION 
 SELECT part(iclass,2) from detail
 UNION 
 SELECT part(iclass,3) from detail
 UNION 
 SELECT part(iclass,4) from detail
 UNION 
 SELECT part(iclass,5) from detail
 UNION 
 SELECT part(iclass,6) from detail
 UNION 
 SELECT part(iclass,7) from detail
 UNION 
 SELECT part(iclass,8) from detail
) t WHERE icls>'' ORDER BY icls;

在我的示例中,我只查看了每个字段的前 8 个条目。当然,这可以根据您的需要进行修改。您最终将得到两个唯一编号的表,其中包含所有可能的发明者和所有可能的 ipsubclass(以及以类似方式所有受让人)。您可以在这里查看我的 SQLfiddle:http ://sqlfiddle.com/#!2/aeafe/1

现在剩下的任务是用合适的键填充链接表(主表details及其属性表的 id 对inv, clsassignee.

编辑

链接表正在填充以下语句:

INSERT INTO det2inv (didid,diiid)
SELECT id,invid FROM inv 
INNER JOIN detail ON INSTR(inventor,invname)>0;

INSERT INTO det2cls (dcdid,dccid)
SELECT id,clsid FROM cls 
INNER JOIN detail ON INSTR(iclass,clsname)>0;

-- ... and a similar one for det2ass

INSTR()功能将无法完美运行,因为名称 likeHagen, Pete将成功匹配Gleichenhagen, Peter. 为避免这些情况,应修改比较,如下所示:

...
INNER JOIN detail ON INSTR(REPLACE(CONCAT('|',inventor,'|'),' ',''),
                           REPLACE(CONCAT('|',invname,'|'),' ',''))>0;

您可以在此处查看完整的工作示例http ://sqlfiddle.com/#!2/097be/8

于 2013-09-02T15:52:44.827 回答