在mysql中将分隔字符串转换为多个值

Posted

技术标签:

【中文标题】在mysql中将分隔字符串转换为多个值【英文标题】:Converting delimited string to multiple values in mysql 【发布时间】:2010-05-30 11:36:22 【问题描述】:

我有一个 mysql 旧表,其中包含一个客户端标识符和一个项目列表,后者作为逗号分隔的字符串。例如。 "xyz001", "foo,bar,baz"。这是遗留的东西,用户坚持能够编辑逗号分隔的字符串。

他们现在需要将上述内容分成单独的行的报告表,例如

"xyz001", "foo"
"xyz001", "bar"
"xyz001", "baz"

将字符串分解为子字符串很容易,我已经编写了一个过程来通过创建一个单独的表来实现这一点,但这需要触发器来处理删除、更新和插入。此查询很少需要(例如每月一次),但在运行时必须绝对是最新的,例如触发器的开销是没有保证的,并且创建表的计划任务可能不够及时。

有没有办法编写一个函数来返回一个表或一个集合,以便我可以根据需要将标识符与单个项目连接起来?

【问题讨论】:

【参考方案1】:

这称为walking a string。下面是一个示例,说明您可以如何使用提供的规范来做到这一点:

您需要创建一个包含与字段长度 + 1 一样多的整数的表。因此,如果字段的长度为 255,您将需要 256 条记录,其中仅包含 0-255 的单个数字。

int_table:

+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+---+

接下来,您将需要一个连接该表的查询,并检查该位置是否存在逗号。 (我用字段clientitems 分别调用了你的表legacy_table。)

select 
  legacy_table.client, 
  substring(
    legacy_table.items, 
    int_table.i + 1, 
    if(
      locate(',', legacy_table.items, int_table.i + 1) = 0, 
      length(legacy_table.items) + 1, 
      locate(',', legacy_table.items, int_table.i + 1)
    ) - (int_table.i + 1)
  ) as item
from legacy_table, int_table
where legacy_table.client = 'xyz001'
  and int_table.i < length(legacy_table.items)
  and (
    (int_table.i = 0) 
    or (substring(legacy_table.items, int_table.i, 1) = ',')
  )

它的效率可能不足以让您实际使用它,但我想我会将它作为一个示例来展示,以便您了解可用的内容。

【讨论】:

这是一种享受,不能说我完全理解它,但我一定会研究它直到我理解它。非常感谢。 警告:上面的代码包含一个错误。它不返回列表中的第一个元素(例如“foo”)。下面来自 Thomas 的代码不包含此错误。【参考方案2】:

您可以使用包含整数顺序列表的 Numbers 或 Tally 表来执行此操作:

Select Substring(T.List, N.Value, Locate(', ', T.List + ', ', N.Value) - N.Value)
From Numbers As N
    Cross Join MyTable As T
Where N.Value <= Len(T.List)
    And Substring(', ' + T.List, N.Value, 1) = ', '

在上述情况下,我的Numbers 表结构如下:

Create Table Numbers( Value int not null primary key )

【讨论】:

要使上述代码在 MySQL 中按照与此问题相关的标签工作,您需要使用 MySQL 的 CONCAT(A,B) 函数,而不是使用 Microsoft SQL Server 的 + 的 A+B 方法运算符连接字符串。【参考方案3】:

有趣。

我想不出一个干净整洁的解决方案,但我可以提供一些想法。

我想您可以将 UNION 与以下内容结合使用:

http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

如果您知道任何行中的最大项目数,那么您可以对结果进行联合.. 类似:

(SELECT col1, SPLIT_STR(col2, ',', 1) c2 from tbl)
UNION (SELECT col1, SPLIT_STR(col2, ',', 2) c2 from tbl where col2 like '%,%')
UNION (SELECT col1, SPLIT_STR(col2, ',', 3) c2 from tbl where col2 like '%,%,%')
...

我对 mysql 存储过程不是很了解,但也许你可以用循环做一些聪明的事情,以使其更具动态性。

我希望这能为您指明正确的方向 - ?

【讨论】:

【参考方案4】:

我真的很想用 SQL 让它工作,但我做不到。我编写了这个快速的 php 脚本来完成这项工作。

它又快又脏,而不是我推荐的生产代码。但有时你会尽一切努力快速完成工作。

<?php
$table = "ref_app";
$pri_column = "Repo";
$column = "Topics";
$newTable = "topics";

$conn = mysql_connect("localhost", "dev", "password");
mysql_select_db("esb_data");
if($conn==null)
    echo "Connection not made";
    exit;


$result = mysql_query("select ".$pri_column.", ".$column." from ".$table);
if(mysql_errno())
    echo "<br>".mysql_error();
    exit;


$applications = array();
while($row = mysql_fetch_array($result))
    echo "<br>".$row[$pri_column];
    $topics = explode(",", $row[$column]);
    foreach($topics as $topic)
        $topic = trim($topic);
        $applications[$row[$pri_column]][$topic] = $topic;
    
    echo "<br>".$row[$column];

echo "<pre>";
print_r($applications);
echo "</pre>";

foreach($applications as $app => $topics)
    foreach($topics as $topic)
        $query = "insert into ".$newTable." values ('', \"".$app."\", \"".$topic."\")";
        echo "<br>".$query;
        mysql_query($query);
        if(mysql_errno())
            echo "<br>".mysql_error();
        
    

?>

【讨论】:

【参考方案5】:

概述

这是对建议使用UNION 的答案的补充

上下文

MySQL 5.7

问题

用户 xyz001epo 希望将单行分隔的字符串转换为多行

截图

目标是将BEFORE改为AFTER

解决方案

如果事先知道每个单行分隔字符串的最大项目数,则可以使用 UNION 查询已知数量的元素来完成 在此示例中,我们忽略了第一个元素,因为它被认为对输出结果是多余的(例如,您有一种情况,所有分隔字符串都以一些一次性标记开头,例如 http:// 或类似的东西)

示例代码


SELECT
  tta.*
FROM
(
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',2),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',3),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',4),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',5),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',6),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',7),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',8),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',9),'/',-1)  as txtvalu
    FROM zzdemo_uu112pingasrcdata
UNION
    SELECT
      rowid                                                              as txtname
      ,SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(fld005,'/'),'/',10),'/',-1) as txtvalu
    FROM zzdemo_uu112pingasrcdata
) as tta
WHERE 1
  AND (txtvalu is not null)
  AND (txtvalu <> '')
;;;

完整示例

public lab space for this answer 上提供了完整示例

基本原理

当必须在 MySQL 内部而不是在应用层进行转换时使用 当特定用户帐户出于安全或其他原因而无权在 MySQL 中创建新函数时使用

陷阱

此解决方案假定每个分隔字符串的最大项目数已知,这并不总是一个选项 此解决方案需要在分隔字符串的末尾使用尾随分隔符,否则将不会产生所需的结果 这种做法不美观,会伤眼,伤感情

【讨论】:

以上是关于在mysql中将分隔字符串转换为多个值的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 3 中将逗号分隔的字符串转换为 Numpy 数组

在PHP中将字符串[基本上是逗号分隔的数字]转换为整数的最佳方法[重复]

在 MySql 中将 VARCHAR 转换为 DECIMAL 值

mysql中选定值的逗号分隔字符串

在scala中将字符串日期(不带分隔符)转换为日期格式

如何在 smarty 模板中将数组转换为逗号分隔的字符串?