【问题标题】:How to normalize comma separated values within column of table and then run query如何规范化表列中的逗号分隔值然后运行查询
【发布时间】:2014-12-06 21:57:13
【问题描述】:

假设我有一个具有以下结构的表:

 | column1    |    column2       |
 |------------|------------------|
 | a          |    1,L,3,K,5,    |
 | b          |    R,6,7,8,9     |
 | c          |    8,9,10,D      |
 | d          |    1,2,3,H       |

假设 column1 可以继续通过 z 并且 column2 可以继续使用随机数字和字母。我想要一个可以应用于任意数量的行和列以及 column2 中的值数量的通用解决方案。

我想在 MySQL 中运行一个查询,它将搜索 column2 中的所有值并输出 column1 中包含 column2 中的 3 的字母。输出应该是:

 | column1    |
 |------------|
 | a          | 
 | d          |  

许多帖子都有直接完成此任务的查询的答案,但我想以正确的方式进行。我是 sql 新手,但我相信这意味着通过创建一个新表并在这个新表上运行查询来规范化 column2 中的数据。

有人可以帮我编写代码以在 MySQL 中规范化和运行此查询吗?谢谢。

【问题讨论】:

  • 您打算用标准化的表永久替换您的表吗?
  • 没有。我希望保持原表完好无损。
  • 保持当前表的状态将是一个坏主意。对于数据库,您不希望同一列中有多个值。它可以/将在未来产生问题。
  • Alldayiwonder 你的表设计真的很糟糕.. 虽然 MySQL 有一个逗号分隔列表的解决方法,但它仍然是一个非常糟糕的想法......我的答案是一个可行的解决方案,但我会发布如何规范化它一分钟后
  • FIND_IN_SET 应该适用于您的示例。

标签: mysql csv normalization


【解决方案1】:

我知道这是旧帖子,但我认为我会做出贡献,因为我需要自己执行此练习,这给了我一些想法。

这是我得到的解决方案,这里的附带条件是逗号分隔列表中的值在另一个表中唯一定义:

create table `defined_values` (
  id int(11) not null auto_increment primary key,
  label varchar(12) not null,
  constraint `defined_values_uidx` unique (`label`)
) engine = innodb charset utf8;

create table `delimited_string` (
  id int(11) not null auto_increment primary key,
  `str_delim` varchar(32) not null
) engine = innodb charset utf8;

insert into `defined_values` (`label`) values
('YVR'),
('YEG'),
('YXJ'),
('YYC'),
('YMM')
;

insert into `delimited_string` (`str_delim`) values 
('YVR,YEG,YXJ,YYC,YMM')
;

select 
  v.`label` as `normalized` 
from 
  `delimited_string` s
  join `defined_values` v on (v.`label` = substring(s.`str_delim`, position(v.`label` in s.`str_delim`), length(v.`label`))) 

;

// 产量:

+------------+
| normalized | 
+------------+
| YEG        |
| YMM        |
| YVR        |
| YXJ        |
| YYC        |
+------------+

集合中的 5 行(0.00 秒)

【讨论】:

    【解决方案2】:
    select column1 from your_tab where 3 in (column2)
    

    或者你可以使用

    select column1 from your_tab where column2 like '3,%' or column2 like '%,3' or column2 like '%,3,%' or column2=3
    

    【讨论】:

    • 你能添加一些描述吗?
    【解决方案3】:

    你需要使用FIND_IN_SET()

    SELECT column1 FROM table WHERE FIND_IN_SET('3', column2);
    

    Fiddle Demo

    您应该从不将数据作为逗号分隔列表存储在表中...所以如果我是您,我会考虑将其分隔成行

    要规范化您的数据库,您可以执行这样的查询...注意您需要知道您在 column2 中拥有的片段数..

    CREATE TEMPORARY TABLE IF NOT EXISTS normalized_table AS 
    (   SELECT
          column1,
          SUBSTRING_INDEX(SUBSTRING_INDEX(column2, ',', n.digit+1), ',', -1) column2
        FROM test
        JOIN(SELECT 0 digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) n
            ON LENGTH(REPLACE(column2, ',' , '')) <= LENGTH(column2)-n.digit
        ORDER BY column1, n.digit
    );
    
    DROP table test;
    CREATE table test (column1 varchar(2), column2 varchar(2));
    INSERT INTO test (column1, column2) 
    SELECT column1, column2 FROM normalized_table;
    

    NORMALIZED RESULTS

    每个数字的 JOIN 是您的密钥...如果您有 6 个项目以逗号分隔,则联合 0-5 数字加入。

    如果您不知道有多少,那么只需运行此程序即可知道要合并多少个数字

    SELECT MAX(LENGTH(REPLACE(column2, ',', ''))) FROM test;
    

    【讨论】:

    • column2的片数未知的情况下是否可以进行归一化?我将修改问题以使其保持一般性。
    • @alldayiwonder 是的,但在这种情况下,您需要一个带循环的存储过程
    • @alldayiwonder 如果您查看我的答案的底部,我刚刚编辑了一种方法来确定您在第 2 列中有多少逗号分隔的项目...运行它,它会告诉您如何你需要做的许多工会:)
    【解决方案4】:

    要规范化此表,您可能需要使用由两列组成的表,主键是两列。它看起来像这样:

    | column1    |    column2       |
    |------------|------------------|
    | a          |    1             |
    | a          |    3             |
    | a          |    L             |
    | b          |    R             |
    | c          |    8             |
    | d          |    3             |
    

    然后你可以使用这个简单的查询:

    Select column1 from table where column2 = 3;
    

    【讨论】:

    • 这是有道理的。什么代码可以完成规范化?
    • 由于您的数据集很可能很小,我会从表中删除所有内容,按照我的描述修改表,然后重新加载数据。您还可以在删除之前将表格数据复制到 excel 中,这样您就不会丢失任何东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-26
    相关资源
    最近更新 更多