簡體   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