如何在T-sql中累积值? AFAIK没有ARRAY类型.
我想重新使用相同查询中的值,如使用 array_agg()在此Postgresql示例中演示的那样.
我想重新使用相同查询中的值,如使用 array_agg()在此Postgresql示例中演示的那样.
SELECT a[1] || a[i] AS foo,a[2] || a[5] AS bar -- assuming we have >= 5 rows for simplicity FROM ( SELECT array_agg(text_col ORDER BY text_col) AS a,count(*)::int4 AS i FROM tbl WHERE id between 10 AND 100 ) x
我如何用T-sql最好地解决这个问题?
我能想出的最好的是两个CTE和子选择:
;WITH x AS ( SELECT row_number() OVER (ORDER BY name) AS rn,name AS a FROM #t WHERE id between 10 AND 100 ),i AS ( SELECT count(*) AS i FROM x ) SELECT (SELECT a FROM x WHERE rn = 1) + (SELECT a FROM x WHERE rn = i) AS foo,(SELECT a FROM x WHERE rn = 2) + (SELECT a FROM x WHERE rn = 5) AS bar FROM i
测试设置:
CREATE TABLE #t( id INT PRIMARY KEY,name NVARCHAR(100)) INSERT INTO #t VALUES (3,'John'),(5,'Mary'),(8,'Michael'),(13,'Steve'),(21,'Jack'),(34,'Pete'),(57,'Ami'),(88,'Bob')
有更简单的方法吗?
解决方法
编辑1:我添加了另一个解决方案,显示如何在sql Server上模拟ARRAY_AGG(最后一个答案).
我不确定我是否理解你的问题.
a)不使用sql Server中的数组,而是使用表变量或XML.
b)要连接字符串(在本例中),我将使用SELECT @var = @var Name FROM tbl语句或XML xqueries.
c)基于CTE和多个子查询的解决方案(WITH cte AS()FROM SELECT(SELECT * FROM cte.rn = 1)()…)将生成大量扫描和逻辑读取.
解决方案:
1)表变量SELECT @var = @var Name FROM tbl:
--Creating the "array" DECLARE @Array TABLE ( Idx INT PRIMARY KEY,Val NVARCHAR(100) NOT NULL ); WITH Base AS ( SELECT Val = t.name,Idx = ROW_NUMBER() OVER(ORDER BY t.name ASC) FROM #t t WHERE t.id between 10 AND 100 ) INSERT @Array (Idx,Val) SELECT b.Idx,b.Val FROM Base b; --Concatenating all names DECLARE @AllNames NVARCHAR(4000); --”Reset”/Init @AllNames SET @AllNames = ''; --String concatenation SELECT @AllNames = @AllNames + ',' + a.Val FROM @Array a; --Remove first char (',') SELECT @AllNames = STUFF(@AllNames,1,''); --The final result SELECT @AllNames [Concatenating all names - using a table variable]; /* Concatenating all names - using a table variable ------------------------------------------------ Ami,Bob,Jack,Pete,Steve */ --Concatenating Idx=2 and Idx=5 --”Reset” @AllNames value SET @AllNames = ''; --String concatenation SELECT @AllNames = @AllNames + ',' + a.Val FROM @Array a WHERE a.Idx IN (2,5) --or a.Idx IN (2,(SELECT COUNT(*) FROM @Array)) ORDER BY a.Idx ASC; --Remove first char (',''); --The final result SELECT @AllNames [Concatenating Idx=2 and Idx=5 - using a table variable]; /* Concatenating Idx=2 and Idx=5 - using a table variable ------------------------------------------------------ Bob,Steve */
2)表变量PIVOT:
--Concatenating a finite number of elements (names) SELECT pvt.[1] + ',' + pvt.[0] AS [PIVOT Concat_1_and_i(0)],pvt.[2] + ',' + pvt.[5] AS [PIVOT Concat_2_and_5],pvt.* FROM ( SELECT a.Idx,a.Val FROM @Array a WHERE a.Idx IN (1,2,5) UNION ALL SELECT 0,a.Val --The last element has Idx=0 FROM @Array a WHERE a.Idx = (SELECT COUNT(*) FROM @Array) ) src PIVOT (MAX(src.Val) FOR src.Idx IN ([1],[2],[5],[0])) pvt; /* PIVOT Concat_1_and_i(0) PIVOT Concat_2_and_5 ----------------------- -------------------- Ami,Steve Bob,Steve */
3)XML XQuery:
SET ANSI_WARNINGS ON; GO DECLARE @x XML; ;WITH Base AS ( SELECT Val = t.name,Idx = ROW_NUMBER() OVER(ORDER BY t.name ASC) FROM #t t WHERE t.id BETWEEN 10 AND 100 ) SELECT @x = ( SELECT b.Idx AS [@Idx],b.Val AS [text()] FROM Base b FOR XML PATH('Element'),ROOT('Array') ); /* @x content <Array> <Element Idx="1">Ami</Element> <Element Idx="2">Bob</Element> <Element Idx="3">Jack</Element> <Element Idx="4">Pete</Element> <Element Idx="5">Steve</Element> </Array> */ --Concatenating all names (the result is XML,so a cast is needed) DECLARE @r XML; --XML result SELECT @r=@x.query(' (: $e = array element :) for $e in (//Array/Element) return string($e) '); SELECT REPLACE(CONVERT(NVARCHAR(4000),@r),' ',',') AS [Concatenating all names - using XML]; /* Concatenating all names - using XML ----------------------------------- Ami,Steve */ --Concatenating Idx=1 and all names SELECT @r=@x.query(' (: $e = array element :) for $e in (//Array/Element[@Idx=1],//Array/Element) return string($e) '); SELECT REPLACE(CONVERT(NVARCHAR(4000),') AS [Concatenating Idx=1 and all names - using XML]; /* Concatenating Idx=1 and all names - using XML --------------------------------------------- Ami,Ami,Steve */ --Concatenating Idx=1 and i(last name) DECLARE @i INT; SELECT @r=@x.query(' (: $e = array element :) for $e in (//Array/Element[@Idx=1],//Array/Element[@Idx=count(//Array/Element)]) return string($e) '); SELECT REPLACE(CONVERT(NVARCHAR(4000),') AS [Concatenating Idx=1 and i(last name) - using XML]; /* Concatenating Idx=1 and i(last name) - using XML ------------------------------------------------ Ami,Steve */ --Concatenating Idx=2 and Idx=5 SELECT @r=@x.query(' (: $e = array element :) for $e in (//Array/Element[@Idx=2],//Array/Element[@Idx=5]) return string($e) '); SELECT REPLACE(CONVERT(NVARCHAR(4000),') AS [Concatenating Idx=2 and Idx=5 - using XML (method 1)]; /* Concatenating Idx=2 and Idx=5 - using XML (method 1) ---------------------------------------------------- Bob,Steve */ --Concatenating Idx=2 and Idx=5 SELECT @x.value('(//Array/Element)[@Idx=2][1]','NVARCHAR(100)') + ',' + @x.value('(//Array/Element)[@Idx=5][1]','NVARCHAR(100)') AS [Concatenating Idx=2 and Idx=5 - using XML (method 2)];; /* Concatenating Idx=2 and Idx=5 - using XML (method 2) ---------------------------------------------------- Bob,Steve */
4)如果问题是如何在sql Server上模拟ARRAY_AGG,那么一个答案可能是:使用XML.
例:
SET ANSI_WARNINGS ON; GO DECLARE @Test TABLE ( Id INT PRIMARY KEY,GroupID INT NOT NULL,Name NVARCHAR(100) NOT NULL ); INSERT INTO @Test (Id,GroupID,Name) VALUES (3,'Bob'); WITH BaseQuery AS ( SELECT a.GroupID,a.Name FROM @Test a WHERE a.Id BETWEEN 10 AND 100 ) SELECT x.*,CONVERT(XML,x.sqlServer_Array_Agg).query (' for $e in (//Array/Element[@Idx=1],//Array/Element[@Idx=count(//Array/Element)]) return string($e) ') AS [Concat Idx=1 and Idx=i (method 1)],x.sqlServer_Array_Agg).query(' let $a := string((//Array/Element[@Idx=1])[1]) let $b := string((//Array/Element[@Idx=count(//Array/Element)])[1]) let $c := concat($a,",$b) (: " is used as a string delimiter :) return $c ') AS [Concat Idx=1 and Idx=i (method 2)],x.sqlServer_Array_Agg).query (' for $e in (//Array/Element[@Idx=(1,count(//Array/Element))]) return string($e) ') AS [Concat Idx=1 and Idx=i (method 3)] FROM ( SELECT a.GroupID,(SELECT ROW_NUMBER() OVER(ORDER BY b.Name) AS [@Idx],b.Name AS [text()] FROM BaseQuery b WHERE a.GroupID = b.GroupID ORDER BY b.Name FOR XML PATH('Element'),ROOT('Array') ) AS sqlServer_Array_Agg FROM BaseQuery a GROUP BY a.GroupID ) x;
结果:
GroupID sqlServer_Array_Agg Concat Idx=1 and Idx=i (method 1) Concat Idx=1 and Idx=i (method 2) Concat Idx=1 and Idx=i (method 3) ------- ---------------------------------------------------------------------------------------------------------- --------------------------------- --------------------------------- --------------------------------- 1 <Array><Element Idx="1">Jack</Element><Element Idx="2">Steve</Element></Array> Jack Steve Jack,Steve Jack Steve 2 <Array><Element Idx="1">Ami</Element><Element Idx="2">Bob</Element><Element Idx="3">Pete</Element></Array> Ami Pete Ami,Pete Ami Pete