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

时间:2010-05-30 11:36:23

标签: sql mysql

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

他们现在要求将上面的报告表分成不同的行,例如

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

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

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

5 个答案:

答案 0 :(得分:3)

这称为walking a string。以下是您可以使用提供的规范进行操作的示例:

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

int_table

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

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

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) = ',')
  )

实际使用它可能效率不高,但我想我会把它作为一个例子,让你知道什么是可用的。

答案 1 :(得分: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 )

答案 2 :(得分:0)

有趣。

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

我猜你可以将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存储过程不是很了解,但也许你可以用循环来做一些聪明的东西,以使它更具动态性。

我希望这能指出你正确的方向 - ?

答案 3 :(得分:0)

我真的想让这个使用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();
        }
    }
}
?>

答案 4 :(得分:0)

概述

  • 这是为了补充使用UNION提出的答案

上下文

  • MySQL 5.7

问题

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

屏幕截图

  • 目标是将之前更改为之后 enter image description here

解决方案

  • 如果预先知道每个单行定界字符串的最大项数,则可以通过对已知元素数进行UNION查询来实现
  • 在此示例中,我们忽略第一个元素,因为它被认为对输出结果是多余的(例如,您遇到所有定界字符串都以诸如http://之类的一次性标记开头的情况)< / li>

示例代码


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 <> '')
;;;

完整示例

理性

  • 用于必须在MySQL内部而不是在应用程序层上进行转换的情况
  • 用于特定用户帐户出于安全或其他原因而无权在MySQL中创建新功能的情况下使用

陷阱

  • 此解决方案假定每个定界字符串的已知最大项目数(并非始终是一个选择)
  • 此解决方案要求在定界字符串的末尾使用尾部定界符,否则将无法产生预期的结果
  • 这种方法在美学上并不令人愉悦,会伤害眼睛和伤害某些感觉