Поиск и замена значений в фигурных скобках значениями из другого столбца

#sql #sql-server #tsql #common-table-expression #sql-server-2017

#sql #sql-сервер #tsql #common-table-expression #sql-server-2017

Вопрос:

У меня возникли проблемы при написании SQL-запроса.

У меня есть таблица данных со столбцами ниже.

DataTable 1

               id     notes
           ----------------------------------------------------------------
             1      The Organization
             1      develop document disseminate to {{param = "ac-1_prm_1"}}
             2      develop document  to {{param = "ac-1_prm_2"}}
             2       Test
 

Параметр таблицы данных

  parameterid     value.                 Id
 -------------------------------—————----------------
 ac-1_prm_1      apple doc.              1
 ac-1_prm_2      google doc.             1
 ac-1_prm_3      facebook doc.           2
 

Мне нужно создать final_notes столбец:

  id  notes                                                       final_notes 
 ---------------------------------------------------------------------------------------------     
  1   The Organization                                  The Organization
  1   develop document disseminate                      develop document disseminate to apple 
     to{<!-- -->{param = "ac-1_prm_1"}}                 doc and google doc
     and {<!-- -->{param = "ac-1_prm_2"}}        
      
 
  2   develop document                                  develop document facebook doc 
      to {<!-- -->{param = "ac-1_prm_3"}}
  2   Test                                              Test
 

Код Sql не работает.

В моем коде мне приходится жестко кодировать значение, которое мне не нужно.

      DECLARE @DataTable TABLE (
      id int,
       notes varchar(1000)
       );
     INSERT INTO @DataTable VALUES 
    (1, 'Organization'),
    (1, 'develop document disseminate to {{param = "ac-1_prm_1"}} and 
    {{param = "ac-1_prm_2"}} '),
    (2, 'develop document  to {{param = "ac-1_prm_3"}}'),
    (2, 'test');

   DECLARE @DataTableParameter TABLE (
    parameterid varchar(100),
    [value] varchar(100),
    id int
   );
   INSERT INTO @DataTableParameter VALUES
   ('ac-1_prm_1', 'apple doc.', 1),
   ('ac-1_prm_2', 'google doc.', 1),
   ('ac-1_prm_3', 'facebook doc.', 2)

 ;WITH CTE AS (
 SELECT t1.id, t1.notes, t2.parameterid, t2.value 
 FROM @DataTable AS t1
 INNER  JOIN  @DataTableParameter AS t2 ON t1.id = t2.id
)
  SELECT 
 t.id, 
 REPLACE('develop document disseminate to {'   STUFF(
     (
         SELECT ', {'   parameterid   '}'
         FROM CTE 
         WHERE id = t.id 
         ORDER BY parameterid
         FOR XML PATH(''), TYPE
     ).value('.', 'NVARCHAR(MAX)'),
     1,
     1,
     ''
    )   '}', ',', ' and ') AS notes,
   REPLACE(REPLACE('develop document disseminate to '   STUFF(
     (
         SELECT ', '   value
         FROM CTE 
         WHERE id = t.id 
         ORDER BY value
         FOR XML PATH(''), TYPE
     ).value('.', 'NVARCHAR(MAX)'),
     1,
     1,
     ''
 ), '.', ''), ',', ' and ') AS final_notes
   FROM CTE AS t
  GROUP BY t.id;
 

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

1. Так что же не так с вашим кодом в том виде, в каком он представлен? Работает ли это? Предположительно, нет, но в чем заключается ваш вопрос?

2. назначьте свои параметры переменным, так как у вас есть только 3 строки или у вас будет больше? будет легче replace

3. У меня много (более 1000) строк данных

4. Пожалуйста, кто-нибудь может помочь мне с запросом. Параметры являются динамическими и могут достигать любого предела . Это всего лишь пример данных. Некоторые строки не имеют параметров, а некоторые из них у нас есть несколько в одной строке.

5. в этом случае вы можете переименовать свои заметки из фигурных скобок в простое ключевое слово like ac-1_prm_1 ?

Ответ №1:

Рекурсивный CTE, вероятно, будет здесь довольно громоздким, поскольку вы не можете использовать TOP , и вы также должны фильтровать только конечные результаты.

Вместо этого используйте табличную переменную или временную таблицу и обновляйте ее в цикле.

 DECLARE @results TABLE (id int, notes varchar(1000));

INSERT @results (id, notes)
SELECT id, notes
FROM @DataTable dt;

DECLARE @dtp varchar(100), @dtv varchar(100);

WHILE 1=1
BEGIN
    SELECT TOP (1)
        @dtp = dtp.parameterid,
        @dtv = dtp.value
    FROM @DataTableParameter dtp
    WHERE parameterid > @dtp OR @dtp IS NULL
    ORDER BY parameterid;

    IF @@ROWCOUNT = 0
        BREAK;
    
    UPDATE @results
    SET
        notes = REPLACE(notes, '{{param = "'   @dtp   '"}}', @dtv)
    FROM @results r
    WHERE notes LIKE '%'   @dtp   '%';
END;

SELECT *
FROM @results;
 

db<>скрипка

Ответ №2:

Пожалуйста, попробуйте следующее решение.

SQL

 -- DDL and sample data population, start
DECLARE @DataTable TABLE (id int, notes varchar(1000));
INSERT INTO @DataTable VALUES 
(1, 'Organization'),
(1, 'develop document disseminate to {{param = "ac-1_prm_1"}} and 
{{param = "ac-1_prm_2"}} '),
(2, 'develop document  to {{param = "ac-1_prm_3"}}'),
(2, 'test');

DECLARE @DataTableParameter TABLE (parameterid varchar(100), [value] varchar(100), id int);
INSERT INTO @DataTableParameter VALUES
('ac-1_prm_1', 'apple doc.', 1),
('ac-1_prm_2', 'google doc.', 1),
('ac-1_prm_3', 'facebook doc.', 2);
-- DDL and sample data population, end

DECLARE @parameterid varchar(100), @value VARCHAR(100);
DECLARE @RowCount INT = (SELECT COUNT(*) FROM @DataTableParameter);

WHILE @RowCount > 0 BEGIN
   SELECT @parameterid = parameterid, @value = value
   FROM @DataTableParameter 
   ORDER BY ID DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;
   
   -- do whatever needed, apply any logic, call stored procedures, etc.
   UPDATE @DataTable
   SET notes = REPLACE(notes, CONCAT('{{param = "', @parameterid, '"}}'), @value);

   SET @RowCount -= 1;
END;

-- test
SELECT * FROM @DataTable;
 

Вывод

  ---- -------------------------------------------------------------- 
| id |                            notes                             |
 ---- -------------------------------------------------------------- 
|  1 | Organization                                                 |
|  1 | develop document disseminate to apple doc. and   google doc. |
|  2 | develop document  to facebook doc.                           |
|  2 | test                                                         |
 ---- -------------------------------------------------------------- 
 

Ответ №3:

Это может быть причиной необычного обновления:

 CREATE DATABASE testDb_Shnugo;
GO
USE testDb_Shnugo;
GO
 

—создайте таблицы с вашими тестовыми данными

 CREATE TABLE dbo.DataTable (id INT, notes VARCHAR(1000));
INSERT INTO dbo.DataTable VALUES 
(1, 'The Organization'),
(1, 'develop document disseminate to {{param = "ac-1_prm_1"}} and {{param = "ac-1_prm_2"}} '),
(2, 'develop document  to {{param = "ac-1_prm_3"}}'),
(2, 'test');
GO

CREATE TABLE DataTableParameter (parameterid VARCHAR(100), [value] VARCHAR(100), id INT);
INSERT INTO DataTableParameter VALUES
('ac-1_prm_1', 'apple doc.', 1),
('ac-1_prm_2', 'google doc.', 1),
('ac-1_prm_3', 'facebook doc.', 2);
GO
 

— Вот и волшебство: необычное обновление

 CREATE FUNCTION dbo.MultiReplaceDataTableParameters(@inp VARCHAR(1000), @id INT)
RETURNS VARCHAR(1000)
AS
BEGIN
    DECLARE @result VARCHAR(1000)=@inp;
    SELECT @result = REPLACE(@result,CONCAT('{{param = "',dtp.parameterid,'"}}'),dtp.[value])
    FROM dbo.DataTableParameter dtp
    WHERE dtp.id=@id;

    RETURN @result;
END
GO
 

— Попробуйте

 SELECT *
     ,dbo.MultiReplaceDataTableParameters(dt.notes,dt.id) AS ReplacedText
FROM dbo.DataTable dt
ORDER BY dt.id;
GO
 

—Очистка (осторожно с реальными данными!)

 USE master;
GO

DROP DATABASE testDb_Shnugo;
 

Идея вкратце:

Изворотливое обновление проходит через результат таблицы и использует объявленный параметр в качестве входных данных и оценки. Таким образом, значение будет меняться при посещении любой строки.

Это позволяет выполнять несколько действий за один раз.

Есть очень веские причины избегать этого метода, но в данном случае это может помочь.

 1   The Organization
1   develop document disseminate to apple doc. and google doc. 
2   develop document  to facebook doc.
2   test