【问题标题】:mySQL >> Normalizing a comma-delimited fieldmySQL >> 规范化逗号分隔的字段
【发布时间】:2017-11-08 20:44:47
【问题描述】:

我目前有一个名为“RESOURCES”的表,其中包含一个名为“RES_Tags”的关键字字段。 “RES_Tags”字段包含一个逗号分隔的每条记录的关键字列表。

我需要规范化这个表/字段。

我已经设置了以下表格:TAGS、TAGS_TO_RESOURCES。

请在此处查看架构:http://sqlfiddle.com/#!9/edac4/1

什么查询可以让我解析 RES_Tags 中的关键字,将它们写入 TAGS 表而不创建重复项,然后在 TAGS_TO_RESOURCES 表中写入一个列表?

【问题讨论】:

  • 老实说,我会用 Python 编写一个小脚本来执行此操作。从tags.res_tags 获取每一行,将字符串拆分为一个数组,然后运行一个循环将多行插入到新表中,每个标签一行。
  • 我真的想过在 VBS 中这样做(我最喜欢的风格)。我可以使用 FOR EACH x IN RES_Tags、NEXT、LOOP,但我认为从数据库端开始可能会更好。仍然在栅栏上......
  • 在这种情况下,在 SQL 中执行任务非常尴尬,因此在应用程序代码中执行任务要好得多。 SQL 从一开始就意味着在应用程序代码的上下文中使用。
  • 另外,SQL 版本往往更难维护。我的示例代码需要修改(或更复杂)以处理列中塞满六个以上项目的情况。
  • 谷歌“(撤消或反向或反向)group_concat”。这是一个常见问题。

标签: mysql parsing database-normalization delimited


【解决方案1】:

请将您的代码复制到实际发布中,并提供您尝试用于解决问题的代码。

substring_index 函数返回带有一些分隔符(这里是逗号)的字符串的一部分,当传递一个负索引时,它开始从另一侧搜索匹配项,所以-1 从原本应该是的项目中抓取一个项目多项目列表(对于 index>=2)。

根据我们的讨论,我调整了我的做法,并展示了一个使用自动增量的示例。 (这是在小提琴的“构建模式”部分运行的。)

create table TAGS
(`T_ID` int auto_increment primary key, `T_Name` varchar(18))
;

insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(RES_Tags, ',', 1) as X
    FROM RESOURCES
;

insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(
      SUBSTRING_INDEX(RES_Tags, ',', 2)
      ,',',-1)
  FROM RESOURCES
;

insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(
      SUBSTRING_INDEX(RES_Tags, ',', 3)
      ,',',-1)  as X
  FROM RESOURCES
;
insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(
      SUBSTRING_INDEX(RES_Tags, ',', 4)
      ,',',-1)  as X
  FROM RESOURCES
  ;

insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(
      SUBSTRING_INDEX(RES_Tags, ',', 5)
      ,',',-1)  as X
  FROM RESOURCES
;

insert ignore into TAGS (T_Name)
  SELECT 
    SUBSTRING_INDEX(
      SUBSTRING_INDEX(RES_Tags, ',', 6)
      ,',',-1)  as X
  FROM RESOURCES
;

create table New_TAGS like TAGS;
insert into New_TAGS (T_Name)
  select distinct trim(T_Name)
  from TAGS;

drop table TAGS;
rename table NEW_TAGS to TAGS;

documentation of the substring function Possible duplication of this question

【讨论】:

  • 谢谢,布赖恩!您的查询将如何导致我正在寻找的解决方案(即将子字符串(如果唯一)写入 TAGS 表并在 TAGS_TO_RESOURCES 表中写入引用)?
  • 我的代码返回资源表中的 22 个唯一标签。如果您希望标签在列表中重复使用(即“苹果”在水果中多次出现),则必须在底部添加 group by 1。您尝试使用什么代码将结果放入标签表中?我大致知道您在问什么,但是您对 TAGS_TO_RESOURCES 的预期逻辑是什么?
  • 再次感谢布赖恩!返回的 22 个标签是唯一的 b/c 我忘了在标签中包含重复项。我已经更新了 SQL Fiddle 以包含一个副本(水果和面包中的“香蕉”)。当我运行您的查询时,它确实返回“香蕉”两次。你有解决办法吗?多对多 TAGS_TO_RESOURCES 表的原因是将标签与它们以前关联的资源一起带回。希望这是有道理的。
  • 我已经调整了上面的代码,以删除重复项。您可能需要调整表格的设计方式,以使用自动增量link to documentation。
【解决方案2】:
  1. 基于RESOURCES.RES_tags 创建一组INSERT ... INTO TAGS ... 语句。在TAGS 和ON DUPLICATE KEY ... 中使用UNIQUE 约束或使用INSERT ... SELECT ... NOT EXISTS() 防止重复:

a) 在RES_tags 的开头添加一些字符,在末尾添加不同的字符(比如 - 开始,+ 结束) - 但不要将其保存回 DB(a,b,c 会转换进入-a,b,c+)

b) 动态替换每个 ',' 以结束前一个 INSERT 语句并开始下一个语句;将 '-' 替换为仅开始,'+;仅带有结尾部分(例如,- 替换为 insert into tags(tag) values(",+ 变为 '") 和 , 将是 "), (" - 但为了保持它们的唯一性,需要添加步骤#1中提到的内容)

  1. 执行#1生成的SQL(如insert into tags(tag) values("a"), ("b"), ("c"))

  2. 使用标签链接实体:

    INSERT INTO TAGS_TO_RESOURCES(resource_id, tag_id)
    SELECT RESOURCES.id, TAGS.id
    FROM RESOURCES
    INNER JOIN TAGS
    ON INSTR(CONCAT(',', RESOURCES.RES.tags, ','), CONCAT(',', TAGS.tag, ','))> 0   
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    相关资源
    最近更新 更多