SQL: заполнить пропуски в упорядоченной последовательности строк с помощью макетов - PullRequest
0 голосов
/ 26 июня 2018

У меня есть строки с последовательностями. Идеальная строка - 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;

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Одним из способов является использование 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

Выход:

┌───────┬─────────────┐
│ 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 │
└───────┴─────────────┘

EDIT

Более сложная форма (если вам не нравится множественная замена):

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 │
└───────┴─────────────┘
0 голосов
/ 26 июня 2018

Хотя мой ответ выше, у @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
0 голосов
/ 26 июня 2018

Я бы не использовал регулярные выражения. Было бы намного быстрее использовать 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...