在 PHP 中删除逗号分隔字符串中的值

Posted

技术标签:

【中文标题】在 PHP 中删除逗号分隔字符串中的值【英文标题】:delete a value in comma delimited string in PHP 【发布时间】:2021-05-14 02:44:22 【问题描述】:

我在 SQL Server 中有如下表:

+--------------+----------------------------------------------+
| reportName   | ShareWith                                    |   
+--------------+----------------------------------------------+
| IBM SH  data | jack@gmail.com,alex@gmail.com,jan@gmail.com  |  
| Samsung Sr   | alex@gmail.com,peter@gmail.com               |
| Xiaomi MFG   |                                              |
| Apple US st  | maria@gmail.com,alex@gmail.com               |
| LG EU        | fred@gmail.com                               |
+--------------+----------------------------------------------+

在我的 php 文件中,我有一个输入文本和一个按钮。当用户键入报告名称并单击按钮时,tt 将执行 ajax 调用以从所选报告中删除当前用户的电子邮件。

在 SQL 中应该如下:

select shareWith, UpdatedshareWith = 
  case
    when shareWith like 'alex@gmail.com,%'   then REPLACE(shareWith, 'alex@gmail.com,', '')
    when shareWith like '%,alex@gmail.com,%' then REPLACE(shareWith, ',alex@gmail.com,', ',')
    when shareWith like '%,alex@gmail.com'   then REPLACE(shareWith, ',alex@gmail.com', '')
    when shareWith = 'alex@gmail.com' then ''
    else shareWith
end
from  table
where reportName = 'xxxx';

我正在尝试在 PHP 中动态应用它,但无法使其工作。

$('#button').on('click', function()
  
  var reportName = x;
  var username = y;

  $.ajax(
    type: "POST",
    url: "delete.php",
    data:  reportName : reportName,
    username : username ,
    success: function(data)  console.log(data); 
  );

);

delete.php如下:

$stmt = $conn->prepare("UPDATE table SET shareWith = CASE 
   WHEN shareWith like '?,%' THEN REPLACE(shareWith, '?,', '')
   WHEN shareWith like '%,?,%' THEN REPLACE(shareWith, ',?,', ',')
   WHEN shareWith like '%,?' THEN REPLACE(shareWith, ',?', '')
   ELSE shareWith
   END
   WHERE reportName = ?");

$stmt->execute([$_POST['username'], $_POST['username'], $_POST['username'],
$_POST['username'], $_POST['username'], $_POST['username'], $_POST['reportName']]);

echo json_encode('deleted');

我相信有一种更清洁的方法可以做到这一点。任何建议请我应该在我的代码中更改什么?非常感谢。

【问题讨论】:

规范化架构。见"Is storing a delimited list in a database column really that bad?"。 感谢您的评论。我知道这是一种不好的做法,但该表是由另一个团队创建的,他们以这种方式使用它,我无法更改。 SQL Server 版本是多少? @Zhorov SQL Server 2012 版本 11.0 @DevTN,如果您使用的是 SQL Server 2017+,一个可能的选项是使用 STRING_SPLIT() 拆分 ShareWith 列,然后使用 STRING_AGG() 再次聚合结果。请注意,TRIM() 也在 SQL Server 2017 中引入,因此您需要更改@Gordon Linoff 答案中的代码。 【参考方案1】:

您迫切需要修复架构。在一个字符串中存储多个值是错误的。但有时我们会被其他人非常非常非常糟糕的决定所困扰。

我会建议这种结构:

UPDATE table
    SET shareWith = TRIM(',' FROM REPLACE(CONCAT(',', ShareWIth, ','), CONCAT(',', ?, ',')) )
    WHERE reportName = ? AND
         CONCAT(',', shareWith, ',') LIKE CONCAT('%,', ?, ',%');

也就是说,通过在搜索字符串和模式周围放置分隔符来简化逻辑。

但是,我要重复一遍:您应该有一个表格,每个报告和份额都有一个值。

您可以使用空格和TRIM()REPLACE(),而不是更灵活的TRIM()

    SET shareWith = REPLACE(TRIM(REPLACE(REPLACE(CONCAT(',', ShareWIth, ','), CONCAT(',', ?, ','))), ',', ' '), ' ', ',')

这假定字符串没有空格,这对于字符串中的电子邮件是有意义的。

【讨论】:

感谢您的回答。不幸的是,我无法更改表结构,因为它是由另一个团队创建的,他们正在使用那个。请问一个问题,我不明白您的查询“',' FROM REPLACE”是什么意思,因为我收到了不正确的语法错误。有什么遗漏吗? @DevTN 。 . .那就是 SQL Server 2017 中引入的TRIM() 功能:docs.microsoft.com/en-us/sql/t-sql/functions/…。 谢谢。我正在阅读它,但我的意思是你如何一起使用关键字 FROM REPLACE.. 我还需要一个替代解决方案,因为在工作中我们都在使用 SQL Server 2012【参考方案2】:

请尝试以下解决方案。

它将从 SQL Server 2012 开始工作。

它使用 XML 和 XQuery 来标记电子邮件列表,并删除不需要的电子邮件。您可以将其打包为带有两个参数的存储过程,@reportName@emailToRemove

CTE 完成所有繁重的工作:

    将电子邮件列表转换为 XML 数据类型。 使用 XQuery 消除不需要的电子邮件。 转换回逗号分隔的电子邮件列表。

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, reportName VARCHAR(30), ShareWith VARCHAR(1024));
INSERT INTO @tbl (reportName, ShareWith) VALUES
('IBM SH  data', 'jack@gmail.com,alex@gmail.com,jan@gmail.com'), 
('Samsung Sr', 'alex@gmail.com,peter@gmail.com'),
('Xiaomi MFG', ''),
('Apple US st', 'maria@gmail.com,alex@gmail.com'),
('LG EU', 'fred@gmail.com');
-- DDL and sample data population, end

DECLARE @reportName VARCHAR(30) = 'IBM SH  data'
    , @emailToRemove varchar(30) = 'alex@gmail.com'
    , @separator CHAR(1) = ',';

;WITH rs AS
(
    SELECT *
        , REPLACE(TRY_CAST('<root><r><![CDATA[' + 
                 REPLACE(ShareWith, @separator, ']]></r><r><![CDATA[') + ']]></r></root>' AS XML)
                 .query('
                 for $x in /root/r[lower-case((./text())[1]) ne lower-case(sql:variable("@emailToRemove"))]
                 return data($x)
                 ').value('.', 'VARCHAR(MAX)'), SPACE(1), @separator) AS modifiedShareWith
    FROM @tbl
    WHERE reportName = @reportName
)
UPDATE t
SET ShareWith = rs.modifiedShareWith
FROM @tbl AS t
    INNER JOIN rs ON t.id = rs.id;

 -- test
 SELECT * FROM @tbl;
 

输出

+----+--------------+--------------------------------+
| ID |  reportName  |           ShareWith            |
+----+--------------+--------------------------------+
|  1 | IBM SH  data | jack@gmail.com,jan@gmail.com   |
|  2 | Samsung Sr   | alex@gmail.com,peter@gmail.com |
|  3 | Xiaomi MFG   |                                |
|  4 | Apple US st  | maria@gmail.com,alex@gmail.com |
|  5 | LG EU        | fred@gmail.com                 |
+----+--------------+--------------------------------+

【讨论】:

以上是关于在 PHP 中删除逗号分隔字符串中的值的主要内容,如果未能解决你的问题,请参考以下文章

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

在 React Native 中呈现逗号分隔的值字符串

无法解析逗号分隔的字符串数量。最初带有货币符号

SQL语句,字段表里某列数据变为用逗号分隔的字符串

用PHP检查逗号分隔的字符串中是不是存在值[重复]

sql 如何以逗号为分隔符分割一个字段的值