【问题标题】:Remove values in comma separated list from database从数据库中删除逗号分隔列表中的值
【发布时间】:2025-12-23 07:30:11
【问题描述】:

我的 MySQL 数据库中有一个名为“children”的表。在该表中有一行称为“愿望”(以逗号分隔的孩子的愿望清单项目)。我需要能够更新该列表,以便它只删除一个值。即列表 = 12 号普通牛仔裤、冲浪板、红袜队棒球帽;我想移除 Surfboard。

我现在的查询是这样的

$select = mysql_query('SELECT * FROM children WHERE caseNumber="'.$caseNum.'" LIMIT 1 ');
    $row = mysql_fetch_array($select);

    foreach ($wish as $w) {
        $allWishes = $row['wishes'];
        $newWishes = str_replace($w, '', $allWishes);
        $update = mysql_query("UPDATE children SET wishes='$newWishes' WHERE caseNum='".$caseNum."'");
}

但是 UPDATE 查询并没有删除任何内容。我该怎么做?

【问题讨论】:

  • 我认为希望应该是它自己的表,并通过共享关键 caseNumber 链接回此处。
  • 看起来缺少 1 个字符串连接,但一旦排序,您就会将此查询留给 SQL 注入 - 黑客真的会得到他们想要的。
  • @Daren - 这确实是正确的答案,数据库模型是错误的,需要修复。可惜对 cme​​ts 的赞成票不算数
  • 我希望它是自己的桌子,但它需要这样。
  • @sun-tzu 也许你已经解决了这个问题,但我为我的答案添加了另一个可能的解决方案。

标签: php mysql sql database


【解决方案1】:

使用these user-defined REGEXP_REPLACE() functions,或许可以将其替换为空字符串:

UPDATE children SET wishes = REGEXP_REPLACE(wishes, '(,(\s)?)?Surfboard', '') WHERE caseNum='whatever';

不幸的是,您不能只使用普通的旧REPLACE(),因为您不知道字符串“Surfboard”出现在哪里。事实上,如果 'Surfboard' 出现在开头或结尾,上面的正则表达式可能需要额外的调整。

也许您可以像这样修剪掉前导和尾随逗号:

UPDATE children SET wishes = TRIM(BOTH ',' FROM REGEXP_REPLACE(wishes, '(,(\s)?)?Surfboard', '')) WHERE caseNum='whatever';

那么这里发生了什么?正则表达式删除了“冲浪板”以及它前面的可选逗号和空格。然后周围的TRIM() 函数消除了可能的前导逗号,以防“冲浪板”出现在字符串的开头。这可能也可以由正则表达式处理,但坦率地说,我太累了,无法弄清楚。

注意,我自己从未使用过这些,也不能保证它们的有效性或稳健性,但这是一个开始的地方。而且,正如其他人在 cmets 中提到的那样,您确实应该将它们放在规范化的愿望清单中,而不是作为逗号分隔的字符串。

更新

再想一想,我更倾向于只强制使用内置的REPLACE(),然后清除可能会连续出现两个逗号的多余逗号。这是并排寻找两个逗号,就好像没有空格分隔您的原始列表项。如果项目已用逗号和空格分隔,请在外部 REPLACE() 调用中将 ',,' 更改为 ', ,'

UPDATE children SET wishes = TRIM(BOTH ',' FROM REPLACE(REPLACE(wishes, 'Surfboard', ''), ',,', ',')) WHERE caseNum='whatever';

【讨论】:

    【解决方案2】:

    不完全是您问题的直接答案,但就像达伦所说,最好将愿望作为自己的桌子。也许您可以更改您的数据库架构,以便拥有 3 个表,例如:

    children
    -> caseNum
    -> childName
    
    wishes
    -> caseNum
    -> wishId
    -> wishName
    
    childrensWishes
    -> caseNum
    -> wishId
    

    然后要添加或删除一个孩子的愿望,您只需在childrensWishes 中添加或删除相关行。您当前的设计使其难以操作(正如您所发现的那样),并且使您面临数据不一致的风险。

    作为更直接的答案,您可以通过获取愿望列表、explode() 来修复当前的方式,从数组中删除您不想要的,然后 implode() 将其返回到更新数据库的字符串。

    【讨论】:

      【解决方案3】:

      使愿望表具有这种格式:

      caseNumber,wish
      

      然后你得到一个孩子的所有愿望是这样的:

      SELECT * FROM children c left join wishes w on c.caseNumber = w.caseNumber WHERE c.caseNumber= ?
      

      删除一个愿望变成:

      DELETE from wishes where caseNumber = ?
      

      添加愿望变成:

      INSERT into wishes (caseNumber,wish) values (?,?)
      

      返回一个愿望变成:

      SELECT * FROM children c left join wishes w on c.caseNumber = w.caseNumber WHERE c.caseNumber= ? LIMIT 1
      

      【讨论】:

        【解决方案4】:

        将愿望编入一个随后序列化的数组中可能是一个想法,否则您需要检索字符串,对其进行切片,删除您不想要的部分,然后连接剩余部分。这可以通过使用explode() 函数来完成。

        如果您要使用数组,您将检索该数组,然后使用这样的循环对其进行排序:

        // Wishes array:
        // Array (
        //      [0] Regular Jeans
        //      [1] Surfboard
        //      [2] Red Sox Baseball Cap
        // )
        
        $wishes = $row['wishes']; // This is a serialized array taken from the database
        $wishes = unserialize($wishes);
        
        foreach ($wishes as $key => $value) {
            if ($value == 'Surfboard') {
                unset($wishes[$key]);
                break;
            }
        }
        
        $wishes = serialize($wishes);
        // Update database
        

        请记住,索引 [1] 现在将不存在于数组中,因此如果您希望拥有一个干净的数组,您应该遍历数组并使其自己创建一个新数组:

        foreach ($wishes as $wishes) {
            $newArray[] = $wishes;
        }
        

        【讨论】:

          【解决方案5】:

          我认为此类问题的最佳答案在这里 The best way to remove value from SET field?

          查询应该是这样的,它涵盖 ,value 或 value,或仅包含逗号分隔列中的 value

          更新你的表 放 类别 = 修剪(两个','从 代替( REPLACE(CONCAT(',',REPLACE(col, ',', ',,'), ','),',2,', ''), ',,', ',') ) 在哪里 FIND_IN_SET('2', 类别)

          在这里,您可以在 where 子句中设置您的条件。更多详情请参考以上链接。

          【讨论】:

            【解决方案6】:

            你可以像这样创建函数:

            CREATE  FUNCTION `remove_from_set`(v int,lst longtext) RETURNS longtext CHARSET utf8
            BEGIN
            set @lst=REPLACE(@lst, ',,', ',');
            set @lng=LENGTH(@lst) - LENGTH(REPLACE(@lst, ',', ''))+1;
            set @p=find_in_set(@v,@lst);
            set @l=SUBSTRING_INDEX( @lst, ',', @p-1);
            set @r=SUBSTRING_INDEX( @lst, ',', @p-@lng);
            IF @l!='' AND @r!='' THEN
                return CONCAT(@l,',',@r);
            ELSE
                RETURN CONCAT(@l,'',@r);
            END IF;
            END
            

            使用:

            SELECT remove_from_set('1,,2,3,4,5,6',1)
            

            【讨论】: