Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server SQL Server:临时收集聚合中的值,并在同一查询中重复使用_Sql Server_Arrays_Tsql_Aggregate Functions - Fatal编程技术网

Sql server SQL Server:临时收集聚合中的值,并在同一查询中重复使用

Sql server SQL Server:临时收集聚合中的值,并在同一查询中重复使用,sql-server,arrays,tsql,aggregate-functions,Sql Server,Arrays,Tsql,Aggregate Functions,如何在T-SQL中累积值?AFAIK没有数组类型。 我想在同一个查询中重复使用这些值,就像这个PostgreSQL示例中使用的一样 我如何用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 ( SELEC

如何在T-SQL中累积值?AFAIK没有数组类型。 我想在同一个查询中重复使用这些值,就像这个PostgreSQL示例中使用的一样

我如何用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')

有更简单的方法吗?

不确定这是否有帮助,但您可以始终

select * into #MyTempTable from SomeTable

如果只是收集一些要重用的值,请尝试使用表变量而不是临时表

DECLARE @t TABLE 
(
    id INT PRIMARY KEY,
    name NVARCHAR(100)
)

INSERT @t VALUES (3 , 'John')
-- etc
table变量仅在内存中,而不是像temp表一样进入tempdb数据库

查看更多信息。

编辑1:我添加了另一个解决方案,该解决方案演示了如何在SQL Server上模拟ARRAY_AGG,这是最后一个答案

编辑2:对于解决方案4,我添加了第三种连接方法

我不确定我是否正确理解了你的问题

a我将使用表变量或XML,而不是在SQLServer中使用数组

b在本例中,为了连接字符串,我将使用SELECT@var=@var+Name FROM tbl语句或XML XQuery

c基于cte和多个子查询的解决方案,其中cte为SELECT*FROM cte.rn=1+。。。将生成大量扫描和逻辑读取

解决方案: 1表变量+从tbl中选择@var=@var+名称:

2表变量+轴:

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,Bob,Jack,Pete,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), @r), ' ', ',') AS [Concatenating Idx=1 and all names - using XML];
/*
Concatenating Idx=1 and all names - using XML
---------------------------------------------
Ami,Ami,Bob,Jack,Pete,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), @r), ' ', ',') 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), @r), ' ', ',') 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
*/
结果:

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

数组在tsql中称为表、表变量、临时表或游标。我在回答中添加了第四个解决方案,该解决方案演示了如何在SQL Server上模拟array_AGG。虽然没有实际回答我的问题聚合并在同一查询中重复使用,但这很有趣。非常有用的链接。在内存压力下,属于表变量的页面可以推送到tempdb。@BogdanSahlean谢谢,我不知道-很高兴知道。回答得很好!教育性、综合性、格式良好。解决方案4解决了所提出的问题。两件小事。1要使代码通用,您可能应该在有NVARCHAR4000的地方使用NVARCHARmax。2为了使解决方案4更适合这个问题,您可能希望在输出中包含最后一个元素count//Array/element,如上所示。一旦我的问题有资格为这个特殊的答案奖励额外的分数,我就会开始奖励。我更新了我的答案。通常,[N]VARCHARmax数据类型的性能低于[N]VARCHARn数据类型的性能。
--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           
*/
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,Bob,Jack,Pete,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), @r), ' ', ',') AS [Concatenating Idx=1 and all names - using XML];
/*
Concatenating Idx=1 and all names - using XML
---------------------------------------------
Ami,Ami,Bob,Jack,Pete,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), @r), ' ', ',') 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), @r), ' ', ',') 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
*/
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 , 1, 'John')
,(5 , 1, 'Mary')
,(8 , 1, 'Michael')
,(13, 1, 'Steve')
,(21, 1, 'Jack')
,(34, 2, 'Pete')
,(57, 2, 'Ami')
,(88, 2, '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)]
        , CONVERT(XML,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)]
        , CONVERT(XML,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