Альтернатива рекурсивному регулярному выражению заменить запрос в presto

#sql #optimization #presto

Вопрос:

Как я могу избавиться от этой рекурсивной функции замены регулярных выражений в приведенном ниже SQL-запросе presto с помощью более элегантных шаблонов проектирования SQL?

 SELECT
regexp_replace(
    regexp_replace(
        regexp_replace(
            regexp_replace(
                url, 
                'pattern1', 'replacement1'
            ), 
            'pattern2', 'replacement2'
        ), 
        'pattern3', 'replacement3'
    ), 
    'pattern4', 'replacement4'
) AS deidentified_url
FROM user_requests_tb
 

Мы можем предположить, что каждый URL-адрес соответствует только 1 шаблону

Комментарии:

1. Хотя вышесказанное не является рекурсивным, рекурсию можно использовать для итеративного применения regexp_replace со списком кортежей аргументов. См.: trino.io/docs/current/sql/select.html

2. Извините. Если вы хотите применить только один шаблон к строке, вы можете JOIN в списке шаблонов применить только соответствующий шаблон, предполагая, что он совпадает.

Ответ №1:

Первый пример-без рекурсии, чтобы просто применить шаблон, соответствующий строке (предполагается, что будет соответствовать только один шаблон).:

  1. list это просто способ предоставить список замен.
  2. args предоставляет строку для применения замен.
  3. doit является ли логика, которая применяет только соответствующие шаблоны из list
  4. Окончательное выражение запроса показывает результат для этих совпадающих случаев.
 WITH list (id, arg1, arg2) AS (
        SELECT 1, 'c1', 'x' UNION
        SELECT 2, 'd1', 'x' UNION
        SELECT 3, 'e1', 'x' UNION
        SELECT 4, 'f1', 'x' UNION
        SELECT 5, 'g1', 'x' UNION
        SELECT 6, 'h1', 'x'
     )
   , args (start_str) AS (
        SELECT 'abcde1fghijklmnop'
     )
   , doit (str) AS (
        SELECT regexp_replace(start_str, arg1, arg2)
          FROM args
          JOIN list
            ON regexp_like(args.start_str, list.arg1)
     )
SELECT * FROM doit
;
 

Результат:

  ------------------ 
| str              |
 ------------------ 
| abcdxfghijklmnop |
 ------------------ 
1 row in set
 

Если у вас есть версия presto (например , trino), которая поддерживает WITH RECURSIVE , вот пример того, как рекурсию можно использовать для применения списков замен с переменным числом замен.

  1. list это просто способ предоставить список замен.
  2. args предоставляет строку для применения замен.
  3. doit является рекурсивной логикой, которая перебирает list
  4. В конечном выражении запроса отображаются все результаты (от 0, 1 до n).
 WITH RECURSIVE list (id, arg1, arg2) AS (
        SELECT 1, 'c', 'x' UNION
        SELECT 2, 'd', 'x' UNION
        SELECT 3, 'e', 'x' UNION
        SELECT 4, 'f', 'x' UNION
        SELECT 5, 'g', 'x' UNION
        SELECT 6, 'h', 'x'
     )
   , args (start_str) AS (
        SELECT 'abcdefghijklmnop'
     )
   , doit (n, str) AS (
        SELECT 0  , start_str FROM args UNION ALL
        SELECT n 1, regexp_replace(str, arg1, arg2)
          FROM doit
          JOIN list
            ON list.id = n 1
     )
SELECT * FROM doit
 ORDER BY n
;
 

Результат:

  ------ ------------------ 
| n    | str              |
 ------ ------------------ 
|    0 | abcdefghijklmnop |
|    1 | abxdefghijklmnop |
|    2 | abxxefghijklmnop |
|    3 | abxxxfghijklmnop |
|    4 | abxxxxghijklmnop |
|    5 | abxxxxxhijklmnop |
|    6 | abxxxxxxijklmnop |
 ------ ------------------ 
7 rows in set
 

Тот же SQL с настройкой последнего шага:

  1. Окончательное выражение запроса просто захватывает последнюю версию замененной строки.
 WITH RECURSIVE list (id, arg1, arg2) AS (
        SELECT 1, 'c', 'x' UNION
        SELECT 2, 'd', 'x' UNION
        SELECT 3, 'e', 'x' UNION
        SELECT 4, 'f', 'x' UNION
        SELECT 5, 'g', 'x' UNION
        SELECT 6, 'h', 'x'
     )
   , args (start_str) AS (
        SELECT 'abcdefghijklmnop'
     )
   , doit (n, str) AS (
        SELECT 0  , start_str FROM args UNION ALL
        SELECT n 1, regexp_replace(str, arg1, arg2)
          FROM doit
          JOIN list
            ON list.id = n 1
     )
SELECT * FROM doit
 ORDER BY n DESC
 LIMIT 1
;
 

Результат:

  ------ ------------------ 
| n    | str              |
 ------ ------------------ 
|    6 | abxxxxxxijklmnop |
 ------ ------------------ 
1 row in set