Php 如何替换MySQL字符串中特定字符的每个其他实例?

Php 如何替换MySQL字符串中特定字符的每个其他实例?,php,mysql,sql,string,replace,Php,Mysql,Sql,String,Replace,如何用查询替换mysql列中的值,例如,列为options及其类型为varchar(255) 从 到 我是这样用php做的 <?php $str = "A|10|B|20|C|30"; $arr = explode("|",$str); $newArr = array(); for($i=0;$i<count($arr);$i+=2){ if($arr[$i] && $arr[$i+1]){ $newArr

如何用查询替换mysql列中的值,例如,列为
options
及其类型为
varchar(255)

我是这样用php做的

<?php
    $str =  "A|10|B|20|C|30";
    $arr = explode("|",$str);
    $newArr = array();
    for($i=0;$i<count($arr);$i+=2){
      if($arr[$i] && $arr[$i+1]){
        $newArr[] = $arr[$i]."|".$arr[$i+1];
      }
    }
    echo "Before:".$str."\n";
    echo "After :".implode(",",$newArr);
?>


因此,我不想使用PHP,而是希望在MySQL中执行此操作。

嗯,我想您正在尝试这样做

SELECT GROUP_CONCAT(CONCAT(options,",") SEPARATOR "|") FROM Table.name;
我简单地解释一下,我为每一行取结果,然后连接“,”并用分隔符“|”连接所有行。 您必须用表的名称更改Table.name

如果您想连接一个或多个值,如A、B、C(您没有解释ABC值来自何处,所以让我们假设ValueWherebDiscomingFrom):

如果我的桌子是这样的:

id | ValueWhereABCisComingFrom | options
0  | A    | 10
1  | B    | 20
2  | C    | 30
你会有这样的东西:

A|10,B|20,C|30
编辑1

在那种情况下是没有办法的。mysql中没有像preg_replace这样的函数。你所能做的就是替换所有像这样的“|”


在MariaDB中,有这样一个函数,所以您可以尝试从一个基传递到另一个基。但仅使用MYSQL是不行的://p>不使用存储过程,我将分两步完成:

  • 在管道字符的第二个匹配项处插入逗号:

    updateoptions set options=insert(选项,定位(“|”),选项,定位(“|”,选项)+1),1,”,”;
    
  • 插入剩余的逗号-执行查询N次:

    updateoptions set options=insert(选项,定位(“|”),选项,定位(“|”),选项,长度(选项)-定位(“,”,反转(选项))+1)+1,“,”);
    
    在哪里=

    从选项中选择最大值(圆形(((长度(选项)-长度(替换(选项“|”),”)-1)/2)-1);
    
    (或者不必费心计算,继续执行查询,只要它不告诉您“0行受影响”)

  • 使用这组数据进行检查:

    id选项
    1 A | 10 | B | 20 | C | 30
    2 A |正| B |负
    3 A | 10 | B | 20 | C | 30 | D | 40 | E | 50 | F | 60
    4 A |正| B |负| C |中性| D |邓诺
    
    结果:

    id选项
    1 A | 10,B | 20,C | 30
    2 A |正,B |负
    3 A | 10,B | 20,C | 30,D | 40,E | 50,F | 60
    4 A |正,B |负,C |中性,D | Dunno
    


    (稍后我将提供解释)

    您可以通过创建函数来完成

    CREATE FUNCTION doiterate(str TEXT, i INT, next INT, isp TINYINT(1))
      RETURNS TEXT
      BEGIN
        myloop: LOOP
          IF next = 0 THEN
            LEAVE myloop;
          END IF;
          IF isp = TRUE THEN
            set str = insert(str, i, 1, ',');
            set isp = FALSE;
            set i = next;
            set next = locate('|', str, i + 1);
            ITERATE myloop;
          ELSE
            set isp = TRUE;
            set i = next;
            set next = locate('|', str, i + 1);
            ITERATE myloop;
          END IF;
          LEAVE myloop;
        END LOOP;
        return str;
      END;
    
    这样说:

    SELECT t.`column`,
      @loc := locate('|', t.`column`) as position,
      @next := locate('|', t.`column`, @loc +1) as next,
      @isp := 0 is_pipe,
      @r := doiterate(t.column, @loc, @next, @isp) as returnstring
    from test t;
    
    我想你会很聪明的

    • 更改表名和列名
    • 将其插入到更新请求中
    如果我得到了错误的管道/昏迷更改,您可以将@isp:=更改为1(我假设第二个管道应更改为昏迷)

    Demo

    解释

    如果MySQL有一个正则表达式替换函数,那么这个问题相对容易解决。所以-看。这里需要“高级版本”,以允许它在找到的替换匹配中执行递归替换。然后可以使用以下相对简单的SQL:

    SQL(为简洁起见省略函数代码)

    选择id,
    选项为'before`,
    reg_替换(选项,
    “\\\\..\\\\”,--2个管道符号,中间有任何文本
    “\\\$”,替换第二个管道符号
    “,”,--替换为逗号
    假,--非贪婪匹配
    2,--最小匹配长度=2(2个管道符号)
    0,--无最大匹配长度
    0,--最小子匹配长度=1(1个管道符号)
    0--最大子匹配长度=1(1个管道符号)
    )在……之后`
    来自tbl;
    
    > p>您应该考虑将数据存储在规范化模式中。在您的情况下,该表应如下所示:

    | id | k |        v |
    |----|---|----------|
    |  1 | A |       10 |
    |  1 | B |       20 |
    |  1 | C |       30 |
    |  2 | A | Positive |
    |  2 | B | Negative |
    
    这个模式更灵活,您将看到原因

    那么,如何将给定的数据转换成新的模式呢?您需要一个包含序列号的辅助表。由于您的列是
    varchar(255)
    ,因此只能在其中存储128个值(+127个分隔符)。但是让我们创建1000个数字。您可以使用任何具有足够行的表。但是,由于任何MySQL服务器都有
    information\u schema.columns
    表,所以我将使用它

    drop table if exists helper_sequence;
    create table helper_sequence (i int auto_increment primary key)
        select null as i
        from information_schema.columns c1
        join information_schema.columns c2
        limit 1000;
    
    通过连接两个表,我们将使用这些数字作为字符串中值的位置

    要从分隔字符串中提取值,可以使用
    substring\u index()
    函数。位置
    i
    处的值将为

    substring_index(substring_index(t.options, '|', i  ), '|', -1)
    
    在字符串中,有一系列键,后跟其值。钥匙的位置是奇数。因此,如果键的位置是
    i
    ,则相应值的位置将是
    i+1

    要获取字符串中分隔符的数目并限制我们的联接,我们可以使用

    char_length(t.options) - char_length(replace(t.options, '|', ''))
    
    以规范化形式存储数据的查询为:

    create table normalized_table
        select t.id
            , substring_index(substring_index(t.options, '|', i  ), '|', -1) as k
            , substring_index(substring_index(t.options, '|', i+1), '|', -1) as v
        from old_table t
        join helper_sequence s
          on s.i <= char_length(t.options) - char_length(replace(t.options, '|', ''))
        where s.i % 2 = 1
    
    那么,为什么这种格式是更好的选择呢?除了许多其他原因外,其中一个原因是您可以使用

    select id, group_concat(concat(k, '|', v) order by k separator '|') as options
    from normalized_table
    group by id;
    
    | id |               options |
    |----|-----------------------|
    |  1 |        A|10|B|20|C|30 |
    |  2 | A|Positive|B|Negative |
    
    或者按照您想要的格式

    select id, group_concat(concat(k, '|', v) order by k separator ',') as options
    from normalized_table
    group by id;
    
    | id |               options |
    |----|-----------------------|
    |  1 |        A|10,B|20,C|30 |
    |  2 | A|Positive,B|Negative |
    
    如果您不关心规范化,只希望完成此任务,那么可以使用

    update old_table o
    join (
        select id, group_concat(concat(k, '|', v) order by k separator ',') as options
        from normalized_table
        group by id
    ) n using (id)
    set o.options = n.options;
    
    然后放下
    标准化_表

    但是,您将无法使用像这样的简单查询

    select *
    from normalized_table
    where k = 'A'
    

    请参见

    因此,您想在MySQL中代替PHP执行此操作吗?是的,您明白了,我正在复制您在我的问题中的评论。请向我们显示模式。列为
    options
    ,其类型为
    varchar(255)
    好的,让我重新措辞。
    A
    来自哪里?
    10
    来自哪里?它们在不同的列中吗?您已经使用了什么查询?我已经编辑了我的答案,但很抱歉,只有在MySQL中无法做到这一点。
    | id | k |        v |
    |----|---|----------|
    |  1 | A |       10 |
    |  1 | B |       20 |
    |  1 | C |       30 |
    |  2 | A | Positive |
    |  2 | B | Negative |
    
    select id, group_concat(concat(k, '|', v) order by k separator '|') as options
    from normalized_table
    group by id;
    
    | id |               options |
    |----|-----------------------|
    |  1 |        A|10|B|20|C|30 |
    |  2 | A|Positive|B|Negative |
    
    select id, group_concat(concat(k, '|', v) order by k separator ',') as options
    from normalized_table
    group by id;
    
    | id |               options |
    |----|-----------------------|
    |  1 |        A|10,B|20,C|30 |
    |  2 | A|Positive,B|Negative |
    
    update old_table o
    join (
        select id, group_concat(concat(k, '|', v) order by k separator ',') as options
        from normalized_table
        group by id
    ) n using (id)
    set o.options = n.options;
    
    select *
    from normalized_table
    where k = 'A'