Хорошо, я нашел способ выполнить то, что было после. Вставьте его, это будет неровно.
Таким образом, основная проблема - взять строку с двумя типами разделителей: записи и значения. Каждая запись представляет собой набор значений, и я хотел превратить строку в таблицу с одним столбцом для каждого значения для каждой записи. Я попытался сделать это UDF, но необходимость временной таблицы и динамического SQL означала, что она должна быть хранимой процедурой.
CREATE PROCEDURE [dbo].[ParseValueList]
(
@parseString varchar(8000),
@itemDelimiter CHAR(1),
@valueDelimiter CHAR(1)
)
AS
BEGIN
SET NOCOUNT ON;
IF object_id('tempdb..#ParsedValues') IS NOT NULL
BEGIN
DROP TABLE #ParsedValues
END
CREATE TABLE #ParsedValues
(
EntryID int,
[Rank] int,
Pair varchar(200)
)
Так что это просто базовая настройка, устанавливающая временную таблицу для хранения промежуточных результатов.
;WITH
E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),--Brute forces 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --Uses a cross join to generate 100 rows (10 * 10)
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --Uses a cross join to generate 10,000 rows (100 * 100)
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E4)
Это красивый кусок SQL приходит от SQL Server Central's Forums и зачисляется на «гуру». Это отличная таблица с десятью тысячами строк, идеально подходящая для разделения строк.
INSERT INTO #ParsedValues
SELECT ItemNumber AS EntryID, ROW_NUMBER() OVER (PARTITION BY ItemNumber ORDER BY ItemNumber) AS [Rank],
SUBSTRING(Items.Item, T1.N, CHARINDEX(@valueDelimiter, Items.Item + @valueDelimiter, T1.N) - T1.N) AS [Value]
FROM(
SELECT ROW_NUMBER() OVER (ORDER BY T2.N) AS ItemNumber,
SUBSTRING(@parseString, T2.N, CHARINDEX(@itemDelimiter, @parseString + @itemDelimiter, T2.N) - T2.N) AS Item
FROM cteTally T2
WHERE T2.N < LEN(@parseString) + 2 --Ensures we cut out once the entire string is done
AND SUBSTRING(@itemDelimiter + @parseString, T2.N, 1) = @itemDelimiter
) AS Items, cteTally T1
WHERE T1.N < LEN(@parseString) + 2 --Ensures we cut out once the entire string is done
AND SUBSTRING(@valueDelimiter + Items.Item, T1.N, 1) = @valueDelimiter
Хорошо, это первая действительно плотная мясистая часть. Внутренний выбор разбивает мою строку вдоль разделителя элемента (запятая), используя метод разделения строк гуру. Затем эта таблица передается внешнему элементу, который делает то же самое, но на этот раз использует разделитель значений (двоеточие) для каждой строки.Внутренний RowNumber (EntryID) и внешний RowNumber over Partition (Rank) являются ключевыми для оси. EntryID показывает, к какому элементу относятся значения, а Rank показывает порядковый номер значений.
DECLARE @columns varchar(200)
DECLARE @columnNames varchar(2000)
DECLARE @query varchar(8000)
SELECT @columns = COALESCE(@columns + ',[' + CAST([Rank] AS varchar) + ']', '[' + CAST([Rank] AS varchar)+ ']'),
@columnNames = COALESCE(@columnNames + ',[' + CAST([Rank] AS varchar) + '] AS Value' + CAST([Rank] AS varchar)
, '[' + CAST([Rank] AS varchar)+ '] AS Value' + CAST([Rank] AS varchar))
FROM (SELECT DISTINCT [Rank] FROM #ParsedValues) AS Ranks
SET @query = '
SELECT '+ @columnNames +'
FROM #ParsedValues
PIVOT
(
MAX([Value]) FOR [Rank]
IN (' + @columns + ')
) AS pvt'
EXECUTE(@query)
DROP TABLE #ParsedValues
END
И, наконец, динамический sql, который дает возможность. Получив список отличительных рангов, мы создали список столбцов. Затем он записывается в динамический стержень, который наклоняет значения и разбивает каждое значение на соответствующий столбец, каждый из которых имеет общий заголовок «Value #».
Таким образом, позвонив по номеру EXEC ParseValueList
с правильно отформатированной строкой значений, мы можем разбить его на таблицу, которая будет использоваться в наших целях! Он работает (но, вероятно, слишком много) для простых ключей: пары значений и масштабируется до достаточного количества столбцов (примерно 50, я думаю, но это было бы очень глупо).
В любом случае, надеемся, что помогает любому, у кого есть аналогичная проблема.
(Да, это, вероятно, можно было бы сделать в чем-то вроде SQLCLR как хорошо, но я нахожу большую радость в решении проблем с чистым SQL.)
Я был бы упущен, если бы не упомянул, что я использовал для этого решения концепции Брэда Шульца и Адама Мачанича. – etliens
Это довольно изящная концепция, использующая преобразование XML для обработки перевода. Я подумал об этом. Мне, возможно, придется более внимательно изучить ваше предложение, чтобы разобраться во всем, что происходит. Он не совсем делает то, что я надеялся, хотя и учитывает любое количество значений. Однако он представляет собой легко расширяемый запрос для выполнения известного числа значений. – CodexArcanum