Заменить значения в строке CSV - PullRequest
1 голос
/ 06 марта 2012

У меня есть список товаров, разделенных запятыми, и, поскольку список товаров был заменен новыми товарами, я пытаюсь изменить этот список CSV на новый список товаров.

create table #tmp (
  id int identity(1,1) not null,
  plist varchar(max) null
)

create table #tmpprod (
  oldid int null,
  newid int null
)

insert into #tmp
select '10,11,15,17,19'
union
select '22,34,44,25'
union
select '5,6,8,9'

insert into #tmpprod
select 5, 109
union
select 9, 110
union
select 10, 111
union
select 15, 112
union
select 19, 113
union
select 30, 114
union
select 34, 222
union
select 44, 333

drop table #tmp
drop table #tmpprod

Я пытаюсь использовать разделенную клавишу fn для преобразования в строки, а затем заменить эти значения и затем снова преобразовать столбцы в строки. Это возможно любым другим способом?

Вывод будет следующим:


1   111,11,112,17,113
2   22,222,333,25
3   109,6,8,110

Ответы [ 4 ]

4 голосов
/ 06 марта 2012

Конвертируйте разделенный запятыми список в XML.Используйте таблицу чисел, XQuery и position(), чтобы получить отдельные идентификаторы с положением, которое они имеют в строке.Создайте разделенную запятыми строку, используя трюк for xml path('') с left outer join до #tempprod и упорядочив по position().

;with C as
(
  select T.id,
         N.number as Pos,
         X.PList.value('(/i[position()=sql:column("N.Number")])[1]', 'int') as PID
  from @tmp as T
    cross apply (select cast('<i>'+replace(plist, ',', '</i><i>')+'</i>' as xml)) as X(PList)
    inner join master..spt_values as N
      on N.number between 1 and X.PList.value('count(/i)', 'int')
  where N.type = 'P'  
)
select C1.id,
       stuff((select ','+cast(coalesce(T.newid, C2.PID) as varchar(10))
              from C as C2
                left outer join @tmpprod as T
                  on C2.PID = T.oldid
              where C1.id = C2.id
              order by C2.Pos
              for xml path(''), type).value('.', 'varchar(max)'), 1, 1, '')

from C as C1
group by C1.id

Пример SE-Data

3 голосов
/ 06 марта 2012

Если предположить, что SQL Server 2005 или выше, и что порядок не важен, тогда с помощью этой функции разделения:

CREATE FUNCTION [dbo].[SplitInts]
(
   @List       VARCHAR(MAX),
   @Delimiter  CHAR(1)
)
RETURNS TABLE
AS
   RETURN ( SELECT Item FROM ( SELECT Item = x.i.value('(./text())[1]', 'int') FROM 
            ( SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(@List, @Delimiter, '</i><i>') 
              + '</i>').query('.') ) AS a CROSS APPLY [XML].nodes('i') AS x(i)
          ) AS y WHERE Item IS NOT NULL
   );
GO

Вы можете получить этот результат следующим образом:

;WITH x AS
(
    SELECT id, item, oldid, [newid], rn = ROW_NUMBER() OVER
    (PARTITION BY id ORDER BY PATINDEX('%,' + RTRIM(s.Item) + ',%', ',' + t.plist + ','))
    FROM #tmp AS t CROSS APPLY dbo.SplitInts(t.plist, ',') AS s
    LEFT OUTER JOIN #tmpprod AS p ON p.oldid = s.Item
)
SELECT DISTINCT id, STUFF((SELECT ',' +RTRIM(COALESCE([newid], Item)) 
    FROM x AS x2 WHERE x2.id = x.id
    FOR XML PATH(''), TYPE).value('.[1]', 'varchar(max)'), 1, 1, '') 
FROM x;

Обратите внимание, что ROW_NUMBER() / OVER / PARTITION BY / ORDER BY существует только для , попытайтесь , чтобы заставить оптимизатор возвращать строки в указанном порядке.Вы можете наблюдать это поведение сегодня, и оно может измениться завтра в зависимости от статистики или изменений данных, изменений оптимизатора (пакетов обновления, CU, обновления и т. Д.) Или других переменных.

Короче говоря: если вы зависите от этого порядка, просто отправьте набор обратно клиенту, и пусть клиент создаст список, разделенный запятыми.Вероятно, в любом случае эта функциональность принадлежит.

1 голос
/ 06 марта 2012

Спасибо за этот вопрос - я только что узнал что-то новое.Следующий код является адаптацией статьи , написанной Робом Волком именно на эту тему.Это очень умный запрос!Я не буду копировать весь контент здесь.Я адаптировал его для создания результатов, которые вы ищете в своем примере.

CREATE TABLE #nums (n INT)
DECLARE @i INT 
SET @i = 1
WHILE @i < 8000 
BEGIN
    INSERT #nums VALUES(@i)
    SET @i = @i + 1
END


CREATE TABLE #tmp (
  id INT IDENTITY(1,1) not null,
  plist VARCHAR(MAX) null
)

INSERT INTO #tmp
VALUES('10,11,15,17,19'),('22,34,44,25'),('5,6,8,9')

CREATE TABLE #tmpprod (
  oldid INT NULL,
  newid INT NULL
)

INSERT INTO #tmpprod VALUES(5, 109),(9, 110),(10, 111),(15, 112),(19, 113),(30, 114),(34, 222),(44, 333)

;WITH cte AS (SELECT ID, NULLIF(SUBSTRING(',' + plist + ',' , n , CHARINDEX(',' , ',' + plist + ',' , n) - n) , '') AS prod
    FROM #nums, #tmp
    WHERE ID <= LEN(',' + plist + ',') AND SUBSTRING(',' + plist + ',' , n - 1, 1) = ',' 
    AND CHARINDEX(',' , ',' + plist + ',' , n) - n > 0)
UPDATE t SET plist = (SELECT CAST(CASE WHEN tp.oldid IS NULL THEN cte.prod ELSE tp.newid END AS VARCHAR) + ',' 
            FROM cte LEFT JOIN #tmpprod tp ON cte.prod = tp.oldid
            WHERE cte.id = t.id FOR XML PATH(''))
FROM #tmp t WHERE id = t.id

UPDATE #tmp SET plist = SUBSTRING(plist, 1, LEN(plist) -1)
WHERE LEN(plist) > 0 AND SUBSTRING(plist, LEN(plist), 1) = ','

SELECT * FROM #tmp
DROP TABLE #tmp
DROP TABLE #tmpprod
DROP TABLE #nums

Таблица #nums - это таблица последовательных целых чисел, длина которой должна быть больше самой длинной CSV, которую вы имеетев вашем столе.Первые 8 строк сценария создают эту таблицу и заполняют ее.Затем я скопировал ваш код, за которым следует основная часть этого запроса - очень умный синтаксический анализатор с одним запросом, более подробно описанный в статье, указанной выше.Общее табличное выражение (WITH cte ...) выполняет синтаксический анализ, а скрипт обновления перекомпилирует результаты в CSV и обновляет # tmp.

0 голосов
/ 31 марта 2015

Блог Адама Маханича содержит эту публикацию UDF только для T-SQL, который может принимать подстановочные знаки T-SQL для использования в качестве замены.

http://dataeducation.com/splitting-a-string-of-unlimited-length/

Для моего собственного использования я настроилразмеры varchar до max.Также обратите внимание, что этот UDF работает довольно медленно, но если вы не можете использовать CLR, это может быть вариантом.Незначительные изменения, которые я внес в код автора, могут ограничить его использование SQL Server 2008r2 и более поздними версиями.

CREATE FUNCTION dbo.PatternReplace
(
   @InputString VARCHAR(max),
   @Pattern VARCHAR(max),
   @ReplaceText VARCHAR(max)
)
RETURNS VARCHAR(max)
AS
BEGIN
   DECLARE @Result VARCHAR(max) = ''
   -- First character in a match
   DECLARE @First INT
   -- Next character to start search on
   DECLARE @Next INT = 1
   -- Length of the total string -- 0 if @InputString is NULL
   DECLARE @Len INT = COALESCE(LEN(@InputString), 0)
   -- End of a pattern
   DECLARE @EndPattern INT

   WHILE (@Next <= @Len) 
   BEGIN
      SET @First = PATINDEX('%' + @Pattern + '%', SUBSTRING(@InputString, @Next, @Len))
      IF COALESCE(@First, 0) = 0 --no match - return
      BEGIN
         SET @Result = @Result + 
            CASE --return NULL, just like REPLACE, if inputs are NULL
               WHEN  @InputString IS NULL
                     OR @Pattern IS NULL
                     OR @ReplaceText IS NULL THEN NULL
               ELSE SUBSTRING(@InputString, @Next, @Len)
            END
         BREAK
      END
      ELSE
      BEGIN
         -- Concatenate characters before the match to the result
         SET @Result = @Result + SUBSTRING(@InputString, @Next, @First - 1)
         SET @Next = @Next + @First - 1

         SET @EndPattern = 1
         -- Find start of end pattern range
         WHILE PATINDEX(@Pattern, SUBSTRING(@InputString, @Next, @EndPattern)) = 0
            SET @EndPattern = @EndPattern + 1
         -- Find end of pattern range
         WHILE PATINDEX(@Pattern, SUBSTRING(@InputString, @Next, @EndPattern)) > 0
               AND @Len >= (@Next + @EndPattern - 1)
            SET @EndPattern = @EndPattern + 1

         --Either at the end of the pattern or @Next + @EndPattern = @Len
         SET @Result = @Result + @ReplaceText
         SET @Next = @Next + @EndPattern - 1
      END
   END
   RETURN(@Result)
END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...