简体   繁体   English

如何使用IN,FIND_IN_SET比较逗号分隔字符串中的多个值?

[英]How to compare multiple value in comma separated string using IN, FIND_IN_SET?

I have multiple values in comma separated 我有逗号分隔的多个值

(1,3,5) want to compare with (2,3,4,5,7,5) and this set refer to column value. (1,3,5)想与(2,3,4,5,7,5)进行比较,这个集合指的是列值。 So it should return 3 and 5 所以它应该返回3和5

and this values are dynamic 这个值是动态的

I have used 我用过

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')) and so on 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'))等等

but it very tedius let me know any better solution for this. 但它非常tedius让我知道任何更好的解决方案。

Short answer 简短的回答

You should avoid this. 你应该避免这种情况。 While it actually can be done, your current architecture is violating at least first NF . 虽然它实际上可以完成,但您当前的架构至少违反了第一个NF And that's bad case. 这是不好的情况。 Storing delimiter-separated list is applicable only if you need to work with entire string, but not separate value itself. 存储分隔符分隔列表仅在您需要处理整个字符串时才适用,但不适用于单独的值本身。 Therefore, most proper solution would be: create additional table and put your values there. 因此,最合适的解决方案是:创建附加表并将值放在那里。

Long answer 答案很长

This can be treated as some sort of puzzle - but I strongly do not recommend to use it on real application. 这可以被视为某种谜题 - 但我强烈建议不要在实际应用中使用它。 So, let's suppose we have table t : 所以,我们假设我们有表t

+------+------------------+
| id   | col              |
+------+------------------+
|    1 | 1,35,61,12,8     |
|    4 | 82,12,99,100,1,3 |
|    6 | 35,99,1          |
+------+------------------+

And we want to 'intersect' our strings with string '1,3,35' . 我们希望用字符串'1,3,35'我们的字符串'1,3,35' I assume that your string is derived from application - therefore, you're able to do some preparations with it. 我假设你的字符串是从应用程序派生的 - 因此,你可以用它做一些准备工作。

Final SQL will look like: 最终的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

(the demo is available here ). 这里有演示版)。

How it works 这个怎么运作

There are some tricks, that were used for this SQL. 有一些技巧,用于此SQL。 First, iteration variable. 首先,迭代变量。 MySQL supports user-defined variables and they can be used for some sort of iterations in queries. MySQL支持用户定义的变量 ,它们可用于查询中的某种迭代。 And we're using it to pass valid offset and length into our string - to get piece of it via SUBSTR() . 我们正在使用它将有效的偏移量和长度传递给我们的字符串 - 通过SUBSTR()得到它的一部分。

Next trick: we need to produce certain amount of rows - otherwise iteration won't work. 下一招:我们需要产生一定数量的行 - 否则迭代将无效。 That can be done the following way: count delimiters in each row and repeat it with that count+1. 这可以通过以下方式完成:计算每行中的分隔符并使用该计数+ 1重复它。 MySQL has no sequences, but there is third trick: to create desired count via huge CROSS JOIN (with summation of powers of 2 to get consecutive numbers). MySQL没有序列,但还有第三个技巧:通过巨大的CROSS JOIN创建所需的计数(功率总和为2以得到连续的数字)。 And that's for what internal LEFT JOIN is. 那是内部LEFT JOIN内容。 In fact, I've faced this issue in one of my questions. 事实上,我在一个问题中遇到过这个问题。

And, finally, we're doing INNER JOIN on entire result to get our intersected values. 最后,我们对整个结果进行INNER JOIN以获得相交的值。 Note: this is the part, for which you'll need to make some preparations on your string. 注意:这是你需要为你的字符串做一些准备的部分。 But it's easy to split string in application, getting needed UNION ALL part of query above. 但是在应用程序中拆分字符串很容易,在上面获取所需的UNION ALL部分查询。

What is out of the issue 什么是不可能的

  • Invalid strings. 无效的字符串。 No checks will be done for things like '1,,,,4,5' . 不会对'1,,,,4,5'这样的事情进行检查。 Really - it's not an intention of this method 真的 - 这不是这种方法的意图
  • Invalid non-numeric values. 无效的非数字值。 Since we're replacing 0..9 (that huge REPLACE part) - we can't do that dynamically - MySQL can't "replace any char, except.." This is a bottleneck, yes - but, again - not intention of the method 因为我们正在替换0..9 (那个巨大的REPLACE部分) - 我们不能动态地做到这一点 - MySQL不能“替换任何字符,除了......”这是一个瓶颈,是的 - 但是,再次 - 不是意图方法

While I wouldn't recommend doing this in live code, it can be done without the need for variables:- 虽然我不建议在实时代码中执行此操作,但可以在不需要变量的情况下完成: -

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

What this is doing is selecting 0 to 9 unioned, and joining this against itself. 这样做是选择0到9联合,并加入它自己。 This gets 100 combinations, and by a bit of multiplication it gets the numbers 0 to 100. It then cross joins this against the table ou want to check, and uses this number as a parameter to SUBSTRING_INDEX to split it up on the commas. 这得到100个组合,并通过一点乘法得到数字0到100.然后将其与要检查的表交叉连接,并使用此数字作为SUBSTRING_INDEX的参数将其拆分为逗号。 As such it can cope with ~100 numbers in the comma separated string you want to check. 因此,它可以处理您要检查的逗号分隔字符串中的~100个数字。 Down side is that it will duplicate some of these numbers, hence duplicates need to be removed. 缺点是它会复制其中一些数字,因此需要删除重复数据。

The resulting numbers can then be used with FIND_IN_SET() to check the rows that contain these numbers in their comma separated field. 然后,结果数字可以与FIND_IN_SET()一起使用,以在逗号分隔字段中检查包含这些数字的行。

I have then used GROUP_CONCAT with DISTINCT to display the matching numbers for that row. 然后我使用带有DISTINCT的GROUP_CONCAT来显示该行的匹配数字。

SQL Fiddle for it here:- 这里的SQL小提琴: -

http://www.sqlfiddle.com/#!2/edf97/3 http://www.sqlfiddle.com/#!2/edf97/3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM