Выберите STUFF (), чтобы одновременно заменить несколько символов в строке на SQL Server - PullRequest
0 голосов
/ 16 марта 2020

У меня есть большой список не избыточных идентификаторов и последовательностей, как показано ниже (таблица A).

enter image description here

Во второй таблице у меня есть несколько записей идентификаторов, позиций и букв, подлежащих изменению (таблица B).

enter image description here

Что я хотел бы сделать, это заменить конкретную c буквы в указанных c позициях в основной последовательности таблицы A на основе изменений, приведенных в таблице B.

Я пытаюсь сделать это с помощью функции STUFF() - пример мой запрос приведен ниже - но в идеале я хотел бы, чтобы все 4 изменения proteinID P12111 выполнялись одновременно в основной последовательности.

SELECT 
    A.[proteinID],
    STUFF(A.[proteinSeq], CAST(B.[position] AS INT), 1, B.[change_to] ) AS [proteinSeq]
FROM 
    [dbo].[TableA] A
LEFT OUTER JOIN 
    [dbo].[TableB] B ON A.proteinID = B.proteinID

С помощью вышеуказанного запроса я получаю 4 раза вторую последовательность с одним изменением за раз.

enter image description here

Я не уверен, смогу ли я получить запрошенный вывод, используя STUFF(). С другой стороны, я бы хотел избежать CURSOR, если это возможно. Любой совет, пожалуйста?

Ответы [ 3 ]

0 голосов
/ 16 марта 2020
WITH aa AS (
      -- get positions and how many times we need to make changes
      SELECT a.*,
             row_number() over (partition by a.proteinid order by a.seq) as seqnum
             , b.position
      from [dbo].[TableA] a
      RIGHT OUTER JOIN 
      [dbo].[Tableb] b on
      b.proteinid=a.proteinid

     ),
     cte AS
      (
      -- Get an anchor
      SELECT a.proteinid, a.seq, 1 AS lev
      FROM [dbo].[Tablea] a
      UNION ALL   
      -- CAST is needed to match the data types, choose yours as needed
      SELECT cte.proteinid, CAST(stuff(cte.seq, b.position, 1, b.changeto) AS VARCHAR(100)), lev + 1
      FROM cte 
      INNER JOIN aa
      ON cte.lev = aa.seqnum
      AND cte.proteinid = aa.proteinid
      INNER JOIN [dbo].[TableB] b
      ON cte.proteinid = b.proteinid
      AND aa.position=b.position
    )

SELECT * FROM cte
-- We filter as only need final change
WHERE cte.lev=
(SELECT MAX(t1.lev) FROM cte AS t1 
WHERE t1.proteinid=cte.proteinid)
0 голосов
/ 17 марта 2020

вы можете использовать WHILE вместо CURSOR.

Пожалуйста, найдите демо здесь .

0 голосов
/ 16 марта 2020

Вы можете использовать рекурсивный CTE. Я немного не уверен, что означает change_from - либо вы меняете значение в позиции, либо нет. В любом случае логика c будет выглядеть так:

with bb as (
      select b.*,
             row_number() over (partition by proteinid order by seq) as seqnum
      from b
     ),
     cte as 
      select a.proteinid, a.seq, 1 as lev
      from a
      union all
      select cte.proteinid, stuff(a.seq, b.position, 1, b.change_to), lev + 1
      from cte join
           b
           on cte.lev = b.seqnum and
              cte.proteinid = a.proteinid
    )
select cte.*
from (select cte.*,
             row_number() over (partition by proteinid order by lev desc) as seqnum
      from cte
     ) cte
where seqnum = 1;
...