T-SQL: сводная, но для столбцов, разделенных точкой с запятой вместо столбцов - PullRequest
0 голосов
/ 29 октября 2009

В столбце есть значения, разделенные точкой с запятой. Значения в моей таблице:
Значения
1; 2; 3; 4; 5

Я хотел бы преобразовать его в процедуру, чтобы в нем были значения в виде строк:
Значения
1
2
3
4 * +1010 * 5

Как я могу сделать это в T-SQL?

Ответы [ 4 ]

1 голос
/ 06 января 2013

Меня поразило то, что, возможно, я оставил место для дополнительного ответа или дополнительного улучшения, заключавшегося в том, что большинство приведенных ответов / ссылок заключались в том, как разделить такие значения для одного скалярного значения, а не как применять такой логика разбиения для столбца значений в таблице.

Я включаю как решение таблицы чисел, так и решение XML. Решение XML было вдохновлено более ранним постом priyanka.sarkar. Я думаю, что решение таблицы чисел, использующее таблицу фактических чисел вместо CTE, как в приведенном ниже решении, возможно, является самым быстрым, но подход на основе XML заслуживает того, чтобы его разработать, потому что он действительно хорошо выглядит.

Итак, моя попытка.

CREATE PROCEDURE PARSE_DELIMITED_VALUES
AS
WITH FIRST_NUMBERS (N) AS (
        SELECT 1 UNION ALL SELECT 1
), SECOND_NUMBERS (N) AS (
        SELECT E1.N
        FROM FIRST_NUMBERS E1
        CROSS JOIN FIRST_NUMBERS E2
), THIRD_NUMBERS (N) AS (
        SELECT E1.N
        FROM SECOND_NUMBERS E1
        CROSS JOIN SECOND_NUMBERS E2
), FOURTH_NUMBERS (N) AS (
        SELECT E1.N
        FROM THIRD_NUMBERS E1
        CROSS JOIN THIRD_NUMBERS E2
), FIFTH_NUMBERS (N) AS (
        SELECT E1.N
        FROM FOURTH_NUMBERS E1
        CROSS JOIN FOURTH_NUMBERS E2
), NUMBERS (N) AS (
        SELECT N
        FROM NUMBERS
        WHERE N <= 8000 /*adjust these as needed to come up with a max number equal to the max character length allowed in the Values column*/
        /*or better yet, if you can, just remove this first...numbers... header stuff so long as you create a temp or permanent table that contains the same numbers to work with*/
)
SELECT SUBSTRING(
    MYTABLE.Values,
    CASE
    WHEN NUMBERS.NUMBER = 1 THEN 1
    ELSE NUMBERS.NUMBER + 1
    END,
    CASE CHARINDEX(';', MYTABLE.Values, NUMBERS.NUMBER + 1)
    WHEN 0 THEN LEN('^' + MYTABLE.Values + '^') - 2 + 1
    ELSE CHARINDEX(';', MYTABLE.Values, NUMBERS.NUMBER + 1)
    END
    - CASE
    WHEN NUMBERS.NUMBER = 1 THEN 1
    ELSE NUMBERS.NUMBER + 1
    END
) AS PARSED_VALUE
FROM MYTABLE
INNER JOIN NUMBERS
ON NUMBERS.NUMBER <= LEN('^' + MYTABLE.Values + '^') - 2
AND (
    NUMBERS.NUMBER = 1
    OR SUBSTRING(MYTABLE.Values, NUMBERS.NUMBER, 1) = ';'
)
GO

-- if your values column can contain NULL values I would change the join at the end as follows:  
--from INNER JOIN NUMBERS  
--to LEFT OUTER JOIN NUMBERS  

Вышеприведенное, вероятно, было бы наиболее эффективным, если бы CTE WITH NUMBERS ... были заменены временной или постоянной таблицей, содержащей те же числовые значения. С другой стороны, CTE выполняет свою работу и хранит ее в одном месте.

CREATE PROCEDURE PARSE_DELIMITED_VALUES
AS
SELECT E.x.value('.', 'VARCHAR(MAX)') AS PARSED_VALUE
FROM (
    SELECT CAST('<x>' + REPLACE(Values, ';', '</x><x>') + '</x>' AS XML) my_x
    FROM MYTABLE
) TT
CROSS APPLY my_x.nodes('/x') AS E(x)
GO

-- if your values column can contain NULL values I would change the join at the end as follows:  
from `CROSS APPLY`  
to `OUTER APPLY`  
1 голос
/ 08 ноября 2009

Раствор 1 (с использованием xml):

declare @str varchar(20)
declare @xml as xml
set @str= '1;2;3;4;5'
SET @xml = cast(('<x>'+replace(@str,';' ,'</x><x>')+'</x>') as xml)
SELECT col.value('.', 'varchar(10)') as value FROM @xml.nodes('x') as tbl(col)

Решение 2 (с использованием рекурсивного cte)

declare @str as varchar(100)
declare @delimiter as char(1)
set @delimiter = ';'
set @str = '1;2;3;4;5' -- original data
set @str = @delimiter + @str + @delimiter

;with num_cte as
(     
      select 1 as rn
      union all
      select rn +1 as rn 
      from num_cte 
      where rn <= len(@str)
)
, get_delimiter_pos_cte as
( 
      select      
                  ROW_NUMBER() OVER (ORDER BY rn) as rowid, 
                  rn as delimiterpos            
      from num_cte
      cross apply( select substring(@str,rn,1)  AS chars) splittedchars 
      where chars = @delimiter
)

select substring(@str,a.delimiterpos+1 ,c2.delimiterpos - a.delimiterpos - 1) as Countries
from get_delimiter_pos_cte a
inner join get_delimiter_pos_cte c2 on c2.rowid = a.rowid+1
option(maxrecursion 0)
0 голосов
/ 29 октября 2009

Вы действительно имеете в виду «строки», как в «кортежах» (чтобы вы могли вставлять данные в другую таблицу, по одному элементу в строке) или вы хотите, чтобы данные отображались вертикально?

Я бы подумал, что замена строки (посмотрите на строковые функции T-SQL) поможет, не так ли? В зависимости от выходной цели, вы замените; с CRLF или
. Вы даже можете использовать Replace для создания динамических операторов SQL Insert, которые могут быть выполнены SP для вставки строк (если это было вашим намерением).

Для целей презентации это плохая практика.

Если это чисто для презентации, и вам разрешено, я бы вывел все как XML, а затем XSLT как угодно. Честно говоря, я не помню, когда в последний раз я работал непосредственно с набором записей. Я всегда вывод на XML.

0 голосов
/ 29 октября 2009

Это не самый элегантный подход, но, возможно, стоит попробовать. Он создает команду Sql в виде строки и в конце выполняет ее.

DECLARE @Values VARCHAR(8000)
-- Flatten all values lists into one string
SET @Values = REPLACE(REPLACE((SELECT [Value] FROM [dbo.MyTable] FOR XML PATH('')), '<Value>', ''), '</Value>', ';')
SET @Values = SUBSTRING(@Values, 0, LEN(@Values))

DECLARE @SeparatorIndex INT
SET @SeparatorIndex = (SELECT TOP 1 PATINDEX('%[;]%', @Values))

DECLARE @InsertClause VARCHAR(50)
SET @InsertClause = 'INSERT INTO [dbo.MyTable] VALUES ('

DECLARE @SQL VARCHAR(500)
SET @SQL = @InsertClause + SUBSTRING(@Values, 0, @SeparatorIndex) + '); '

SET @Values = RIGHT(@Values, LEN(@Values) - (@SeparatorIndex - 1))

SET @SQL = REPLACE(@SQL + (SELECT (REPLACE(@Values, ';', '); ' + @InsertClause))) + ')', '; )', '')

EXEC (@SQL)

Команда заканчивается (в Sql Server 2005) как:

INSERT INTO [dbo.MyTable] VALUES (1); INSERT INTO [dbo.MyTable] VALUES (2); INSERT INTO [dbo.MyTable] VALUES (3); INSERT INTO [dbo.MyTable] VALUES (4); INSERT INTO [dbo.MyTable] VALUES (5) ...'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...