【问题标题】:WITH-statement inside Outer Apply外部应用内的 WITH 语句
【发布时间】:2021-09-11 03:15:53
【问题描述】:

我正在使用for xml path 来汇总值:

select max(x.Ids), max(x.Number), (select country,city for json path) as [Json]
from t
outer apply (
    select Stuff((select ',' + Convert(varchar(10),t2.Id)
    from t t2
    where t2.city=t.city and t2.country=t.country
    for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Ids,
    Stuff((select ',' + Convert(varchar(10),t2.Number)
    from t t2
    where t2.city=t.city and t2.country=t.country
    for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Numbers
)x

outer apply 中的选择案例/查询是相同的。我想知道是否可以重用这个查询?我尝试在 outer apply 内创建一个 CTE,但这似乎不被 SQL Server 接受:

select max(x.Ids), max(x.Number), (select country,city for json path) as [Json]
from t
outer apply (
    with Test_CTE( Id, Number) AS (
        SELECT ID, Number FROM t t2
        where t2.city=t.city and t2.country=t.country
    )
    select Stuff((select ',' + Convert(varchar(10),t2.Id)
    from Test_CTE t2        
    for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Ids,
    Stuff((select ',' + Convert(varchar(10),t2.Number)
    from Test_CTE t2 
    for xml path(''),type).value('(./text())[1]','varchar(10)'),1,1,'') as Numbers
)x

上述尝试给出以下错误:

关键字“with”附近的语法不正确。

【问题讨论】:

  • 您不能以这种方式使用公用表表达式。您使用的是什么版本的 SQL Server?
  • Unlick 子查询,CTE 必须在语句的开头定义。 WITH CTE AS ({select statement here}) 应该出现在实际的 select 语句之前。
  • CTE 是在你开始的 start 定义的,而不是中间。

标签: sql-server with-statement for-xml-path


【解决方案1】:

在您的 for xml 中,您可以包含两列,避免与 ',' 连接,并保留列的名称,以便输出包含包含在元素中的值。

然后,在外部逻辑中,您可以查询有问题的特定元素,转换为字符串,将关闭标签替换为空白字符串,将打开标签替换为逗号,并将第一个逗号替换为stuff

您还可以考虑在开始保存一些处理之前对您的源应用distinct 子句。

所以,对于这样的数据:

declare @t table (Id int, Number int, country char(1), city char(1));
insert @t values 
    (1, 101, 'a', 'z'),
    (2, 102, 'b', 'y'),
    (3, 103, 'a', 'z');

你可以这样做:

select      [Json] = (select t.country, t.city for json path),
        
            Ids = stuff(replace(replace(convert(varchar(max),
                ap._xml.query('./Id')
            ),'<Id>', ','),'</Id>',''), 1, 1, ''),
        
            Nums = stuff(replace(replace(convert(varchar(max),
                ap._xml.query('./Number')
            ),'<Number>', ','),'</Number>',''), 1, 1, '')

from        (select distinct t.city, t.country from @t t) t
cross apply (select _xml = (
                select  t2.Id, 
                        t2.Number
                from    @t t2
                where   t2.city = t.city and t2.country = t.country 
                for     xml path(''), type
            )) ap;

得到这样的输出:

Json Ids Nums
[{"country":"b","city":"y"}] 2 102
[{"country":"a","city":"z"}] 1,3 101,103

但我不知道您是否认为新方法比原来的方法更优雅。我会说它是,但仍然很丑陋。请记住,较新版本的 sql server 具有 string_agg 函数,可以为您完成这一切。

【讨论】:

  • 谢谢!这正是我想要的。与旧解决方案相比,给了我显着的性能提升。我在 SQL Server 2016 上,因此使用“for XML path”而不是 string_agg。我注意到的一件事是,如果 Id 或 Number 中的一个值丢失,那么东西/替换将留下一个 。但这当然可以更换。
  • @Charlieface,这里的重点是字符串聚合:将列中的字符串值转换为单个单元格中的 csv。 .value 只检索第一个元素,这不是我们需要的。
猜你喜欢
  • 2010-09-20
  • 2013-08-19
  • 2021-08-24
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 2020-08-22
  • 2023-01-13
相关资源
最近更新 更多