我有一个带有 VARCHAR 列
VISITOR_ID
的 MySQL 表,用于保存多个或单个访问者 ID 值。我希望 ENTERED_TEXT 具有一对多关系,并且不想为与 VISITOR_ID 的关系创建多行 - 因此我选择将多个 ID 添加到 VISITOR_ID 列。我现在想在该列中搜索多个相关 ID。
| VISITOR_ID | ENTERED_TEXT |
------------------------------------------
| 123,133,777 | text text |
| 555 | text text text |
| 444,133,777 | text |
| 999 | text text text text text |
当我尝试使用带有 IN() 的选择时
SELECT *
FROM `My_Table`
WHERE
VISITOR_ID IN(444,777)
我只返回一行并出错:
| 444,133,777 |
警告:#1292 截断不正确的 DOUBLE 值'123,133,777'
我预计这两行会返回:
| 444,133,777 |
| 123,133,777 |
还:
如果我使用:
SELECT *
FROM `My_Table`
WHERE
VISITOR_ID IN(555,999)
我取回其中包含单个值的 2 行:
| 555 |
| 999 |
我想我明白我的 IN() 子句没有将逗号分隔值视为单独的值,而是像这样“123,133,777” - 但我如何将这些值读取为适合我的 IN 的单独值() 搜索?
我试图更改列类型,以及拆分值的各种方法。不确定最好的方法是什么。 我也知道我可以通过为 VISITOR_ID 使用单独的行来解决这个问题——我正在考虑。只是我认为让这些多个 ID 共享用户输入的相似的单个文本值是有意义的。好像1条记录比多条记录好?不确定这是要走的路。
FIND_IN_SET()
功能 可以做你想做的事,但这不是它的预期用途。它应该用于搜索 MySQL 的SET
数据类型. 的值
实际上,它搜索逗号分隔的字符串并返回在列表中找到您的值的位置。
mysql> select find_in_set(777, '123,133,777') as loc;
+-----+
| loc |
+-----+
| 3 |
+-----+
它有缺点:它一次只能搜索一个值,因此要搜索多个值,您必须使用多个搜索词:
mysql> select find_in_set(777, '123,133,777') or find_in_set(444, '123,133,777') as loc;
它也破坏了使用索引的任何机会。这意味着您的查询将具有非常糟糕的性能,因为它们必须进行表扫描。
对空间也很挑剔。如果您有一个逗号分隔的字符串,其中包含
'123, 133, 777'
这样的空格,它根本不会按您期望的方式工作,因为它只会在您搜索包含 ' 777'
. 这样的空格的字符串时匹配
您最好针对一对多关系正确规范化数据(正如 GMB 上面的评论所说)并每行存储一个值。那么搜索的代码就简单多了,它可以利用索引来优化查询。
这是为什么我们不鼓励人们在字符串中存储逗号分隔列表的众多原因之一。
这是一种通过使用
json_table
将逗号分隔的列拆分为行然后将条件应用于这些生成的行的方法:
with cte as (
select t.VISITOR_ID, j.VISITOR
from mytable t
join json_table(
CONCAT('[', t.VISITOR_ID, ']'),
'$[*]' columns (VISITOR BIGINT(20) path '$')
) j
)
select distinct VISITOR_ID
from cte
where VISITOR IN(444,777);