T-SQL切割字符串方法小结
T-SQL切割字符串方法小结,只有表值函数那个是自己的思想,其它都是来源于网络的思想,请大家不要笑话,嘻嘻~网上大牛太多,这点东西虽然上不了台面,但是也算是自己的一个学习吧,能够对一个人有用也行。再不济也可以作为自己的参考笔记。
一、拼接动态SQL方法:
1. 把逗号替换为 ') insert into temptab values(' 讲逗号间的值截出来存入表变量,但是这种有些局限性
- CREATE PROCEDURE usp_SplitStr2 @str varchar(8000),@split_Del VARCHAR(10)
- WITH ENCRYPTION,EXECUTE AS OWNER
- AS
- BEGIN
- DECLARE @strSql varchar(8000)
- SELECT @strSql='DECLARE @temptab TABLE(id INT IDENTITY(1,1),col varchar(8000))
- insert into @temptab(col) values('''+REPLACE(@str,@split_Del,''')
- insert into @temptab(col) values(''')+''')
- SELECT col FROM @temptab'
- exec (@strSql)
- END
- -----------测试-------
- DECLARE @temptab TABLE(id INT IDENTITY(1,1),col varchar(8000))
- INSERT INTO @temptab(col)
- EXEC usp_SplitStr2 'a,b,c,d',','
- SELECT * FROM @temptab
----------测试-----
Result:
2. 把逗号替换为 UNION ALL 的方法,类似第一种方法
- /*巧生成 UNION ALL SELECT */
- declare @str varchar(24)='a,b,c,d,e,f,g,h,i,j,kbac'
- declare @sql varchar(max)='SELECT ''' ----为 a 加 前一个 单引号
- set @sql+=REPLACE(@str,',',''' Union all SELECT ''')+'''' ---为 最后一个 加 单引号
- EXEC (@SQL)
二、 利用 SQLServer 的 XQuery node,value方法
- declare @str varchar(24)='a,b,c,d,e,f,g,h,j,kafad',@split char(1)=','
- SELECT B.id
- FROM (
- SELECT [value] = CONVERT(XML , '<v>' + REPLACE(@str , @split , '</v><v>')
- + '</v>')
- ) A
- OUTER APPLY ( SELECT id = N.v.value('.' , 'varchar(100)')
- FROM A.[value].nodes('/v') N ( v )
- ) B
- 依据浅显的理解,自己写了个 SP 去拆分(因为函数内部貌似不允许print/select/raiserror 也不允许嵌套其它函数 真是蛋疼~~)
- CREATE PROCEDURE usp_SplitStrByXQuery(@Str VARCHAR(8000),@split_Del VARCHAR(10)) -- 这里定义成 varchar 的原因大家自己想想
- WITH ENCRYPTION,EXECUTE AS OWNER
- AS
- BEGIN
- DECLARE @temptab TABLE(oldStr VARCHAR(8000),newStr VARCHAR(8000))
- INSERT INTO @temptab
- ( oldStr ,
- newStr
- )
- SELECT tab.Old_Str ,
- LTRIM(RTRIM(xtab.newStr.value('.[1]', 'varchar(8000)')))
- FROM ( SELECT @Str AS Old_Str ,
- CONVERT(XML, '<xmlroot><v>' + REPLACE(@Str,
- @split_Del,
- '</v><v>')
- + '</v></xmlroot>') AS xml_Column
- ) tab
- CROSS APPLY xml_Column.nodes('/xmlroot/v') xtab ( newStr );
- SELECT * FROM @temptab
- END
- -----然而瞬间打脸,居然是可以的,顺便把 concatenate 的 for xml path 方法贴出来
- CREATE FUNCTION uf_SplitStrByXQuery(@Str NVARCHAR(4000),@split_Del NVARCHAR(10))
- RETURNS @temptab TABLE(orderID INT IDENTITY(1,1),splitStr NVARCHAR(4000))
- AS
- BEGIN
- INSERT INTO @temptab( splitStr )
- SELECT B.splitStr FROM
- (
- SELECT CONVERT(XML,'<v>'+REPLACE(@Str,@split_Del,'</v><v>')+'</v>') AS xmlColumn
- ) A
- CROSS APPLY
- (
- SELECT N.value.value('.[1]','NVARCHAR(4000)') AS splitStr FROM A.xmlColumn.nodes('/v')N(value)
- ) B
- RETURN
- END
- /* Concatenate String function can be created, using for xml path*/
- DECLARE @tmptab TABLE(id INT,strCol NVARCHAR(4000))
- INSERT INTO @tmptab( id, strCol )
- SELECT * FROM uf_SplitStrByXQuery('a,b,c',',')
- --SELECT * FROM @tmptab
- SELECT stuff((SELECT ','+strCol FROM @tmptab ORDER BY id FOR XML PATH('')),
- 1,1,'')
三 、 新颖的方法 摘自技术博客 略微有所修改
- declare @split_Del varchar(10)=','
- ;with StrTab(id,col) as
- (
- select 'A','LAS,AFE,FJWEO,JDOFW,AFD'
- UNION ALL
- SELECT 'B','WEOIW,FWOE,[IJWWJ,OIASJOW'
- UNION ALL
- SELECT 'C','A,B,C,D,E,F,G,H,I,J'
- ),
- Num(orderid)AS (
- select rank()over(order by number)
- from master..spt_values where number between 1 and 8000 group by number
- )
- SELECT s.id,ROW_NUMBER()over(partition by s.id order by s.id) as orderid,
- SUBSTRING(s.col,n.orderid,
- CHARINDEX(@split_Del,s.col+@split_Del,n.orderid)-n.orderid) as splited_Str
- FROM NUM n JOIN StrTab s on n.orderid<LEN(s.col) and SUBSTRING(@split_Del+s.col,n.orderid,1)=@split_Del
- order by s.id
- ---再改
- create function uf_SplitStr(@str varchar(8000),@split varchar(10))
- --with encryption,execute as owner
- returns @temptab table(oldStr varchar(8000),newStr varchar(8000))
- as
- begin
- ;with num_Recur as
- (
- select 1 as number
- union all
- select number+1 from num_Recur where number<=1000
- ),
- tab(strCol) as
- (
- select @str
- )
- insert into @temptab(oldStr,newStr)
- select t.strCol as oldStr,
- SUBSTRING(@str,
- number,
- CHARINDEX(@split,@str+@split,number)-number) as newStr
- from tab t join num_Recur n
- on n.number<LEN(t.strCol)
- and SUBSTRING(@split+@str,n.number,1)=@split
- option(maxrecursion 1000)
- return
- end
- select * from uf_SplitStr('a,b,c',',')
- select * from (values('a,b,c,d'),('e,f,h'),('k,i,l')) tab(strcol) cross apply uf_SplitStr(strcol,',') ftab
四、拆分字符串表值函数:
一根指针获取:
- CREATE FUNCTION uf_SplitStr(@Str NVARCHAR(4000),@split_Del NVARCHAR(10))
- RETURNS @tmpTab TABLE(id INT,col NVARCHAR(4000))
- WITH ENCRYPTION,EXECUTE AS OWNER
- AS
- BEGIN
- DECLARE @tmpStr NVARCHAR(4000),
- @chindex INT=1,
- @count INT=0
- SET @chindex=CHARINDEX(@split_Del,@Str,1) -- first charindex
- WHILE @chindex>=0
- BEGIN
- SET @count+=1
- SET @tmpStr=SUBSTRING(@Str,1,@chindex-1);
- --PRINT CAST(@count AS VARCHAR)+@tmpStr
- INSERT INTO @tmpTab
- (id,col )
- VALUES ( @count,@tmpStr
- )
- SET @Str=SUBSTRING(@Str,@chindex+1,LEN(@Str)-@chindex) -- recursively obtain substring of @Str
- --PRINT @Str
- SET @chindex=CHARINDEX(@split_Del,@Str,1)
- IF(@chindex=0 AND LEN(@Str)>0)
- BEGIN
- SET @Str=SUBSTRING(@Str,1,LEN(@Str)) -- last not contains comma
- INSERT INTO @tmpTab
- ( id, col )
- VALUES ( @count+1, -- id - int
- @Str -- col - nvarchar(4000)
- )
- BREAK -- not exists then break , out of cursor
- END
- END
- RETURN
- END
- GO
- /* Test Script Start*/
- IF OBJECT_ID('tempdb..#temptabSplit','U') IS NOT NULL DROP TABLE #temptabSplit
- GO
- CREATE TABLE #temptabSplit(uniq_id INT IDENTITY(1,1) ,colStr varchar(8000))
- GO
- INSERT INTO #temptabSplit(colStr)
- VALUES(',1,2,3,3434,545,,,,'),
- ('abcafeoijojsefjwoe')
- SELECT t.*,f.* FROM #temptabSplit t OUTER APPLY uf_SplitStr(t.colStr,',') f
- /* Test Script End*/
两根指针循环获取:
- ------以下代码可能有 bug 请慎重
- CREATE FUNCTION [dbo].[uf_SplitStr](@str varchar(8000),@split_Del varchar(10))
- returns @temptable table(col varchar(4000))
- as
- begin
- --declare @str varchar(max)='a,b,c,d,e,f,g,h,i,j,kbac'
- --declare @split_Del varchar(10)=',',
- declare @tempstr varchar(4000)=''
- declare @chindex_pre int=0,
- @chindex_next int=CHARINDEX(@split_Del,@str),@indcnt int=0
- while 1=1
- begin
- if (@chindex_pre<@chindex_next)
- BEGIN
- set @tempstr=SUBSTRING(@str,@chindex_pre+1,@chindex_next-@chindex_pre-1)
- --print @tempstr
- insert into @temptable values(@tempstr)
- END
- --if(@chindex_pre>@chindex_next)
- ELSE
- begin
- set @tempstr=SUBSTRING(@str,@chindex_pre+1,LEN(@str)-@chindex_pre)
- --print @tempstr
- insert into @temptable values(@tempstr)
- break
- end
- select @chindex_pre=@chindex_next
- set @chindex_next=CHARINDEX(@split_Del,@str,@chindex_pre+1)
- end
- return
- end
- GO
- 以前的脚本: 可能有不对
- declare @str varchar(8000)='a,b,c,d',
- @split varchar(10)=',',
- @Final_Str varchar(max)='',
- @Temp_Str varchar(128)
- declare @start int=0
- declare @location int
- declare @length int
- set @str=LTRIM(RTRIM(@str))-- trim begining and end space
- set @location=CHARINDEX(@split,@str)--First location
- SET @Final_Str=SUBSTRING(@str,@start+1,@location-1)-- the string before first loaction
- WHILE 1=1-- loop while exists split in str
- BEGIN
- SET @start=@location+1--Second change substr start location
- --set @length=(LEN(@Str)-@location)--length of new str
- SET @Str=SUBSTRING(@str,@start,LEN(@Str))
- SET @location=CHARINDEX(@split,@str)--redefine the location for split in str
- if @location!=0-- check if it is the last one, if so ,another logic to avoid errors.
- SET @Temp_Str=SUBSTRING(@str,1,@location-1)
- else set @Temp_Str=SUBSTRING(@str,1,@location+len(@str))
- SET @Final_Str+=@Temp_Str
- if @location=0 break
- END
- print @Final_Str
- 也是一根指针的,只是把最后一个拿出来做判断了
- CREATE FUNCTION dbo.ufn_SplitStringTODT
- (
- @SourceSql VARCHAR(MAX) ,
- @StrSeprate VARCHAR(10)
- )
- RETURNS @temp TABLE (ID INT IDENTITY(1,1),C1 INT )
- AS
- BEGIN
- DECLARE @i INT
- SET @SourceSql = RTRIM(LTRIM(@SourceSql))
- SET @i = CHARINDEX(@StrSeprate, @SourceSql)
- WHILE @i >= 1
- BEGIN
- INSERT @temp(C1)
- VALUES ( LEFT(@SourceSql, @i - 1) )
- SET @SourceSql = SUBSTRING(@SourceSql, @i + 1,
- LEN(@SourceSql) - @i)
- SET @i = CHARINDEX(@StrSeprate, @SourceSql)
- END
- IF @SourceSql <> '\'
- INSERT @temp(C1)
- VALUES ( @SourceSql )
- RETURN
- END
- GO
- --=====================================================
- --用法:
- SELECT * FROM dbo.ufn_SplitStringTODT('13,2,1,3',',')
五. 上递归大法 获取 substring 函数的 起始位置,以及截取字串长度 prePos,subPos 当然还有拆分出的字串的 order (orderid)
- DECLARE @temptab TABLE(strCol VARCHAR(8000))
- INSERT INTO @temptab ( strCol )
- VALUES ( 'a,b,c,d,e,f' -- strCol - varchar(8000)
- )
- --,
- --( 'g,h,i,j,k,l' -- strCol - varchar(8000)
- -- )
- DECLARE @split_Del VARCHAR(10)=','
- --SELECT * FROM @temptab
- ;WITH tab(strCol,orderid,prePos,subPos) AS (
- SELECT strCol,1 AS orderid,1 AS prePos,CHARINDEX(@split_Del,strCol) AS subPos FROM @temptab
- UNION ALL
- SELECT strCol,orderid+1,subPos+1,CHARINDEX(@split_Del,strCol,subPos+1) AS subPos FROM tab
- WHERE CHARINDEX(@split_Del,strCol,subPos+1)>0
- )
- SELECT tab.strCol,tab.orderid,SUBSTRING(tab.strCol,tab.prePos,tab.subPos-tab.prePos) AS newStr FROM tab OPTION(MAXRECURSION 10)
- --issue: the last alpha would be lost
- /*
- a,b,c,d,e,f 1 a
- a,b,c,d,e,f 2 b
- a,b,c,d,e,f 3 c
- a,b,c,d,e,f 4 d
- a,b,c,d,e,f 5 e
- */
- Version 2 to fix the issue above
- DECLARE @temptab TABLE(strCol VARCHAR(8000))
- INSERT INTO @temptab ( strCol )
- VALUES ( ',a,b,c,d,e,f' -- strCol - varchar(8000)
- )
- --,
- --( 'g,h,i,j,k,l' -- strCol - varchar(8000)
- -- )
- DECLARE @split_Del VARCHAR(10)=','
- --SELECT * FROM @temptab
- ;WITH tab(strCol,orderid,prePos,subPos) AS (
- SELECT strCol,1 AS orderid,1 AS prePos,CHARINDEX(@split_Del,strCol) AS subPos FROM @temptab
- UNION ALL
- SELECT strCol,orderid+1,subPos+1,CHARINDEX(@split_Del,strCol+@split_Del,subPos+1) AS subPos FROM tab
- WHERE CHARINDEX(@split_Del,strCol+@split_Del,subPos+1)>0
- )
- SELECT tab.strCol,tab.orderid,SUBSTRING(tab.strCol,tab.prePos,tab.subPos-tab.prePos) AS newStr FROM tab OPTION(MAXRECURSION 10)
- --issue: the last alpha would be lost
- /*
- ,a,b,c,d,e,f 1
- ,a,b,c,d,e,f 2 a
- ,a,b,c,d,e,f 3 b
- ,a,b,c,d,e,f 4 c
- ,a,b,c,d,e,f 5 d
- ,a,b,c,d,e,f 6 e
- ,a,b,c,d,e,f 7 f
- */
- -------------------------------- Recursively split Character String -------------------------------------------
- CREATE PROCEDURE uf_SplitStrNew(@strCol VARCHAR(8000),@split_Del VARCHAR(10))
- WITH ENCRYPTION,EXECUTE AS OWNER
- AS
- BEGIN
- DECLARE @temptab TABLE(strCol varchar(8000))
- INSERT INTO @temptab ( strCol ) VALUES(@strCol)
- SELECT strCol FROM @temptab
- ;WITH tab(strCol,orderid,prePos,subPos) AS (
- SELECT strCol,1 AS orderid,1 AS prePos,CHARINDEX(@split_Del,strCol) AS subPos FROM @temptab
- UNION ALL
- SELECT strCol,orderid+1,subPos+1,CHARINDEX(@split_Del,strCol+@split_Del,subPos+1) AS subPos FROM tab
- WHERE CHARINDEX(@split_Del,strCol+@split_Del,subPos+1)>0
- )
- SELECT tab.strCol,tab.orderid,SUBSTRING(tab.strCol,tab.prePos,tab.subPos-tab.prePos) AS newStr FROM tab OPTION(MAXRECURSION 10)
- END
- GO
- EXEC dbo.uf_SplitStrNew 'a,b,c,d,e,f',','
- --issue has been fixed
- /*
- ,a,b,c,d,e,f 1
- ,a,b,c,d,e,f 2 a
- ,a,b,c,d,e,f 3 b
- ,a,b,c,d,e,f 4 c
- ,a,b,c,d,e,f 5 d
- ,a,b,c,d,e,f 6 e
- ,a,b,c,d,e,f 7 f
- */
小结:以上方法均亲测,但是不排除有问题,拆分无外乎 1 使用动态拼凑 SQL 将 逗号替换为 INSERT INTO 或者 Union all select 等等能够把分隔符之间字符串取出的表达式,2 转换为 xml 数据,利用XQuery结合CROSS/OUTER APPLY 的优势进行函数取出 3.利用SQL语句的 SELECT 获取定位逗号,然后根据逗号分隔去取出
4.使用 charindex 跟 substring 做成表值函数。 显然 第四种方法最易想到。
5.利用 CTE 递归循环获取 split delimeter 的 position,此法慎用,递归深度太大可能导致内存泄漏
补充:
用户自定义函数可以是确定性的也可以是非确定性的。确定性并不是根据任何参数类型定义的,而是根据函数的功能定义的。如果给定了一组特定的有效输入,每次函数就都能返回相同的结果,那么就说该函数是确定性的。SUM()就是一个确定性的内置函数。3、5、10的总合永远都是18,而GETDATE()的值就是非确定性的,因为每次调用它的时候GETDATE()都会改变。
为了达到确定性的要求,函数必须满足以下4个条件:
[1] 函数必须是模式绑定的。这意味着函数所依赖的任何对象会有一个依赖记录,并且在没有删除这个依赖的函数之前都不允许改变这些对象。
[2] 函数引用的所有其他函数,无论是用户定义的,还是系统定义的,都必须是确定性的。
[3] 不能引用在函数外部定义的表(可以使用表变量和临时表,只要它们是在函数作用域内定义就行)。
[4] 不能使用扩展存储过程。
确定性的重要性在于它显示了是否要在视图或计算列上建立索引。如果可以可靠地确定视图或计算列的结果,那么才允许在视图或计算列上建立索引。这意味着,如果视图或计算列引用非确定性函数,则在该视图或列上将不允许建立任何索引。
如果判定函数是否是确定性:除了上面描述的规则外,这些信息存储在对象的IsDeterministic属性中,可以利用OBJECTPROPERTY属性检查。
- SELECT OBJECTPROPERTY(OBJECT_ID('DateOnly'),'IsDeterministic'); --只是刚才的那个自定义函数
输出结果如下:
居然是非确定性的。原因在于之前在定义该函数的时候,并没有加上这个"WITH SCHEMABINDING"。
- ALTER FUNCTION dbo.DateOnly(@Date date)
- RETURNS date
- WITH SCHEMABINDING --当加上这一句之后
- AS
- BEGIN
- RETURN @Date
- END
在执行查询,该函数就是确定性的了。
2015-02-13
T-SQL切割字符串方法小结的更多相关文章
- T-SQL切割字符串方法小结 2
有表tb, 如下: id value ----------- ----------- 1 aa,bb 2 aaa,bbb,ccc 欲按id,分拆value列, 分拆后结果如下: id value -- ...
- C# 过滤sql特殊字符串方法
1. /// <summary> /// 过滤不安全的字符串 /// </summary> /// <param name="Str" ...
- SQL server 分页方法小结
这里面介绍一下常用的分页方法: 1.使用top来分页 select top @pageSize * from table where id not in (select top @pageSize*( ...
- Sql分隔字符串方法--split
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO --DEClARE @str varchar(500)='a,b2,v5,d3,ew,2,3,dd' ...
- LINQ To SQL && Lambda 使用方法小结 (转)
1. 查询Student表中的所有记录的Sname.Ssex和Class列.select sname,ssex,class from studentLinq: from s in Students ...
- Go语言编程中字符串切割方法小结
1.func Fields(s string) []string,这个函数的作用是按照1:n个空格来分割字符串最后返回的是[]string的切片 复制代码代码如下: import ( "fm ...
- Sql server2005 优化查询速度50个方法小结
Sql server2005 优化查询速度50个方法小结 Sql server2005优化查询速度51法查询速度慢的原因很多,常见如下几种,大家可以参考下. I/O吞吐量小,形成了瓶颈效应. ...
- asp.net检查验证字符串是否为纯数字方法小结
原文 asp.net检查验证字符串是否为纯数字方法小结 在asp.net中验证字符串是不是为数字我们没有像php中那么多丰富的函数来直接使用,这里我整理了一些比较实例的验证字符串是否为纯数字方法代码 ...
- Go语言中字符串的查找方法小结
这篇文章主要介绍了Go语言中字符串的查找方法小结,示例的main函数都是导入strings包然后使用其中的方法,需要的朋友可以参考下 1.func Contains(s, substr strin ...
随机推荐
- CentOS 6.5 安装配置
关于CentOS的安装,网上有很多详细的教程.其实重点就在于硬盘的分区和软件的定制这两块.下面我在VirtualBox虚拟机上安装 CentOS-6.5-i386-minimal. 1.在起始菜单处选 ...
- Android-Native-Server 启动和注册详细分析
Android-Native-Server 启动和注册详细分析 以mediaService为实例来讲解: mediaService的启动入口 是一个 传统的 main()函数 源码位置E:\ ...
- Google maps library的使用
公司的项目中用到了google地图API, 使用Google API开发就会用到Marker, 用来在google 地图上标注位置 但是google marker使用过程中也有个问题,就是如果在goo ...
- Linux就是这个范儿之第一次亲密接触(3)
原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 .作者信息和本声明.否则将追究法律责 1.4 返璞归真的命令行 有一种说法,现代计算机不是靠电力驱动,而是靠“鼠标”.多少应用程序的界面需 ...
- S2SH项目框架搭建(完全注解)
1.引入相关jar包 2.配置Spring配置文件,命名为applicationContext.xml(配置好后放到src目录下) <beans xmlns="http://www.s ...
- ubuntu 忘记用户名及密码解决办法
1.重启系统 2.长按Shift键,直到出现菜单.选择recovery mode,即恢复模式 3.选择root 4.# 后面敲入 cat /etc/shadow 查看用户名 5.# passwd & ...
- error: variable '__this_module' has initializer but incomplete type错误解决
版权所有,转载必须说明转自 http://my.csdn.net/weiqing1981127 原创作者:南京邮电大学 通信与信息系统专业 研二 魏清 问题描述:使用SAM9X25 内核版本是2. ...
- 对PostgreSQL xmax的理解
xmax The identity (transaction ID) of the deleting transaction, or zero for an undeleted row version ...
- 从零开始学android开发-项目debug
在红框处能看到变量值
- 从零开始学android开发-详细谈谈intent的startActivityForResult()方法
1.两种实现activity跳转的方法 实现activity的跳转主要有两种方法,startActivity()和startActivityForResult();例如activity A跳转到act ...