繁体   English   中英

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

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

我有逗号分隔的多个值

(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'))等等

但它非常tedius让我知道任何更好的解决方案。

简短的回答

你应该避免这种情况。 虽然它实际上可以完成,但您当前的架构至少违反了第一个NF 这是不好的情况。 存储分隔符分隔列表仅在您需要处理整个字符串时才适用,但不适用于单独的值本身。 因此,最合适的解决方案是:创建附加表并将值放在那里。

答案很长

这可以被视为某种谜题 - 但我强烈建议不要在实际应用中使用它。 所以,我们假设我们有表t

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

我们希望用字符串'1,3,35'我们的字符串'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

这里有演示版)。

这个怎么运作

有一些技巧,用于此SQL。 首先,迭代变量。 MySQL支持用户定义的变量 ,它们可用于查询中的某种迭代。 我们正在使用它将有效的偏移量和长度传递给我们的字符串 - 通过SUBSTR()得到它的一部分。

下一招:我们需要产生一定数量的行 - 否则迭代将无效。 这可以通过以下方式完成:计算每行中的分隔符并使用该计数+ 1重复它。 MySQL没有序列,但还有第三个技巧:通过巨大的CROSS JOIN创建所需的计数(功率总和为2以得到连续的数字)。 那是内部LEFT JOIN内容。 事实上,我在一个问题中遇到过这个问题。

最后,我们对整个结果进行INNER JOIN以获得相交的值。 注意:这是你需要为你的字符串做一些准备的部分。 但是在应用程序中拆分字符串很容易,在上面获取所需的UNION ALL部分查询。

什么是不可能的

  • 无效的字符串。 不会对'1,,,,4,5'这样的事情进行检查。 真的 - 这不是这种方法的意图
  • 无效的非数字值。 因为我们正在替换0..9 (那个巨大的REPLACE部分) - 我们不能动态地做到这一点 - MySQL不能“替换任何字符,除了......”这是一个瓶颈,是的 - 但是,再次 - 不是意图方法

虽然我不建议在实时代码中执行此操作,但可以在不需要变量的情况下完成: -

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小提琴: -

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

暂无
暂无

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

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