我有逗号分隔的多个值
(1,3,5)想要与(2,3,4,5,7,5)进行比较,这个集合指的是列值。所以它应该返回3和5
这个值是动态的
我用过
SELECT * FROM table WHERE FIND_IN_SET('3', ('2,3,4,5,7,5')) AND FIND_IN_SET('5', ('2,3,4,5,7,5'))
依此类推
但它非常让我知道任何更好的解决方案。
答案 0 :(得分:2)
简短回答
你应该避免这种情况。虽然它实际上可以完成,但您当前的架构至少违反了first NF。这是不好的情况。存储分隔符分隔列表仅在您需要处理整个字符串时才适用,但不适用于单独的值本身。因此,最合适的解决方案是:创建附加表并将值放在那里。
答案很长
这可以被视为某种谜题 - 但我强烈不建议在实际应用中使用它。所以,我们假设我们有表t
:
+------+------------------+ | id | col | +------+------------------+ | 1 | 1,35,61,12,8 | | 4 | 82,12,99,100,1,3 | | 6 | 35,99,1 | +------+------------------+
我们希望用字符串'1,3,35'
“交叉”我们的字符串。我假设您的字符串源自应用程序 - 因此,您可以使用它做一些准备工作。
最终SQL将如下所示:
SELECT
resulted.id,
GROUP_CONCAT(resulted.sub) AS result
FROM
(SELECT
r.id,
TRIM(BOTH ',' FROM SUBSTR(
r.col,
@cur,
LOCATE(',', r.col, @cur+1)-@cur
)) AS sub,
@cur:=IF(
CHAR_LENGTH(r.col)=LOCATE(',', r.col, @cur+1),
1,
LOCATE(',', r.col, @cur+1)
) AS cur
FROM
(SELECT
id,
CONCAT(TRIM(BOTH ',' FROM t.col), ',') AS col,
CHAR_LENGTH(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(col
, '9', '')
, '8', '')
, '7', '')
, '6', '')
, '5', '')
, '4', '')
, '3', '')
, '2', '')
, '1', '')
, '0', '')
) + 1 AS repeats
FROM t) AS r
LEFT JOIN
(SELECT
(two_1.id + two_2.id + two_4.id +
two_8.id + two_16.id) AS id
FROM
(SELECT 0 AS id UNION ALL SELECT 1 AS id) AS two_1
CROSS JOIN (SELECT 0 id UNION ALL SELECT 2 id) AS two_2
CROSS JOIN (SELECT 0 id UNION ALL SELECT 4 id) AS two_4
CROSS JOIN (SELECT 0 id UNION ALL SELECT 8 id) AS two_8
CROSS JOIN (SELECT 0 id UNION ALL SELECT 16 id) AS two_16
) AS init
ON init.id<r.repeats
CROSS JOIN
(SELECT @cur:=1) AS vars
) AS resulted
INNER JOIN
(SELECT '1' AS sub UNION ALL
SELECT '3' UNION ALL
SELECT '35'
) AS input
ON resulted.sub=input.sub
GROUP BY
resulted.id
(演示可用here)。
工作原理
有一些技巧,用于此SQL。首先,迭代变量。 MySQL支持user-defined variables,它们可用于查询中的某种迭代。我们正在使用它将有效的偏移量和长度传递给我们的字符串 - 通过SUBSTR()
得到它。
下一招:我们需要产生一定数量的行 - 否则迭代将无效。这可以通过以下方式完成:计算每行中的分隔符并使用该计数+ 1重复它。 MySQL没有序列,但还有第三个技巧:通过巨大的CROSS JOIN
创建所需的计数(使用2
的幂的总和来获得连续的数字)。这就是内部LEFT JOIN
的内容。事实上,我在one的问题中遇到了这个问题。
最后,我们对整个结果进行INNER JOIN
以获取相交的值。注意:这是你需要为你的字符串做一些准备的部分。但是在应用程序中拆分字符串很容易,需要UNION ALL
部分查询。
问题是什么
'1,,,,4,5'
之类的内容进行检查。真的 - 这不是这种方法的意图0..9
(那个巨大的REPLACE
部分) - 我们不能动态地做到这一点 - MySQL不能“替换任何字符,除了......”这是一个瓶颈,是的 - 但是,再次 - 不是方法的意图答案 1 :(得分:1)
虽然我不建议在实时代码中执行此操作,但无需变量即可完成: -
SELECT id, some_col, GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX('1,3,5', ',', AnInt), ',', -1) ORDER BY 1) AS anItem
FROM some_table
CROSS JOIN
(
SELECT 1 + Units.i + Tens.i * 10 as AnInt
FROM
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Units,
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Tens
) Sub1
WHERE FIND_IN_SET(SUBSTRING_INDEX(SUBSTRING_INDEX('1,3,5', ',', AnInt), ',', -1), some_col)
GROUP BY id, some_col
这样做是选择0到9联合,并将其加入到自身。这得到100个组合,并通过一点乘法得到数字0到100.然后将其与要检查的表交叉连接,并使用此数字作为SUBSTRING_INDEX的参数将其拆分为逗号。因此,它可以处理您要检查的逗号分隔字符串中的~100个数字。缺点是它会复制其中一些数字,因此需要删除重复数据。
结果数字随后可与FIND_IN_SET()一起使用,以检查逗号分隔字段中包含这些数字的行。
然后我使用带有DISTINCT的GROUP_CONCAT来显示该行的匹配数字。
这里的SQL小提琴: -