1

我必须放置一些列并按 STRING_AGG 将它们组合在一起我还想将每行的数字放在首位

我有的:

姓名 蛋糕 古柯 冰淇淋
相同的
萨拉 无效的
约翰 无效的

我希望输出是这样的:

姓名 描述
山姆 1.2 2.5 3.6
萨拉 1.一个 2.一个
约翰 1.二 2.二

我的代码:

SELECT Name, STRIN_AGG(CONCAT(Cake, ' ,', Coca,' ,', ice-cream))
FROM FoodTable

但我不知道如何首先考虑 STRING_AGG 中每行的数字

4

6 回答 6

3

你不需要string_agg()

select name,
       concat('1.' + cake, ' 2.' + coca, ' 3.' + ice_cream)
from t;

请注意,如果任何值为 ,则+返回。但是,只是忽略值。NULLNULLconcat()NULL

如果你真的,真的想使用string_agg()你可以:

select t.name, v.all_together
from t cross apply
     (select string_agg(v.n + v.val, ' ') within group (order by v.n) as all_together
      from (values ('1.', t.cake),
                   ('2.', t.coca),
                   ('3.', t.ice_cream)
           ) v(n, val)
     ) v;
于 2021-07-31T11:29:48.083 回答
2

这是一个完整的动态sql方法。无需使用 XML 或 JSON 对数据进行序列化和反序列化。在这种情况下,食品列表包含在一个临时表中,因此它从 tempdb.sys.columns 中读取列名。

该查询使用 CROSS APPLY 对(食品项目的)列进行反透视,并将 a 分配ROW_NUMBER()给每个非NULL项目值。像这样的东西

drop table if exists #FoodTable;
go
create table #FoodTable(
  [Name]            varchar(100) not null,
  Cake              varchar(100) null,
  Coca              varchar(100) null,
  [ice-cream]       varchar(100) null);
--select * from dbo.test_actuals

insert #FoodTable values
('Sam', 'one', 'five', 'six'),
('Sara', 'one', 'one', null),
('Jon', 'two', 'two', null);

;with unpvt_cte([Name], item, val, rn) as (
    select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
    from #FoodTable f
         cross apply (values ('Cake', Cake),
                             ('Coca', Coca),
                             ('IceCream', [ice-cream])) v(item, val)
    where v.val is not null)
select [Name], string_agg(concat(rn, '.', val), ' ') within group (order by rn) answer
from unpvt_cte
group by [Name];
Name        answer
Jon         1.two 2.two
Sam         1.one 2.five 3.six
Sara        1.one 2.one

使查询动态

declare @food_list              nvarchar(max);

select @food_list=string_agg(quotename(concat_ws(',', quotename(sysc.[name], ''''), 
                             quotename(sysc.[name], '[]')), '()'), ',')
from   tempdb.sys.columns sysc
where  object_id = Object_id('tempdb..#FoodTable')
       and [name]<>'Name'; 

declare 
  @sql_prefix             nvarchar(max)=N'
;with unpvt_cte([Name], item, val, rn) as (
    select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
    from #FoodTable f
         cross apply (values ',
  @sql_suffix             nvarchar(max)=N'
         ) v(item, val)
    where v.val is not null)
select [Name], string_agg(concat(rn, ''.'', val), '' '') within group (order by rn) answer
from unpvt_cte
group by [Name];';
declare
  @sql                    nvarchar(max)=concat(@sql_prefix, @food_list, @sql_suffix);

print(@sql);
exec sp_executesql @sql;

print 语句输出以下内容

;with unpvt_cte([Name], item, val, rn) as (
    select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
    from #FoodTable f
         cross apply (values ('Cake',[Cake]),('Coca',[Coca]),('ice-cream',[ice-cream])
         ) v(item, val)
    where v.val is not null)
select [Name], string_agg(concat(rn, '.', val), ' ') within group (order by rn) answer
from unpvt_cte
group by [Name];
于 2021-07-31T12:06:46.840 回答
1

您可以使用联合来获取每列的数字。在这里,我使用了 cte,但您可以使用子查询。联合中的每个查询将食物类型列重命名为food并添加num将在最终查询中使用的列。在最后的查询中,where 子句过滤 NULL 食物和一个 group by,string_aggandconcat用于检索所需格式的数据。我在下面包含了一个工作小提琴:

WITH FoodTableNums AS (
    SELECT Name, Cake as food, 1 as num FROM FoodTable UNION ALL
    SELECT Name, Coca as food, 2 as num FROM FoodTable UNION ALL
    SELECT Name, icecream as food, 3 as num FROM FoodTable
)
SELECT
    Name,
    STRING_AGG(CONCAT(num,'.', food),',' ) WITHIN GROUP( ORDER BY num asc) as Description
FROM
    FoodTableNums
WHERE
    food IS NOT NULL
GROUP BY
    Name
    
姓名 描述
约翰 1.二,2.二
山姆 1.1,2.5,3.6
萨拉 1.一,2.一

db<>在这里摆弄

让我知道这是否适合您。

于 2021-07-31T11:29:35.267 回答
0

这是一个更具动态性的选项。您只需要排除某些列...在这种情况下NAME

我们使用一点 JSON 来动态UNPIVOT行,然后string_agg()进行合并。

示例或dbFiddle

Select A.Name
      ,B.NewValue 
 From YourTable A
 Cross Apply (
                Select NewValue=STRING_AGG(concat(Seq,'.',Value),' ') within group (order by Seq)
                 From (
                         Select [Key]
                               ,[Value]
                               ,[Seq]  = row_number() over (order by @@spid)
                         From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper ) ) 
                         Where [Key] not in ('Name')
                      ) B1
             ) B

结果

Name    NewValue
Same    1.one 2.five 3.six
Sara    1.one 2.one
John    1.two 2.two
于 2021-07-31T12:12:31.873 回答
0

无论表中有多少列,这是通用的方法。

它基于 XML/XQuery。

不需要UNPIVOT行,然后STRING_AGG()合并。

每行中的所有数据都保持在一行中。

SQL

-- DDL and data population, start
DECLARE @tbl table  (
  [Name]            varchar(100) not NULL PRIMARY KEY,
  Cake              varchar(100) null,
  Coca              varchar(100) null,
  [ice-cream]       varchar(100) null);
INSERT @tbl VALUES
('Sam', 'one', 'five', 'six'),
('Sara', 'one', 'one', null),
('Jon', 'two', 'two', null);
-- DDL and data population, end

SELECT p.[Name] 
    , x.query('
    for $r in /root/*[local-name()!="Name"]/text()
    let $pos := count(root/*[. << $r]) - 1
    return concat(string($pos), ".", $r)').value('text()[1]', 'VARCHAR(MAX)') AS Result
FROM @tbl AS p
   CROSS APPLY (SELECT * FROM @tbl AS c
      WHERE c.[Name] = p.[Name]
      FOR XML PATH(''), TYPE, ROOT('root')) AS t(x);

输出

+------+--------------------+
| Name |       Result       |
+------+--------------------+
| Jon  | 1.two 2.two        |
| Sam  | 1.one 2.five 3.six |
| Sara | 1.one 2.one        |
+------+--------------------+
于 2021-08-01T02:09:04.943 回答
0

虽然我同意那些说规范化表会更好的说法,但如果你不能这样做,那么这个提议会使 GGordon 的解决方案动态化,构建一个检索 FoodTable 上所有列的 SQL 语句。无论它们是 3 或 100 个食物栏。

CREATE TABLE FoodTable (
  Name VARCHAR(4),
  Cake VARCHAR(3),
  Coca VARCHAR(4),
  icecream VARCHAR(4)
);

INSERT INTO FoodTable ("Name", "Cake", "Coca", "icecream")
VALUES ('Sam', 'one', 'five', 'six'),
       ('Sara', 'one', 'one', NULL),
       ('John', 'two', 'two', NULL);

declare @SQL nvarchar(max);    

WITH Food As (  
  SELECT ORDINAL_POSITION - 1 AS Num, COLUMN_NAME AS Food
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_NAME = N'FoodTable' AND COLUMN_NAME <> 'Name'
)
SELECT @SQL = N'WITH FoodTableNums AS ( ' +
                string_agg('SELECT Name, ' + Food + ' as Food, ' + convert(varchar(20), Num) + ' as Num FROM FoodTable', ' UNION ALL ') + 
                ') SELECT Name, STRING_AGG(CONCAT(num,''.'', food),'','' ) WITHIN GROUP( ORDER BY num asc) as Description FROM FoodTableNums WHERE food IS NOT NULL GROUP BY Name' 
FROM Food; 

EXECUTE sp_ExecuteSQL @SQL;

你可以看到它在这里工作:小提琴

于 2021-07-31T12:33:30.150 回答