Пользовательская функция агрегации (concat) в SQL Server
Вопрос: Я хочу написать настраиваемую агрегированную функцию, которая объединяет строку в группе.
Чтобы я мог сделать
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD1, FIELD2
Все, что я нахожу, это агрегированные функции SQL CRL, но мне нужен SQL, без CLR.
Изменить: 1
Запрос должен выглядеть так:
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD0
Изменить 2:
Это правда, что это невозможно без CLR.
Тем не менее, ответ на подзапрос наблюдателя может быть изменен, поэтому он не кодирует специальные символы XML.
Тонкое изменение для этого заключается в том, чтобы добавить это после "FOR XML PATH":
,
TYPE
).value('.[1]', 'nvarchar(MAX)')
Вот несколько примеров
DECLARE @tT table([A] varchar(200), [B] varchar(200));
INSERT INTO @tT VALUES ('T_A', 'C_A');
INSERT INTO @tT VALUES ('T_A', 'C_B');
INSERT INTO @tT VALUES ('T_B', 'C_A');
INSERT INTO @tT VALUES ('T_C', 'C_A');
INSERT INTO @tT VALUES ('T_C', 'C_B');
INSERT INTO @tT VALUES ('T_C', 'C_C');
SELECT
A AS [A]
,
(
STUFF
(
(
SELECT DISTINCT
', ' + tempT.B AS wtf
FROM @tT AS tempT
WHERE (1=1)
--AND tempT.TT_Status = 1
AND tempT.A = myT.A
ORDER BY wtf
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 2, ''
)
) AS [B]
FROM @tT AS myT
GROUP BY A
SELECT
(
SELECT
',äöü<>' + RM_NR AS [text()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
) AS XmlEncodedNoNothing
,
SUBSTRING
(
(
SELECT
',äöü<>' + RM_NR AS [data()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
)
,2
,10000
) AS XmlEncodedSubstring
,
(
STUFF
(
(
SELECT ',äöü<>' + RM_NR + CHAR(10)
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 1, ''
)
) AS XmlDecodedStuffInsteadSubstring
Ответы
Ответ 1
Вы не можете создавать собственные агрегаты вне CLR.
Единственный тип функций, которые вы можете записать в чистом T-SQL, - это скалярные и табличные функции.
Сравните страницы CREATE AGGREGATE, в котором перечислены только опции стиля CLR, CREATE FUNCTION, которые показывает параметры T-SQL и CLR.
Ответ 2
Посмотрите на что-то вроде. Это не агрегированная функция. Если вы хотите реализовать свою собственную агрегированную функцию, она должна быть CLR...
DECLARE @Table TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO @Table (ID,Val) SELECT 1, 'A'
INSERT INTO @Table (ID,Val) SELECT 1, 'B'
INSERT INTO @Table (ID,Val) SELECT 1, 'C'
INSERT INTO @Table (ID,Val) SELECT 2, 'B'
INSERT INTO @Table (ID,Val) SELECT 2, 'C'
--Concat
SELECT t.ID,
SUM(t.ID),
stuff(
(
select ',' + t1.Val
from @Table t1
where t1.ID = t.ID
order by t1.Val
for xml path('')
),1,1,'') Concats
FROM @Table t
GROUP BY t.ID
Ответ 3
Это решение работает без необходимости развертывания из Visual Studio или DLL файла на сервере.
Скопировать-Вставить и работать!
http://groupconcat.codeplex.com/
dbo.GROUP_CONCAT(VALUE )
dbo.GROUP_CONCAT_D(VALUE ), DELIMITER )
dbo.GROUP_CONCAT_DS(VALUE , DELIMITER , SORT_ORDER )
dbo.GROUP_CONCAT_S(VALUE , SORT_ORDER )
Ответ 4
Обнаружено ссылка вокруг конкатенации, которая охватывает такие методы, как
Конкатенация значений, когда количество элементов неизвестно
- Рекурсивный метод CTE
- XML-методы blackbox
- Использование режима Common Language Runtime
- Скалярный UDF с рекурсией
- Таблица с оценкой UDF с циклом WHILE
- Динамический SQL
- Подход курсора
Ненадежные подходы
- Скалярный UDF с расширением расширения t-SQL
- Скалярный UDF с конкатенацией переменных в SELECT
Хотя он не охватывает функции aggerate, может быть полезно использовать конкатенацию, чтобы помочь вам в решении вашей проблемы.
Ответ 5
Вы можете сделать что-то вроде того, что я сделал ниже, чтобы создать настраиваемую функцию конкатенации агрегатов в чистом T-SQL. Очевидно, что я пошел с жестко закодированным именем таблицы и по столбцу, но должен проиллюстрировать этот подход. Вероятно, есть некоторый способ сделать это поистине универсальной функцией, используя динамический TSQL, построенный из входных параметров.
/*
User defined function to help perform concatenations as an aggregate function
Based on AdventureWorks2008R2 SalesOrderDetail table
*/
--select * from sales.SalesOrderDetail
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = N'fnConcatenate')
DROP FUNCTION fnConcatenate
GO
CREATE FUNCTION fnConcatenate
(
@GroupByValue int
)
returnS varchar(8000)
as
BEGIN
DECLARE @SqlString varchar(8000)
Declare @TempStore varchar(25)
select @SqlString =''
Declare @MyCursor as Cursor
SET @MyCursor = CURSOR FAST_FORWARD
FOR
Select ProductID
From sales.SalesOrderDetail where SalesOrderID = @GroupByValue
order by SalesOrderDetailID asc
OPEN @MyCursor
FETCH NEXT FROM @MyCursor
INTO @TempStore
WHILE @@FETCH_STATUS = 0
BEGIN
select @SqlString = ltrim(rtrim(@TempStore )) +',' + ltrim(rtrim(@SqlString))
FETCH NEXT FROM @MyCursor INTO @TempStore
END
CLOSE @MyCursor
DEALLOCATE @MyCursor
RETURN @SqlString
END
GO
select SalesOrderID, Sum(OrderQty), COUNT(*) as DetailCount , dbo.fnConcatenate(salesOrderID) as ConCatenatedProductList
from sales.SalesOrderDetail
where salesOrderID= 56805
group by SalesOrderID