分享一个自己写的字符串分割 表函数

砸死牛顿的苹果 2018-03-04 05:50:32
加精
/*
by 砸死牛顿的苹果
qq 343774501
*/

-- 辅助数字表(可单独使用)
if object_id('getnums') is not null drop function getnums
go
create function [dbo].[getnums]
(@n int)
returns table as
return
with t1 as(select 1 n union all select 1)
,t2 as(select 1 n from t1,t1 a,t1 b,t1 c)
,t3 as(select 1 n from t2,t2 a,t2 b,t2 c)
,t4 as(select 1 n from t3,t3 a)
select top(@n) row_number()over(order by(select 1)) n from t4 order by n
go
-- 字符串分割函数
if object_id('fn_split') is not null drop function fn_split
go
create function fn_split(@s nvarchar(max),@split nvarchar(2))
returns table
as
return
with
t0 as (select substring(@s,n,1)ch,n from getnums(len(@s)))
,t1 as(select ch,rid=n-row_number()over(order by n)+1 from t0 where ch<>@split)
select rid,keys=(select ''+ch from t1 b where b.rid=a.rid for xml path(''))
from t1 a
group by rid
go

-- 举例测试
select * from fn_split('aaa,bb,cc,123',',')

/*
rid keys
1 aaa
2 bb
3 cc
4 123
*/
...全文
4129 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
我在sql2014上测试 版主的 Fun_String2ToStringArray 最快
吉普赛的歌 2018-04-03
  • 打赏
  • 举报
回复
引用 14 楼 ayalicer 的回复:
最近二分法 写了个fn_splitb 测试比 sql2016 自带string_split 快好几倍 sql2016 的string_split 效率接近 逐个分割字符步进 进行分割 这个帖子晚点我结掉 再发新写法吧
不可能吧? 你看我 #13 中那个系统自带的切分500个数只用了 3 毫秒哦……
  • 打赏
  • 举报
回复
最近二分法 写了个fn_splitb 测试比 sql2016 自带string_split 快好几倍 sql2016 的string_split 效率接近 逐个分割字符步进 进行分割 这个帖子晚点我结掉 再发新写法吧
xiaoxiangqing 2018-04-02
  • 打赏
  • 举报
回复
PCCYC 2018-04-02
  • 打赏
  • 举报
回复
吉普赛的歌 2018-04-02
  • 打赏
  • 举报
回复
/*
Microsoft SQL Server 2017 (RTM-CU5) (KB4092643) - 14.0.3023.8 (X64)   Mar  2 2018 18:24:44   
Copyright (C) 2017 Microsoft Corporation  Enterprise Edition: Core-based Licensing (64-bit) on 
Windows 10 Pro 10.0 <X64> (Build 15063: ) 
*/
--备用函数 
if object_id('getnums') is not null drop function getnums
go
create function [dbo].[getnums]
(@n int)
returns table as
return
with t1 as(select 1 n union all select 1)
,t2 as(select 1 n from t1,t1 a,t1 b,t1 c)
,t3 as(select 1 n from t2,t2 a,t2 b,t2 c)
,t4 as(select 1 n from t3,t3 a)
select top(@n) row_number()over(order by(select 1)) n from t4 order by n 
go
--1. 基于 xml path + row_number
if object_id('fn_split') is not null drop function fn_split
go
create function fn_split(@s nvarchar(max),@split nvarchar(2))
returns table
as
return 
with 
t0 as (select substring(@s,n,1)ch,n from getnums(len(@s)))
,
t1 as(select ch,rid=n-row_number()over(order by n)+1 from t0 where ch<>@split)
select 
rid,
keys=(select  ''+ch from t1 b where b.rid=a.rid for xml path(''))
from t1 a 
group by rid
go
--2. 基于 xml 的nodes
IF OBJECT_ID('[dbo].[Fun_String2ToStringArray]') IS NOT NULL 
	DROP FUNCTION [dbo].[Fun_String2ToStringArray]
GO
CREATE FUNCTION [dbo].[Fun_String2ToStringArray](@str NVARCHAR(MAX), @split NVARCHAR(10))
RETURNS @table TABLE ([item] NVARCHAR(max))
AS
BEGIN
    IF LEN(@split) = 0
      BEGIN
        SET @split = N','
      END

    DECLARE @xml XML;
    SET @xml = CONVERT(XML, '<x><![CDATA[' + replace(CONVERT(VARCHAR(MAX), @str), @split, ']]></x><x><![CDATA[') + ']]></x>')

    INSERT INTO @table
      SELECT item
      FROM   (SELECT c.value('text()[1]', 'nvarchar(4000)') [item]
              FROM   @xml.nodes('/x') t(c)) t
      WHERE  item IS NOT NULL

    RETURN
END
GO
--3. 从SQL Server2016起支持的分割表值函数 string_split , 不用写

--4. 下面是测试代码
DECLARE @str NVARCHAR(MAX);
SELECT @str='1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500';

SET NOCOUNT ON
SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT * FROM dbo.fn_split(@str,',') AS fs

SELECT * FROM dbo.[Fun_String2ToStringArray](@str,',') AS fs

SELECT * FROM string_split(@str,',') AS fs
/*
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 SQL Server 执行时间:
   CPU 时间 = 1187 毫秒,占用时间 = 1204 毫秒。
表 '#BD909E0C'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 181 毫秒。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 3 毫秒。
*/
专门在SQL Server2017 下测试了三种分割函数, 还是系统自带的 string_split 最快…… 当然, 楼主的精神可嘉, 值得我们学习。
唐诗三百首 2018-03-31
  • 打赏
  • 举报
回复
感谢分享.
wuhui420 2018-03-30
  • 打赏
  • 举报
回复
有用不有用不
weixin_41926650 2018-03-30
  • 打赏
  • 举报
回复
不错!!nice!!!
  • 打赏
  • 举报
回复
xml写法的 fn_split 我改名为 fn_Split2
  • 打赏
  • 举报
回复
declare @str nvarchar(max)='aaa,bb,cc,123' select @str=@str+','+@str from getnums(20) -- getnums表函数 我上面也已分享 select len(@str) select * from fn_Split2('aaa,bb,cc,123',',') select * from fn_split('aaa,bb,cc,123',',')
  • 打赏
  • 举报
回复
sql2016 没用过,改天上虚拟机试试看 xml方式 我对比下我的写法快4倍左右,而且还带每个key的 序号 rid 便于获取指定范围的key
吉普赛的歌 2018-03-04
  • 打赏
  • 举报
回复
刀客的想法确实比较新颖, 已推荐
吉普赛的歌 2018-03-04
  • 打赏
  • 举报
回复
谢谢分享, 不知楼主是否有与比较常见的 xml 切分函数(有兴趣还可以试 SQL Server2016系统自带的 string_split )比较过效率 ?
--1. 创建fn_Split函数. ( 切分字符串, 返回一个列名为id的表 )  
IF EXISTS(
       SELECT *
       FROM   dbo.sysobjects
       WHERE  id = OBJECT_ID('fn_Split')
              AND (TYPE = 'FN' OR TYPE = 'TF' OR TYPE = 'IF')
   )
    DROP FUNCTION fn_Split  
GO  
  
CREATE FUNCTION [dbo].[fn_Split]
(
	@str           VARCHAR(MAX),
	@separator     VARCHAR(10)
)
RETURNS TABLE
AS
	RETURN 
	(
	    --Example:  SELECT id FROM fn_Split('a,b,d,c',',')  
	    SELECT B.id
	    FROM   (
	               (
	               	   --A 的作用只是生成 '<v>a</v><v>b</v><v>d</v><v>c</v>' 的XML格式的数据, 提供数据源 
	                   SELECT [value] = CONVERT(XML, '<v>' + REPLACE(@str, @separator, '</v><v>') + '</v>')
	               ) A 
	               OUTER APPLY
	               (
	               	   --B 的作用是将A中的 XML 数据的值枚举出来转换成行
	                   SELECT id = N.v.value('.', 'varchar(100)') FROM   A.[value].nodes('/v') N(v)
	               ) B
	           )
	)
GO
  • 打赏
  • 举报
回复
测试过;另一个案例,用spt_values表性能反而降低了,虽然它有索引
卖水果的net 2018-03-04
  • 打赏
  • 举报
回复
如果逗号不多的话,比如少于2048个,可以考虑使用spt_values表。

11,848

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 非技术版
社区管理员
  • 非技术版社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧