【问题标题】:Concatenate the result of an ordered String_Split in a variable将有序 String_Split 的结果连接到变量中
【发布时间】:2016-06-30 17:20:15
【问题描述】:

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

使用 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

但是,这只会返回最后一个值,StackExchange。我已经注意到该答案中的警告,即此技巧仅适用于某些执行计划,如 here 所述。

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

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

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

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

我准备了一个SEDE Query 来进行实验。预期的数据库名称要么不带点,也就是 StackOverflow,带 1 个点:StackOverflow.Meta 或 2 个点,`StackExchange.Audio.Meta,数据库的完整列表是 here

【问题讨论】:

  • 为什么不data.stackexchange.com/meta.avp/query/506813?哦,关于您使用变量分配方法的愿望,永远无法保证它会起作用。 stackoverflow.com/questions/15138593/…
  • 我意识到我正在使用未定义的行为,所以我也接受它作为答案。
  • 没有真正回答这个问题,但是如果您正在使用所有这些字符串操作生成 URL,为什么不只使用一个查找表呢?在您的 SEDE 查询中,您已经在做一些时髦的事情(when 'audio 然后是Avp),所以为所有这些准备一个翻译表不是更好吗?
  • @DavidG 到目前为止只有两种特殊情况,因为我每次都必须复制它,所以我不想在添加新站点/.databases 时有任何需要维护的东西。但我考虑过,是的。

标签: sql-server tsql sql-order-by


【解决方案1】:

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

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

【讨论】:

  • 这很好用,我喜欢它完全无视我的尝试;)。这不是 parsename 最初设计的地方,但如果你(和 Martin)没有向我指出,我不会检查或尝试这个。谢谢。
  • @rene 我只是觉得你的方法过于复杂,抱歉。没有看到 MartinSmith 的评论,但我现在读了它更有信心;-)
【解决方案2】:

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

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

返回

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

【讨论】:

  • UDF 函数很棒,我喜欢并且会使用它。不幸的是,在 SEDE 上,我们没有创建函数权限。
  • @rene 对于普通人来说,看到 22K 代表提出问题仍然令人耳目一新。
  • 嗯,是的,在敲了我 2 个小时的头之后,我已经花了很多时间在上面,我不想放弃......
【解决方案3】:

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

;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

【讨论】:

    【解决方案4】:

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

    你可以使用CONCAT:

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

    反转是通过CONCAT的参数顺序来完成的。 Here's an example.

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

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

    另请注意,vNext 将添加STRING_AGG

    【讨论】:

      【解决方案5】:

      为了回答这个 XY 问题的“X”,并解决 HTTPS 开关(尤其是元站点)和其他一些站点名称更改,我编写了以下 SEDE query,它以格式输出所有站点名称用于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('StackOverflow.%', name) > 0,
                  CASE SUBSTRING(name, 15, 200)
                  WHEN 'Br' THEN 'pt'
                  WHEN 'Br.Meta' THEN 'pt.meta'
                  ELSE SUBSTRING(name, 15, 200)
                  END + '.stackoverflow.com',
                  IIF(PATINDEX('%.Meta', name) > 0,
                    'meta.' + SUBSTRING(name, 0, PATINDEX('%.Meta', name)) + '.com',
                    name + '.com'
                  )
                )
              )
            )
          ) + '/'
        )
        FROM sys.databases WHERE database_id > 5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-21
        • 2020-09-23
        • 1970-01-01
        相关资源
        最近更新 更多