Объединение данных из нескольких строк в одно поле varchar (max), есть ли альтернатива курсору и .WRITE - PullRequest
0 голосов
/ 04 мая 2020

У меня есть данные в исходной таблице, которая выглядит, как показано ниже. По сути, это повествовательное поле, разделенное на 47 частей. В данных существует соглашение о том, что если пользователь вводит новую строку, она заменяется символом `и этим заканчивается эта строка.

CREATE TABLE narrdata (parentindex integer, linenum integer, linetext varchar(48))

Я хочу объединить данные из отдельных строк в одну varchar ( max) поле. Раньше я делал это с помощью курсора и .WRITE вот так.
Мне интересно, есть ли другой способ сделать это без .WRITE.

CREATE TABLE #target_table (myindex integer, narrative varchar(max))

/* some code here to initialize rows in target_table - so assume it already has a row */

DECLARE @item                   integer
DECLARE @line                   integer
DECLARE @chunk                  varchar(54)
DECLARE @crlf                   bit
DECLARE @crlf_last              bit
DECLARE @item_last              integer

DECLARE cur_narr CURSOR FOR
SELECT
   parentindex,
   linenum,
   CASE
      WHEN linetext IS NOT NULL THEN
         CASE
            WHEN RIGHT(RTRIM(linetext), 1) = '`' THEN
               SUBSTRING(linetext, 1, LEN(linetext) - 1) + CHAR(13) + CHAR(10)
            ELSE
               RTRIM(linetext)
         END
      ELSE ''
   END,
   CASE
      WHEN linetext IS NOT NULL THEN
         CASE WHEN RIGHT(RTRIM(linetext), 1) = '`' THEN 1 ELSE 0 END
      ELSE 0
   END
FROM narrdata
WHERE parentindex IN ( /* some condition here */ )
ORDER BY parentindex, linenum

SET @item_last = -1
SET @crlf_last = 1

OPEN cur_narr

FETCH NEXT FROM cur_narr
INTO @item, @line, @chunk, @crlf

WHILE @@FETCH_STATUS = 0
   BEGIN

   IF @item <> @item_last
      BEGIN

      SET @crlf_last = 1
      SET @item_last = @item

      END

   IF @crlf_last = 0
      SET @chunk = ' ' + @chunk

   UPDATE #target_table
   SET narrative .WRITE(@chunk, NULL, 0)
   WHERE myindex = @item

   SET @crlf_last = @crlf

   FETCH NEXT FROM cur_narr
   INTO @item, @line, @chunk, @crlf

   END

CLOSE cur_narr
DEALLOCATE cur_narr


INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 1, 'This is a narrative which should be separated')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 2, 'into several lines in the source data.  I have')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 3, 'no idea which line this part will be in.  This')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 4, 'however should be before a newline.`')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 5, 'A new line just started here.`')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 6, 'Another new line here.  Then some blank lines.`')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 7, '`')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 8, '`')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 9, 'How about this now?  Is this enough sample')
INSERT INTO narrdata (parentindex, linenum, linetext) VALUES (37791, 10, 'data.')

Ожидаемый результат


This is a narrative which should be separated into several lines in the source data.  I have no idea which line this part will be in.  This however should be before a newline.
A new line just started here.
Another new line here.  Then some blank lines.


How about this now?  Is this enough sample data.

Вот пример, который вы можете запустить для себя:

CREATE TABLE #narrdata (parentindex integer, linenum integer, linetext varchar(48))

INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 1, 'This is a narrative which should be separated')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 2, 'into several lines in the source data.  I have')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 3, 'no idea which line this part will be in.  This')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 4, 'however should be before a newline.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 5, 'A new line just started here.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 6, 'Another new line here.  Then some blank lines.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 7, '`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 8, '`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 9, 'How about this now?  Is this enough sample')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 10, 'data.')

INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 1, 'This is a narrative which should be separated')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 2, 'into several lines in the source data.  I have')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 3, 'no idea which line this part will be in.  This')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 4, 'however should be before a newline.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 5, 'A new line just started here.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 6, 'Another new line here.  Then some blank lines.`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 7, '`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 8, '`')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 9, 'How about this now?  Is this enough sample')
INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37792, 10, 'data.')

INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37793, 1, 'A simple line example.')

CREATE TABLE #target_table (myindex integer, narrative varchar(max))

INSERT INTO #target_table (myindex, narrative)
SELECT DISTINCT parentindex, ''
FROM #narrdata

DECLARE @item                   integer
DECLARE @line                   integer
DECLARE @chunk                  varchar(54)
DECLARE @crlf                   bit
DECLARE @crlf_last              bit
DECLARE @item_last              integer

DECLARE cur_narr CURSOR FOR
SELECT
   parentindex,
   linenum,
   CASE
      WHEN linetext IS NOT NULL THEN
         CASE
            WHEN RIGHT(RTRIM(linetext), 1) = '`' THEN
               SUBSTRING(linetext, 1, LEN(linetext) - 1) + CHAR(13) + CHAR(10)
            ELSE
               RTRIM(linetext)
         END
      ELSE ''
   END,
   CASE
      WHEN linetext IS NOT NULL THEN
         CASE WHEN RIGHT(RTRIM(linetext), 1) = '`' THEN 1 ELSE 0 END
      ELSE 0
   END
FROM #narrdata
ORDER BY parentindex, linenum

SET @item_last = -1
SET @crlf_last = 1

OPEN cur_narr

FETCH NEXT FROM cur_narr
INTO @item, @line, @chunk, @crlf

WHILE @@FETCH_STATUS = 0
   BEGIN

   IF @item <> @item_last
      BEGIN

      SET @crlf_last = 1
      SET @item_last = @item

      END

   IF @crlf_last = 0
      SET @chunk = ' ' + @chunk

   UPDATE #target_table
   SET narrative .WRITE(@chunk, NULL, 0)
   WHERE myindex = @item

   SET @crlf_last = @crlf

   FETCH NEXT FROM cur_narr
   INTO @item, @line, @chunk, @crlf

   END

CLOSE cur_narr
DEALLOCATE cur_narr

SELECT * FROM #target_table

DROP TABLE #target_table
DROP TABLE #narrdata

Ответы [ 5 ]

1 голос
/ 05 мая 2020

В вашем запросе дополнительных ответов, не использующих XML, см. Ниже, используя циклы T- SQL.

Не уверен, что это более эффективно, но, безусловно, веселее. :)

CREATE TABLE #target_table
(
    myindex INT, 
    narrative VARCHAR(MAX)
)

DECLARE @output VARCHAR(MAX)
DECLARE @loop INT = 1
DECLARE @lineloop INT = 0

DECLARE @maxloop INT

-- find the max parent index
SET @maxloop = (SELECT MAX(maxloop.parentindex) FROM narrdata maxloop)

DECLARE @maxlineloop INT

-- loop through the parent indexes
WHILE @loop <= @maxloop
    BEGIN
    -- find the max linenum for that the current parent index
    SET @maxlineloop = (SELECT MAX(lineloop.linenum) FROM narrdata lineloop WHERE lineloop.parentindex = @loop)

    -- loop through the linenums for the current parent index
    WHILE @lineloop <= @maxlineloop
        BEGIN
        SET @output = (SELECT ISNULL(@output+' ','') + z.linetext 
        FROM narrdata z
        WHERE z.parentindex = @loop AND z.linenum = @lineloop)
        --Strip out the line breaks chars (plus the additional space before it)
        SET @output = REPLACE(@output,' `','')
        -- add one to the linenum
        SET @lineloop += 1
        END
    -- INSERT THE row into the temp table
    INSERT INTO #target_table (myindex, narrative) VALUES (@loop, @output)
    -- Set the linenum back to 0
    SET @lineloop = 0
    --add 1 to the parentindex loop
    SET @loop += 1
    END

-- Present the results
SELECT * FROM #target_table

-- Drop the temp table
DROP TABLE #target_table
1 голос
/ 04 мая 2020

Это должно помочь вам

INSERT INTO narrdata2 (originalparentindex, linetext) 

SELECT parentindex, STUFF(
                        (

SELECT ' ' + CASE WHEN z.linetext = '`' THEN NULL ELSE z.linetext END FROM narrdata z
WHERE z.parentindex = y.parentindex
ORDER BY z.linenum
FOR xml path('')
)
                        , 1
                        , 1
                        , '') FROM narrdata y GROUP BY parentindex

Я назвал новую таблицу narrdata2 со следующей структурой, надеюсь, это поможет!

CREATE TABLE narrdata2 (parentindex INT, originalparentindex INT, linetext VARCHAR(MAX)) 
1 голос
/ 05 мая 2020

Судя по предыдущим ответам, это работает. Я переключил обработку добавленного интервала на конец. И я удалил, казалось бы, посторонний вызов STUFF, который, как мне кажется, просто удалял первый символ. Это оставляет последнюю строку с дополнительным пробелом, но это нормально. Я добавил в вызов REPLACE, чтобы заменить галочки на CRLF. Вещь, TYPE.value, похоже, не требуется, поскольку, когда я тестировал длину, превышающую 8000, казалось, что она работает. Мне, вероятно, следует посмотреть максимальную длину типа данных xml. Похоже, здесь происходит неявное преобразование типа с вызовом REPLACE или без него. Я хотел бы понять это больше.

Я использовал FOR XML PATH раньше для чего-то подобного, хотя с фактическим XML я просто не рассматривал это. Я открыт для других ответов, которые не связаны с FOR XML.

SELECT 
   parentindex,                  
    REPLACE( (SELECT z.linetext + CASE WHEN RIGHT(z.linetext, 1) = '`' THEN '' ELSE ' ' END FROM #narrdata z WHERE z.parentindex = y.parentindex ORDER BY z.linenum FOR XML PATH(''), TYPE).value('.', 'VARCHAR(max)'), '`', CHAR(13) + CHAR(10))
FROM #narrdata y
GROUP BY parentindex
1 голос
/ 04 мая 2020

Попробуйте это для SQL Server 2014.

    Create table #narrdata (parentindex int, linenum int, linetext nvarchar(max))

    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 1, 'This is a narrative which should be separated')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 2, 'into several lines in the source data.  I have')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 3, 'no idea which line this part will be in.  This')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 4, 'however should be before a newline.`')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 5, 'A new line just started here.`')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 6, 'Another new line here.  Then some blank lines.`')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 7, '`')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 8, '`')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 9, 'How about this now?  Is this enough sample')
    INSERT INTO #narrdata (parentindex, linenum, linetext) VALUES (37791, 10, 'data.')

SELECT STUFF((SELECT ' '+ inr.linetext
                FROM #narrdata inr
               WHERE inr.parentindex = nr.parentindex
               ORDER BY inr.linenum
                 FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
  FROM #narrdata nr
 GROUP BY nr.parentindex
0 голосов
/ 05 мая 2020

Я нашел другой метод, но, похоже, он работает только с одним индексом за раз. Есть ли способ адаптировать это для работы со всей таблицей, кроме очевидного l oop и курсора?

DECLARE @narrative varchar(max)
SELECT @narrative = COALESCE(@narrative + CASE WHEN RIGHT(@narrative, 1) = '`' THEN '' ELSE ' ' END, '') + linetext
FROM #narrdata
WHERE parentindex = 37791
ORDER BY linenum

SELECT REPLACE( @narrative, '`', CHAR(13) + CHAR(10) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...