【问题标题】:SQL: Fill missings in a ordered string sequence with dummiesSQL:用假人填充有序字符串序列中的缺失
【发布时间】:2018-12-05 11:34:47
【问题描述】:

我有带有序列的字符串。理想的字符串是 01-02-03-04 但在某些记录中我缺少:

---------
| seq0  |
| ----- |
| 01-04 |
| 02-03 |
| 02-04 |
| 01-04 |
| 02    |
---------    

这些是示例。任何组合都是可能的。为了更好地识别缺少的内容,我想插入 xx 以获取缺少的内容:

-----------------------
| seq0  | seq1        | 
| ----- | ----------- | 
| 01-04 | 01-xx-xx-04 | 
| 02-03 | xx-02-03-xx | 
| 02-04 | xx-02-xx-04 | 
| 01-04 | 01-xx-xx-04 | 
| 02    | xx-02-xx-xx | 
-----------------------    

我有一个使用 REGEXP 和 REPLACE 的解决方案(见下文)。但是如果序列更长(例如:01-...-12),编写代码会很麻烦。我想知道是否有另一种方法可以做到这一点。我可以使用短过程(MySQL),但也许有一种优雅的方法可以使用(几乎)纯 SQL 来做到这一点。

生成表格的代码:

DROP TABLE IF EXISTS t0;
CREATE TABLE t0 (
  seq0       VARCHAR(100)
, seq1      VARCHAR(100)
);

INSERT INTO t0 (seq0) VALUES 
 ('01-04')
,('02-03')
,('02-04') 
,('01-04')
,('02')
;

SELECT * FROM t0;

UPDATE t0
SET seq1 = seq0
    -- insert xx       
    , seq1 = CASE WHEN seq1 REGEXP '01-03' THEN REPLACE(seq1,'01-03','01-xx-03') ELSE seq1 END
    , seq1 = CASE WHEN seq1 REGEXP '01-04' THEN REPLACE(seq1,'01-04','01-xx-xx-04') ELSE seq1 END   

    , seq1 = CASE WHEN seq1 REGEXP '02-04' THEN REPLACE(seq1,'02-04','02-xx-04') ELSE seq1 END   

    -- right pad xx
    , seq1 = CASE WHEN seq1 REGEXP '01$' THEN REPLACE(seq1,'01','01-xx-xx-xx') ELSE seq1 END   
    , seq1 = CASE WHEN seq1 REGEXP '02$' THEN REPLACE(seq1,'02','02-xx-xx') ELSE seq1 END   
    , seq1 = CASE WHEN seq1 REGEXP '03$' THEN REPLACE(seq1,'03','03-xx') ELSE seq1 END   

    -- left pad xx
    , seq1 = CASE WHEN seq1 REGEXP '^02' THEN REPLACE(seq1,'02','xx-02') ELSE seq1 END   
    , seq1 = CASE WHEN seq1 REGEXP '^03' THEN REPLACE(seq1,'03','xx-xx-03') ELSE seq1 END   
    , seq1 = CASE WHEN seq1 REGEXP '^04' THEN REPLACE(seq1,'04','xx-xx-xx-04') ELSE seq1 END   
    ;
SELECT * FROM t0;

【问题讨论】:

    标签: mysql sql regex replace


    【解决方案1】:

    我不会使用正则表达式。使用 C# 或其他编程语言会快得多,但这应该可以工作。

    注意:这不处理具有错误数据的 seq0。

    ALTER FUNCTION F_GetPattern
    (
        @Sequence VARCHAR(100)
    )
    RETURNS
    VARCHAR(1000)
    AS
    BEGIN
        DECLARE @T1 VARCHAR(10)
        DECLARE @T2 VARCHAR(10)
        DECLARE @I1 INT 
        DECLARE @I2 INT = 0
        DECLARE @Size INT = 4
        DECLARE @Index INT = 0
        DECLARE @Result VARCHAR(100) = ''
    
        SET @T1 = SUBSTRING(@Sequence, 1, 2)
        SET @T2 = SUBSTRING(@Sequence, 4, 2)
    
        SET @I1 = CAST(@T1 AS INT)
        IF (LEN(@Sequence ) > 3)
            SET @I2 = CAST(@T2 AS INT)
    
        WHILE @Index < @Size
        BEGIN
            IF @Index > 0 AND @Index < @Size
                SET @Result = @Result + '-'
            IF @Index + 1 = @I1 
                SET @Result = @Result + @T1
            ELSE
            BEGIN
                IF @Index + 1 = @I2
                    SET @Result = @Result + @T2
                ELSE
                    SET @Result = @Result + 'xx'
            END
            SET @Index = @Index + 1
        END
    
        return @Result
    END
    GO
    SELECT seq0, DBO.F_GetPattern(seq0) FROM t0
    GO
    

    【讨论】:

      【解决方案2】:

      一种方法是使用REPLACE:

      SELECT seq0,
            REPLACE(REPLACE(REPLACE(
            REPLACE('01-02-03-04',
               CASE WHEN INSTR(seq0, '01') > 0 THEN 'u' ELSE '01' END, 'xx')
              ,CASE WHEN INSTR(seq0, '02') > 0 THEN 'u' ELSE '02' END, 'xx')
              ,CASE WHEN INSTR(seq0, '03') > 0 THEN 'u' ELSE '03' END, 'xx')
              ,CASE WHEN INSTR(seq0, '04') > 0 THEN 'u' ELSE '04' END, 'xx') AS result
      FROM t0;
      

      DBFiddle Demo

      输出:

      ┌───────┬─────────────┐
      │ seq0  │   result    │
      ├───────┼─────────────┤
      │ 01-04 │ 01-xx-xx-04 │
      │ 02-03 │ xx-02-03-xx │
      │ 02-04 │ xx-02-xx-04 │
      │ 01-04 │ 01-xx-xx-04 │
      │ 02    │ xx-02-xx-xx │
      └───────┴─────────────┘
      

      编辑

      更高级的形式(如果你不喜欢嵌套多个替换):

      SELECT seq0, result
      FROM (
          SELECT seq0
              ,@u:= REPLACE(IF(@prev_value=seq0, @u,@start_string),
                            IF(INSTR(seq0, sub.c) > 0, 'u', sub.c), 'xx') AS result
              ,@cnt:=IF(@prev_value=seq0,@cnt-1,@l) AS c
              ,@prev_value := seq0
          FROM ( SELECT DISTINCT *
              FROM t0 ,(SELECT '01' AS c UNION SELECT '02' 
                        UNION SELECT '03' UNION SELECT '04') num)sub
          ,(SELECT @u := ''
              ,@prev_value := ''
              ,@start_string := '01-02-03-04'
              ,@l := length(@start_string)-length(replace(@start_string,'-',''))
              ,@cnt := 0
              ) z
          ORDER BY sub.seq0, sub.c
      ) q
      WHERE q.c = 0;
      

      DBFiddle Demo2

      输出:

      ┌───────┬─────────────┐
      │ seq0  │   result    │
      ├───────┼─────────────┤
      │ 01-04 │ 01-xx-xx-04 │
      │ 02    │ xx-02-xx-xx │
      │ 02-03 │ xx-02-03-xx │
      │ 02-04 │ xx-02-xx-04 │
      └───────┴─────────────┘
      

      【讨论】:

      • 感谢您提供这些解决方案。第一个更容易理解,第二个我必须深入研究。一旦掌握它,它将增加我的 SQL 知识。
      【解决方案3】:

      虽然我上面的回答很好,但@Lukasz Szozda 有一个更简单的方法。如果您只有几个条目,请帮自己一个忙,并保持这样的简单!

      如果您需要一个更强大的选项,可以扩展到更大的序列,那么这里是另一个使用 STUFF(...) 的选项。如果序列变大并且必须对大量记录执行此操作,它很可能比其他选项更有效。

      CREATE FUNCTION F_GetPattern
      (
          @Sequence VARCHAR(100)
      )
      RETURNS
      VARCHAR(1000)
      AS
      BEGIN
          DECLARE @T1 VARCHAR(10)
          DECLARE @T2 VARCHAR(10)
          DECLARE @I1 INT 
          DECLARE @I2 INT = 0
          DECLARE @Result VARCHAR(100) = 'xx-xx-xx-xx'
      
          SET @T1 = SUBSTRING(@Sequence, 1, 2)
          SET @T2 = SUBSTRING(@Sequence, 4, 2)
      
          SET @I1 = CAST(@T1 AS INT)
          IF (LEN(@Sequence ) > 3)
              SET @I2 = CAST(@T2 AS INT)
      
          SET @Result = STUFF(@Result, @I1 * 3 -2, 2, @T1) 
          IF @I2 > 0
              SET @Result = STUFF(@Result, @I2 * 3 -2, 2, @T2)
          return @Result
      END
      GO
      SELECT seq0, DBO.F_GetPattern(seq0) FROM t0
      GO
      

      【讨论】:

      • 感谢这两个解决方案。我不使用简短的程序,但我会尝试理解它。不幸的是,MariaDB 没有 STUFF 功能。我会尝试找到解决方法。
      • 这是什么语法?
      • 那是 SQL Server,但是您应该能够对 MySQL 使用“INSERT”。 dbload.com/articles/mssql-stuff-function-in-mysql.htm
      猜你喜欢
      • 2015-10-06
      • 1970-01-01
      • 2013-05-15
      • 2017-09-17
      • 1970-01-01
      • 2016-02-29
      • 2011-04-03
      • 2019-05-16
      • 2020-07-17
      相关资源
      最近更新 更多