在MySql中使用特定模式提取子字符串

时间:2016-05-04 11:52:55

标签: mysql sql

我有一个文本字段,如下所示:

option[A]sum[A]g3et[B]

我想获取[ ]内没有重复的文本。 意思是:

A
B

不能出现像[ [ ] ]这样的双重案例。

我知道这是一种在数据库中保存数据的可怕方法。我无法更改数据的保存方式。我只需要从本专栏中获取非常具体的(一次性)信息。

我试着这样做:

SELECT substring_index(substring_index(sentence, '[', -1),']', 1)
FROM (SELECT 'THIS[A] IS A TEST' AS sentence) temp;

这给了我A,但它不适用于许多[]

我想过使用正则表达式,但我不知道有多少[ ]

我该怎么做?

1 个答案:

答案 0 :(得分:5)

这不是DB的工作,但有可能:

CREATE TABLE tab(id INT, col VARCHAR(100));           
INSERT INTO tab(id, col) 
VALUES (1, 'option[A]sum[A]g3et[B]'), (2, '[Cosi]sum[A]g3et[ZZZZ]');      

SELECT DISTINCT *
FROM (
  SELECT id, RIGHT(val, LENGTH(val) - LOCATE('[', val)) AS val
  FROM
  (
    SELECT id, SUBSTRING_INDEX(SUBSTRING_INDEX(t.col, ']', n.n), ']', -1) AS val
    FROM tab t 
    CROSS JOIN 
    (
     SELECT a.N + b.N * 10 + 1 n
       FROM 
      (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
      ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
    ) n
    WHERE n.n <= 1 + (LENGTH(t.col) - LENGTH(REPLACE(t.col, ']', '')))
  ) sub
) s
WHERE val <> ''
ORDER BY ID;

SqlFiddleDemo

注意:

根据col最大长度,您可能需要在CROSS JOIN部分生成更多数字。现在它高达100。

输出:

enter image description here

工作原理:

  1. 使用CROSS JOIN
  2. 生成数字表
  3. 根据]拆分字符串作为分隔符
  4. RIGHT(val, LENGTH(val) - LOCATE('[', val))将部分删除至[
  5. 过滤掉空记录
  6. 仅获取DISTINCT
  7. 最内层查询:

    ╔════╦══════════╗
    ║ id ║   val    ║
    ╠════╬══════════╣
    ║  1 ║ option[A ║
    ║  1 ║ sum[A    ║
    ║  1 ║ g3et[B   ║
    ║  1 ║          ║
    ╚════╩══════════╝
    

    第二个子查询:

    ╔════╦═════╗
    ║ id ║ val ║
    ╠════╬═════╣
    ║  1 ║ A   ║
    ║  1 ║ A   ║
    ║  1 ║ B   ║
    ║  1 ║     ║
    ╚════╩═════╝
    

    最外层的查询:

    ╔════╦═════╗
    ║ id ║ val ║
    ╠════╬═════╣
    ║  1 ║ A   ║
    ║  1 ║ B   ║
    ╚════╩═════╝
    
      

    我需要每行查询的结果..不合并

    所以添加简单:

    WHERE n.n <= 1 + (LENGTH(t.col) - LENGTH(REPLACE(t.col, ']', '')))
      AND t.id = ?
    

    编辑2:

      

    请参阅http://sqlfiddle.com/#!9/8ee95/1您的查询部分适用于我的数据。我还将类型更改为longtext。

    您想在MySQL中解析JSON。 正如我在解析之前所说并在应用层中获得价值。此答案仅用于演示/玩具目的,并且性能非常低。

    如果你仍然坚持使用SQL解决方案:

    SELECT id, val,s.n
    FROM (
      SELECT id, RIGHT(val, LENGTH(val) - LOCATE('[', val)) AS val,n
      FROM
      (
        SELECT id, SUBSTRING_INDEX(SUBSTRING_INDEX(t.col, ']', n.n), ']', -1) AS val, n.n
        FROM (SELECT id, REPLACE(col, '[]','') as col FROM tab) t
        CROSS JOIN 
        (
         SELECT e.N * 10000 + d.N * 1000 + c.N * 100 + a.N + b.N * 10 + 1 n
           FROM 
          (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
          ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
          ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
          ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d
          ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e
    
        ) n
        WHERE n.n <= 1 + (LENGTH(t.col) - LENGTH(REPLACE(t.col, ']', '')))
      ) sub
    ) s
    WHERE val <> ''
    GROUP BY id, val
    HAVING n <> MAX(n)
    ORDER BY id,n;
    

    SqlFiddleDemo

    输出:

    ╔═════╦═════════════╦════╗
    ║ id  ║    val      ║ n  ║
    ╠═════╬═════════════╬════╣
    ║  1  ║ CE31285LV4  ║  1 ║
    ║  1  ║ D32E        ║  3 ║
    ║  1  ║ GTX750      ║  5 ║
    ║  1  ║ M256S       ║  7 ║
    ║  1  ║ H2X1T       ║  9 ║
    ║  1  ║ FMLANE4U4   ║ 11 ║
    ╚═════╩═════════════╩════╝
    

    编辑3:

      

    那到底做了什么?你为什么需要n

    CROSS JOIN并且整个子查询只是tally表。就这些。如果MySQL具有生成数字序列的功能(例如generate_series或预先填充的数字表格,则不需要CROSS JOIN

    SUBSTRING_INDEX需要数字表:

      

    <强> SUBSTRING_INDEX(STR,DELIM,计数)

         

    在分隔符delim的计数出现之前,从字符串str返回子字符串。 如果count为正数,则返回最终分隔符左侧的所有内容(从左侧开始计算)。如果count为负数,则返回最终分隔符右侧的所有内容(从右侧开始计算) 。搜索delim时,SUBSTRING_INDEX()执行区分大小写的匹配。