将有序 String_Split 的结果连接到一个变量中

Concatenate the result of an ordered String_Split in a variable

在我使用的 Sql 服务器数据库中,数据库名称类似于 StackExchange.Audio.MetaStackExchange.AudioWhosebug。幸运的是,这也是一个网站的 url。我只需要在点上拆分它并反转它:meta.audio.stackexchange。添加 http://.com 就完成了。显然Whosebug不需要任何反转。

使用 SqlServer 2016 string_split 函数,我可以轻松拆分和重新排序其结果:

select value
from string_split(db_name(),'.')
order by row_number() over( order by (select 1)) desc

这给了我

|  Value        |
-----------------
| Meta          |
| Audio         |
| StackExchange |

因为我需要在变量中包含 url 我希望使用这个 answer 连接它所以我的尝试看起来像这样:

declare @revname nvarchar(150)
select @revname = coalesce(@revname +'.','')  + value
from string_split(db_name(),'.')
order by row_number() over( order by (select 1)) desc

然而这只是 returns 我的最后一个值,StackExchange。我已经注意到该答案中的警告,该技巧仅适用于某些执行计划,如 here.

所述

问题似乎是由 order by 子句引起的。如果没有它,我将获得所有值,但顺序错误。我尝试按照 Microsoft 文章中的建议添加 ltrimrtrim 函数以及子查询,但到目前为止没有运气。

有没有办法让 Sql Server 2016 查询引擎将 string_split 的有序结果连接到变量中?

我知道我可以使用 for XML 甚至普通光标来获得我需要的结果,但我还不想放弃这个优雅的解决方案。

因为我是 运行 在 Stack Exchange Data Explorer 上,所以我无法使用函数,因为我们没有创建这些函数的权限。我可以执行存储过程,但我希望我可以避开这些。

我准备了一个SEDE Query to experiment with. The database names to expect are either without dots, aka Whosebug, with 1 dot: Whosebug.Meta or 2 dots, `StackExchange.Audio.Meta, the full list of databases is here

我认为你把事情复杂化了。你可以使用 PARSENAME:

SELECT 'http://' + PARSENAME(db_name(),1) + 
       ISNULL('.' + PARSENAME(db_name(),2),'') + ISNULL('.'+PARSENAME(db_name(),3),'') 
       + '.com'

这正是我在拆分函数中使用演示序列 (PS) 的原因。人们经常嘲笑对此类项目使用 UDF,但解析某些内容供以后使用通常是一次性的。

Select * from [dbo].[udf-Str-Parse]('meta.audio.stackexchange','.')

Returns

Key_PS  Key_Value
1       meta
2       audio
3       stackexchange

UDF

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('meta.audio.stackexchange','.')
--       Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--       Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')

Returns @ReturnTable Table (Key_PS int IDENTITY(1,1) NOT NULL , Key_Value varchar(max))

As

Begin
   Declare @intPos int,@SubStr varchar(max)
   Set @IntPos = CharIndex(@delimeter, @String)
   Set @String = Replace(@String,@delimeter+@delimeter,@delimeter)
   While @IntPos > 0
      Begin
         Set @SubStr = Substring(@String, 0, @IntPos)
         Insert into @ReturnTable (Key_Value) values (@SubStr)
         Set @String = Replace(@String, @SubStr + @delimeter, '')
         Set @IntPos = CharIndex(@delimeter, @String)
      End
   Insert into @ReturnTable (Key_Value) values (@String)
   Return 
End

可能不太优雅的解决方案,但它只需要几行并且可以使用任意数量的点。

;with cte as (--build xml
  select 1 num, cast('<str><s>'+replace(db_name(),'.','</s><s>')+'</s></str>' as xml) str
)
,x as (--make table from xml
    select row_number() over(order by num) rn, --add numbers to sort later
       t.v.value('.[1]','varchar(50)') s
    from cte cross apply cte.str.nodes('str/s') t(v)
)
--combine into string
select STUFF((SELECT '.' + s AS [text()]
            FROM x
            order by rn desc --in reverse order
            FOR XML PATH('')
            ), 1, 1, '' ) name

Is there a way I can nudge the Sql Server 2016 Query Engine to concatenate the ordered result from that string_split in a variable?

你可以只使用 CONCAT:

DECLARE @URL NVARCHAR(MAX)
SELECT @URL = CONCAT(value, '.', @URL) FROM STRING_SPLIT(DB_NAME(), '.')
SET @URL = CONCAT('http://', LOWER(@URL), 'com');

反转是通过参数的顺序完成的CONCATHere's an example.

它将 StackExchange.Garage.Meta 更改为 http://meta.garage.stackexchange.com

这通常可用于拆分和反转字符串,但请注意它确实会留下尾随分隔符。我确定您可以在其中添加一些逻辑或 COALESCE 来避免这种情况发生。

另请注意,vNext 将添加 STRING_AGG

为了回答这个 XY 问题的 'X',并解决 HTTPS 切换(特别是对于 Meta 站点)和一些其他站点名称更改,我写了以下内容 SEDE query which outputs all site names in the format used on the network site list

SELECT name,
  LOWER('https://' +
    IIF(PATINDEX('%.Mathoverflow%', name) > 0,
    IIF(PATINDEX('%.Meta', name) > 0, 'meta.mathoverflow.net', 'mathoverflow.net'),
      IIF(PATINDEX('%.Ubuntu%', name) > 0,
      IIF(PATINDEX('%.Meta', name) > 0, 'meta.askubuntu.com', 'askubuntu.com'),
        IIF(PATINDEX('StackExchange.%', name) > 0,
          CASE SUBSTRING(name, 15, 200)
          WHEN 'Audio' THEN 'video'
          WHEN 'Audio.Meta' THEN 'video.meta'
          WHEN 'Beer' THEN 'alcohol'
          WHEN 'Beer.Meta' THEN 'alcohol.meta'
          WHEN 'CogSci' THEN 'psychology'
          WHEN 'CogSci.Meta' THEN 'psychology.meta'
          WHEN 'Garage' THEN 'mechanics'
          WHEN 'Garage.Meta' THEN 'mechanics.meta'
          WHEN 'Health' THEN 'medicalsciences'
          WHEN 'Health.Meta' THEN 'medicalsciences.meta'
          WHEN 'Moderators' THEN 'communitybuilding'
          WHEN 'Moderators.Meta' THEN 'communitybuilding.meta'
          WHEN 'Photography' THEN 'photo'
          WHEN 'Photography.Meta' THEN 'photo.meta'
          WHEN 'Programmers' THEN 'softwareengineering'
          WHEN 'Programmers.Meta' THEN 'softwareengineering.meta'
          WHEN 'Vegetarian' THEN 'vegetarianism'
          WHEN 'Vegetarian.Meta' THEN 'vegetarianism.meta'
          WHEN 'Writers' THEN 'writing'
          WHEN 'Writers.Meta' THEN 'writing.meta'
          ELSE SUBSTRING(name, 15, 200)
          END + '.stackexchange.com',
          IIF(PATINDEX('Whosebug.%', name) > 0,
            CASE SUBSTRING(name, 15, 200)
            WHEN 'Br' THEN 'pt'
            WHEN 'Br.Meta' THEN 'pt.meta'
            ELSE SUBSTRING(name, 15, 200)
            END + '.whosebug.com',
            IIF(PATINDEX('%.Meta', name) > 0,
              'meta.' + SUBSTRING(name, 0, PATINDEX('%.Meta', name)) + '.com',
              name + '.com'
            )
          )
        )
      )
    ) + '/'
  )
  FROM sys.databases WHERE database_id > 5