优化查询以拆分已知大小的字符串

jdm*_*eon 5 performance sql-server azure-sql-database functions string query-performance

我有一个带有 . 作为分隔符,它看起来像这样......

abc.efg.hij 
Run Code Online (Sandbox Code Playgroud)

我想要一个将其转换为三列 Col1、Col2 和 Col3 的查询。我想知道最快的方法是什么。到目前为止,由于我有限的数据库经验,我还没有做得很好。我有一个功能:

CREATE FUNCTION [dbo].[split](
   @delimited NVARCHAR(MAX),
   @delimiter NVARCHAR(100)
 ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
 AS
 BEGIN
   DECLARE @xml XML
   SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'

   INSERT INTO @t(val)
   SELECT  r.value('.','varchar(MAX)') as item
   FROM  @xml.nodes('/t') as records(r)
   RETURN
 END
Run Code Online (Sandbox Code Playgroud)

这就是我现在正在做的事情,但我相信它可以做得更快,我也愿意接受明显更好的功能或用于拆分字符串的开箱即用的想法。我相信我已经运行dbo.split(Name, '.')了三遍并且只能运行一次。

SELECT
      Col1 = (SELECT Val from dbo.split(Name, '.') WHERE Id = '1'),
      Col2 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '2'),
      Col3 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '3')
FROM Mains
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激

Rob*_*ley 7

代替:

SELECT
  Col1 = (SELECT Val from dbo.split(Name, '.') WHERE Id = '1'),
  Col2 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '2'),
  Col3 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '3')
FROM Mains
Run Code Online (Sandbox Code Playgroud)

用:

SELECT
  s.*
FROM Mains
CROSS APPLY (
    SELECT
        MAX(CASE WHEN Id = 1 THEN Val END) AS Col1,
        MAX(CASE WHEN Id = 2 THEN Val END) AS Col2,
        MAX(CASE WHEN Id = 3 THEN Val END) AS Col3
    FROM dbo.split(Name,'.') s
    ) s 
Run Code Online (Sandbox Code Playgroud)

这个想法是你仍然希望在 Mains 中每行正好有一行。在内部使用聚合函数CROSS APPLY就可以做到这一点。通过使用 CASE,您只需split()每行调用一次。

这里有很多关于拆分字符串的问题,SQL DB 已经string_split()内置了一个函数。


Mar*_*ith 7

由于分隔符是一个点,一个非常简洁的方法是

SELECT Col1 =  PARSENAME(name,3), 
       Col2 =  PARSENAME(name,2), 
       Col3 =  PARSENAME(name,1)
FROM Mains
Run Code Online (Sandbox Code Playgroud)

以上确实不依赖于任何一个组件部分的长度超过 128 个字符,尽管PARSENAME它用于解析点分隔的 SQL Server 对象名称,NULL如果它们不适合SYSNAME

我没有做过任何性能测试。我怀疑下面的效果可能会更好。

SELECT Col1 = LEFT(name, FirstDot - 1),
       Col2 = SUBSTRING(name, FirstDot + 1, SecondDot - FirstDot - 1),
       Col3 = SUBSTRING(name, 1 + SecondDot, 8000)
FROM   Mains
       CROSS APPLY (VALUES(CHARINDEX('.', name))) V1(FirstDot)
       CROSS APPLY (VALUES(CHARINDEX('.', name, 1 + FirstDot))) V2(SecondDot) 
Run Code Online (Sandbox Code Playgroud)