【问题标题】:update a column based on a value, php mysql根据值更新列,php mysql
【发布时间】:2015-01-17 11:29:20
【问题描述】:

我有一个下表,我的问题是如何根据参数更新 mysql 中的列。

+---+------------+-------------+
| id|     A      |      B      |
+---+------------+-------------+
| 1 | a b a a    |             |
| 2 | b c a      |             |
| 3 | b d c      |             |
| 4 | a          |             |
+---+------------+-------------+

预期结果应计算“a”出现的次数,然后更新列 B。如下所示。当然我需要使用正则表达式来计算“a”的数量

+---+------------+-------------+
|   |     A      |      B      |
+---+------------+-------------+
| 1 | a b a a    |      3      |
| 2 | b c a      |      1      |
| 3 | b d c      |      0      |
| 4 | a          |      1      |
+---+------------+-------------+

【问题讨论】:

标签: php mysql


【解决方案1】:

这可以在单个 SQL 语句中完成。 (不幸的是,这种方法不满足使用正则表达式的要求。编辑:请参阅下面的后续内容,了解使用正则表达式的不优雅和低效的方法。)

我们可以获取A 列中字符“a”的出现次数,并将B 列设置为计数,查询如下:

UPDATE mytable t
   SET t.B = ( CHAR_LENGTH(t.A) - CHAR_LENGTH(REPLACE(t.A,'a','')) )

让我们稍微解开一下。这是一个UPDATE 语句,没有WHERE 子句,所以我们将访问并可能更新mytable 中的每一行。 (我们正在分配一个表别名 t。这不是必需的,但我们将使用该别名限定我们以后对列 AB 的引用,以使阅读 SQL 语句的人更清楚那些是对表中列的引用。

在下一行,我们有一个简单的SET 子句,为列B 赋值。

这是下一个表达式,我们分配给列 B 的值,我们应该稍微解压。

要计算'a' 字符的数量,我们可以使用一个小技巧:我们知道'a' 字符的长度正好是一个字符。

“诀窍”是使用REPLACE 函数,搜索所有出现的字符'a',并删除它们(用零长度字符串替换它们)。然后我们可以比较两个字符串的长度(字符数)。区别在于原始字符串中'a' 字符的数量。

作为这些表达式如何工作的演示:

SELECT t.foo                          AS foo
     , REPLACE(t.foo,'a','')          AS foo_2
     , CHAR_LENGTH(t.foo)                  AS len
     , CHAR_LENGTH(REPLACE(t.foo,'a',''))  AS len_2
     , CHAR_LENGTH(t.foo) - CHAR_LENGTH(REPLACE(t.foo,'a','')) AS `len-len_2`
  FROM ( SELECT 'a b a a' AS foo
         UNION ALL SELECT 'b c a'
         UNION ALL SELECT 'b c d'
         UNION ALL SELECT 'a  '
       ) t

从那个查询返回:

foo      foo_2      len   len_2  len-len_2  
-------  ------  ------  ------  ---------
a b a a   b           7       4          3
b c a    b c          5       4          1
b c d    b c d        5       5          0
a                     3       2          1

注意:返回的内容基本上是对已删除字符数的计数。因此,如果我们想计算一个包含多个字符的字符串的出现次数,例如:cat,我们需要考虑这一点。

将返回值除以cat 中的字符数是一种方法。或者,我们可以将字符串 cat 替换为长度为两个字符的字符串,例如'xx',因此每次出现一个字符的长度差异。


跟进

最初的问题询问如何使用正则表达式计算“a”字符。我的第一个想法是 MySQL REGEXP 不可能,因为它的返回值要么是 NULL,要么是 0,要么是 1。但是稍微考虑一下,如果我们数到某个有限的数字,它是可以做到的的发生。可以检查一个字符串是否至少包含一个 'a' 字符,这非常简单:

'a b a a' REGEXP 'a'

如果匹配则返回 1,否则返回 0。还可以检查字符串是否包含至少两个“a”字符。这也很简单:

'a b a a' REGEXP 'a.*a'

如果我们将上面两个表达式的结果相加,我们可以得到 0、1 或 2 的“a”字符数。

我们可以重复相同的模式,将其扩展到匹配 3、4、5 等 'a' 字符。

这并不优雅,我们当然不想知道 CPU 在进行所有这些比较时会变得多热。但它确实返回指定的结果,最多为某个有限的最大计数。在这个例子中,六个。 (包含超过 6 个 'a' 字符的字符串将返回 6。

作为演示:

SELECT t.foo
     ,   (t.foo REGEXP CONCAT('.*',REPEAT('a.*',1)))
       + (t.foo REGEXP CONCAT('.*',REPEAT('a.*',2)))
       + (t.foo REGEXP CONCAT('.*',REPEAT('a.*',3)))
       + (t.foo REGEXP CONCAT('.*',REPEAT('a.*',4)))
       + (t.foo REGEXP CONCAT('.*',REPEAT('a.*',5)))
       + (t.foo REGEXP CONCAT('.*',REPEAT('a.*',6)))
       AS cnt_a
  FROM ( SELECT 'a b a a' AS foo
         UNION ALL SELECT 'b c a'
         UNION ALL SELECT 'b c d'
         UNION ALL SELECT 'a  '
       ) t

【讨论】:

    【解决方案2】:

    选项 1:纯 SQL

    将要计数的子字符串替换为空字符串。通过比较结果字符串和原始字符串的长度,您可以知道有多少次出现:

    update table set b = (length(a) - length(replace(a,'a',''))) / length('a')
    

    您可以用任意长度的任意字符串替换常量字符串'a'

    选项 2:使用 PHP 和 SQL

    您可以使用 PHP 遍历所有行并使用 substr_count 函数来计算子字符串的出现次数(在您的情况下,子字符串将是 'a')。然后更新该行中b 的值。假设字段id 是您的主键:

    $query = $pdo->query("select id,a from table");
    while($row = $query->fetch()) {
        $b = substr_count($row['a']);
        $id = $row['id'];
        $pdo->query("update table set b = $b where id = $id");
    }
    

    请注意,这种方法效率不高。

    【讨论】:

    • 我们为什么要给出使用不推荐使用的mysql接口的例子,不是已经有足够的例子了吗?为什么我们不给出使用 mysqli 或 PDO 的示例?我们最好希望id 是唯一的,它是整数类型而不是VARCHAR,以防它包含像'' OR 1=1 这样的值。从表中获取各个行并发出单独的更新语句是解决问题的非常低效的方法。
    • 你是对的,谢谢你的评论。我修改了我的答案。我列出了 PHP 选项,因为它是一种可能的方法,效率不高,但正如你所说的那样有效。
    • 转换为 PDO 很好。理想情况下,我们希望看到$b$id 作为绑定参数提供给准备好的语句;这是我们习惯看到的模式。我们仍然没有在问题中看到任何保证 id 列被定义为非空(PHP 代码将错过更新这些行),它是唯一的(否则,PHP 代码将多次更新多行),也就是说它没有被定义为字符类型并且不包含不安全的值(我们需要将 $id 值用单引号括起来,并正确地转义字符串以使其安全地包含在 SQL 文本中。
    • 我很欣赏你的准确,但我认为你太准确了。首先在我的回答中我说Assuming that the field id is your primary key。这意味着id 不能为空。其次,这不是要给他一个完美的工作答案,而是要给出如何解决问题的提示。其余的大家自己想办法。
    【解决方案3】:
    UPDATE Table SET B = i.b
    FROM (SELECT LENGTH(A) - LENGTH(REPLACE(A, 'a', '')) as b from Table ) i
    WHERE i.ID = Table.ID
    

    【讨论】:

    • 这是一个不错的方法,但实现存在一些问题:内联视图不返回名为id 的列;它不计算a 字符的数量,除此之外,实际上不需要内联视图。
    • 非常好的主意,但它只有在要计数的子字符串长度为 1 时才有效。
    猜你喜欢
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多