如果字段是字符串的子序列,则更新行

问题描述

我有一个字符串 S = "1-2-3-4-5-6-7-8"

这是我的数据库表行的样子:

id 子序列
1 1-2-4-5
2 1-3-4-5
3 2-5-7-8
4 5-8-9-10
5 6-7-10-11

等等......

我想编写一个仅更新(在本例中)前 3 行的查询,因为它们是字符串 S 的子序列。

我目前的解决方案是以编程方式遍历每一行,检查它是否是一个子序列,然后更新。但我想知道是否有一种方法可以在 MysqL 级别执行此操作以提高性能

更新:我不介意改变数据的存储方式。例如,字符串 S 可以是保存这些数字的数组,而“子序列”列可以将这些数字保存为数组。

解决方法

不,当您将子序列存储为字符串时,没有办法在 SQL 中以良好的性能执行您描述的查询。原因是做子串比较不能用索引优化,所以你的查询将被迫逐行进行比较。

一般来说,当您尝试将一组值存储为字符串,但又想使用 SQL 将它们视为离散值时,这必然会很笨拙,难以编码,最终性能也会很差。

在这种情况下,我要做的是制作两个表,一个为实体编号,另一个表中,子序列中的每个值都单独存储在一行中。

子序列:

id
1
2

子序列元素:

id 子序列元素
1 1
1 2
1 4
1 5
2 1
2 3
2 4
2 5

等等。

然后您可以使用 技术来查找此集合的每个元素都存在于您要与之进行比较的集合中的情况。

这是一个例子:

SELECT s.id
FROM SubSequences AS s
LEFT OUTER JOIN (
    SELECT id
    FROM SubSequenceElements
    WHERE SubSequenceElement NOT IN (1,2,3,4,5,6,7,8)
  ) AS invalid USING (id)
WHERE invalid.id IS NULL;

换句话说,您希望从 SubSequences 返回行,以便在 SubSequenceElements 中找不到匹配的元素值不在您尝试匹配的集合中。

这有点令人困惑,因为你必须考虑这个问题是一个双重不匹配的问题。但是一旦你得到了关系划分,它就会非常强大。

,

如果该集合可以用数字 0 到 63(或其中的某个子集)表示,那么...

使用这样的列

elements BIGINT UNSIGNED NOT NULL DEFAULT '0'

然后可以将“2-5-7-8”放入其中:

UPDATE ...
    SET elements = (1<<2) | (1<<5) | (1<<7) | (1<<8);

然后可以在一个表达式中完成各种操作:

WHERE elements = (1<<2) | (1<<5) | (1<<7) | (1<<8)  -- Test for exactly that set

WHERE (elements ^ ~ ( (1<<2) | (1<<5) | (1<<7) | (1<<8) )) != 0
    -- checks to see if any other bits are turned on

最后一个示例接近您的需要。 “and not”的一侧会有你的例子的 1..8,另一侧会有

您的示例将 S 表示为 0x1FE;

WHERE subsequence & ~0x1FE

对于 ids 1,3 将是 0(假); id 4 和 5 的非零(真)。