2013-07-16 36 views
0

我有以下变量。查找子字符串/ charindex T-sql

DECLARE @TestConnectionString varchar(255) = 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 

我想从这个连接字符串中分离出每个属性的值。

我相信我必须使用SUBSTRINGCHARINDEX,但不知道如何。我不想硬编码每个属性的长度为user_id可能是"Comeonedude"

有人可以告诉我如何可以提取这些属性的几个例子吗?

与此同时,我会试着看看我是否能找出任何东西。

谢谢

+1

http://stackoverflow.com/questions/2647/split-string-in-sql –

回答

1

我喜欢使用XML转换在TSQL中拆分字符串。这种方法是首选,因为它不需要您在整个地方创建字符串拆分功能,并且以我的体验执行并可以很好地扩展。这里是一个SQLFiddle的例子。

DECLARE @TestConnectionString varchar(255) = 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 

SELECT 
    t.c.value('(property)[1]','VARCHAR(200)') AS [property] 
    ,t.c.value('(value)[1]','VARCHAR(200)') AS [value] 
FROM (
    SELECT CAST('<root><pair><property>' + REPLACE(REPLACE(LEFT(@TestConnectionString,LEN(@TestConnectionString)-1),';','</value></pair><pair><property>'),'=','</property><value>') + '</value></pair></root>' AS XML) AS properties_xml 
) AS i 
CROSS APPLY i.properties_xml.nodes('/root/pair') AS t(c) 

说明:

的@TestConnectionString由该select语句格式为XML文档:

SELECT CAST('<root><pair><property>' + REPLACE(REPLACE(LEFT(@TestConnectionString,LEN(@TestConnectionString)-1),';','</value></pair><pair><property>'),'=','</property><value>') + '</value></pair></root>' AS XML) AS properties_xml 

的XML字符串开头<root><pair><property>,那么REPLACE函数替换每个分界的分号为</value></pair><pair><property>,并用</property><value>替换每个分隔符号。 @TestConnectionString以分号结尾,因此分号必须先被LEFT函数删除,否则我们最后会在XML字符串的末尾添加</value></pair><pair><property>。 XML字符串通过附加</value></pair></root>完成,我们结束了这样的:

<root> 
    <pair> 
    <property>Data Source</property> 
    <value>123.45.67.890</value> 
    </pair> 
    <pair> 
    <property>User ID</property> 
    <value>TestUser</value> 
    </pair> 
    <pair> 
    <property>Password</property> 
    <value>TestPassword</value> 
    </pair> 
    <pair> 
    <property>Initial Catalog</property> 
    <value>TestCatalogName</value> 
    </pair> 
    <pair> 
    <property>Provider</property> 
    <value>SQLNCLI11.1</value> 
    </pair> 
    <pair> 
    <property>Persist Security Info</property> 
    <value>True</value> 
    </pair> 
    <pair> 
    <property>Auto Translate</property> 
    <value>False</value> 
    </pair> 
</root> 

XML字符串被转换为与CAST功能XML数据类型。 运算符可用于将XML文档的节点转换为具有行和列(别名为c)的类似表格的对象(别名为t)。

CROSS APPLY i.properties_xml.nodes('/root/pair') AS t(c) 

现在我们有一个表格,其中的行代表XML文档中的每个节点对。可以使用value函数为我们要选择的每个列指定一个数据类型。

SELECT 
    t.c.value('(property)[1]','VARCHAR(200)') AS [property] 
    ,t.c.value('(value)[1]','VARCHAR(200)') AS [value] 
+0

这是一个非常好的方法。 –

+0

感谢这...小代码,但一个非常独特的方法。 :) – 007

+0

试图弄清楚发生了什么...非常困惑,但很好奇..请解释你的方法多一点?这太神奇了......不需要其他对象(即udf),这对于在多个SQL环境中工作时会非常方便...希望我可以将多个注释标记为答案。 – 007

2

首先将字符串拆分为';' ..你可以在网上找到许多Split功能。用一个把它分解成一个表。

下面的代码是从:How to split string using delimiter char using T-SQL?

CREATE FUNCTION [dbo].[Split] 
( 
@String varchar(max) 
,@Delimiter char =';' -- default value 
) 
RETURNS @Results table 
(
Ordinal int 
,StringValue varchar(max) 
) 
as 
begin 

    set @String = isnull(@String,'') 
    set @Delimiter = isnull(@Delimiter,'') 

    declare 
    @TempString varchar(max) = @String 
    ,@Ordinal int = 0 
    ,@CharIndex int = 0 

    set @CharIndex = charindex(@Delimiter, @TempString) 
    while @CharIndex != 0 begin  
     set @Ordinal += 1  
     insert @Results values 
     (
     @Ordinal 
     ,substring(@TempString, 0, @CharIndex) 
     )  
     set @TempString = substring(@TempString, @CharIndex + 1, len(@TempString) - @CharIndex)  
     set @CharIndex = charindex(@Delimiter, @TempString) 
    end 

    if @TempString != '' begin 
     set @Ordinal += 1 
     insert @Results values 
     (
     @Ordinal 
     ,@TempString 
     ) 
    end 

    return 
end 

假设的顺序始终是相同的,在“=”分割每个resutls的。 采取每串(剩余串的后“=”的长度)的右侧部分..

等瞧,您有其值的每个属性。

- 编辑:从上述拆分函数:

DECLARE @TestConnectionString varchar(255) = 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 

create table #result 
(
property varchar(255), 
Value varchar(255) 
) 

create table #tmp 
(
Property varchar(255) 
) 

create table #tmp2 
(
Value varchar(255) 
) 

insert into #tmp 
select * from split(@TestConnectionString, ';') 


--select * from #tmp 

/* Sclaufe */ 
declare @id varchar(255) 

DECLARE a_coursor CURSOR FOR 
select property from #tmp 
OPEN a_coursor; 
FETCH NEXT FROM a_coursor into  @id; 
WHILE @@FETCH_STATUS = 0 
BEGIN 

    -- select @id 
    insert into #tmp2 
    select * from Split(@id, '=') 

FETCH NEXT FROM a_coursor 
INTO  @id 
END; 
CLOSE a_coursor; 
DEALLOCATE a_coursor; 


select * from #tmp2 

/* Sclaufe */ 
declare @id2 varchar(255) 
declare @oldid varchar(255) 
declare @count int 
set @count = 1 

DECLARE a_coursor CURSOR FOR 
select value from #tmp2 
OPEN a_coursor; 
FETCH NEXT FROM a_coursor into  @id2; 
WHILE @@FETCH_STATUS = 0 
BEGIN 

    print @id2 

    if @count % 2 <> 0 
    begin 
     insert into #result 
     select @id2, '' 

     set @oldid = @id2 
    end 
    else 
    begin 
     update #result 
     set Value = @id2 
     where property = @oldid 
    end 

    set @count = @count + 1 

FETCH NEXT FROM a_coursor 
INTO  @id2 
END; 
CLOSE a_coursor; 
DEALLOCATE a_coursor; 

select * from #result 


drop table #tmp 
drop table #tmp2 
drop table #result 

结果将在#ressult表:

╔═══════════════════════╦═════════════════╗ 
║  property  ║  Value  ║ 
╠═══════════════════════╬═════════════════╣ 
║ Data Source   ║ 123.45.67.890 ║ 
║ User ID    ║ TestUser  ║ 
║ Password    ║ TestPassword ║ 
║ Initial Catalog  ║ TestCatalogName ║ 
║ Provider    ║ SQLNCLI11.1  ║ 
║ Persist Security Info ║ True   ║ 
║ Auto Translate  ║ False   ║ 
╚═══════════════════════╩═════════════════╝ 

编辑:或者,您可以创建一个存储过程:

if exists (select 1 from sysobjects where name = 'getvalue2' and type = 'P') 
begin 
    drop procedure getvalue2 
    print 'Procedure: getvalue2 deleted ...' 
end 
go 

/* 
exec getvalue2 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 
*/ 
create procedure [dbo].[getvalue2] 
( @TestConnectionString varchar(255)) 
as 
begin 

    --= 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 

    create table #result 
    (
    property varchar(255), 
    Value varchar(255) 
    ) 

    create table #tmp 
    (
    firstrun varchar(255) 
    ) 

    create table #tmp2 
    (
    secondrun varchar(255) 
    ) 

    insert into #tmp 
    select * from split(@TestConnectionString, ';') 


    --select * from #tmp 

    declare @id varchar(255) 

    DECLARE a_coursor CURSOR FOR 
    select firstrun from #tmp 
    OPEN a_coursor; 
    FETCH NEXT FROM a_coursor into  @id; 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 

     insert into #tmp2 
     select * from Split(@id, '=') 

    FETCH NEXT FROM a_coursor 
    INTO  @id 
    END; 
    CLOSE a_coursor; 
    DEALLOCATE a_coursor; 

    declare @id2 varchar(255) 
    declare @oldid varchar(255) 
    declare @count int 
    set @count = 1 

    DECLARE a_coursor CURSOR FOR 
    select secondrun from #tmp2 
    OPEN a_coursor; 
    FETCH NEXT FROM a_coursor into  @id2; 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 

     print @id2 

     if @count % 2 <> 0 
     begin 
      insert into #result 
      select @id2, '' 

      set @oldid = @id2 
     end 
     else 
     begin 
      update #result 
      set Value = @id2 
      where property = @oldid 
     end 

     set @count = @count + 1 

    FETCH NEXT FROM a_coursor 
    INTO  @id2 
    END; 
    CLOSE a_coursor; 
    DEALLOCATE a_coursor; 

    select * from #result 
end 

有乐趣, 你是惠康=)

+0

不幸的是,你不能假设的顺序是一样的。 – Anon

+0

仍然会有一个属性为“property = value,property = value ...”的表格,因此您可以轻松地将'''之前的部分作为下一个拆分变量的名称。 –

+0

Now you all所需要做的就是调整一下,所以空值和其他特殊的东西都会被接受。 –

1

这是一般的方法,如果你真的想使用SUBSTRING没有硬编码的号码,你可以使用:

DECLARE @TestConnectionString varchar(255) = 'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 
SELECT SUBSTRING(@TestConnectionString,CHARINDEX('ID=',@TestConnectionString)+3,CHARINDEX(';Password',@TestConnectionString)-CHARINDEX('ID=',@TestConnectionString)-3) 'User ID' 
     ,SUBSTRING(@TestConnectionString,CHARINDEX(';Password=',@TestConnectionString)+10,CHARINDEX(';Initial',@TestConnectionString)-CHARINDEX(';Password=',@TestConnectionString)-10) 'Password' 

如果在你的字符串不一致像这样的做法可能会失败,它可能是值得分裂基于;分隔符的字段字符串。

+0

是的,谢谢你的帮助,并指出一些改进 – 007

1

如果你关心递归,SQL服务器可以处理它。我改写了rCTE查询(一次)我在另一个项目中使用,以提取值:

DECLARE @Test varchar(255) = 
'Data Source=123.45.67.890;User ID=TestUser;Password=TestPassword;Initial Catalog=TestCatalogName;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;' 

;WITH T AS (
    SELECT 
    StartIdx = CAST(0 as int), 
    EndIdx = CAST(0 as int), 
    Result = CAST('' as nvarchar(max)) 
    UNION ALL 
    SELECT 
    StartIdx = CAST(newstartidx AS int), 
    EndIdx = CAST(EndIdx + newendidx as int), 
    Result = CAST(newtoken as nvarchar(max)) 
    FROM 
    T 
    CROSS APPLY(
     SELECT newstartidx = EndIdx + 1 
    ) calc1 
    CROSS APPLY(
     SELECT newtxt = substring(@Test, newstartidx, len(@Test)) 
    ) calc2 
    CROSS APPLY(
     SELECT patidx = charindex(';', newtxt) 
    ) calc3 
    CROSS APPLY(
     SELECT newendidx = CASE 
     WHEN patidx = 0 THEN len(newtxt) 
     ELSE patidx END 
    ) calc4 
    CROSS APPLY(
     SELECT newtoken = substring(@Test, newstartidx, newendidx) 
    ) calc5 
    WHERE newendidx > 0 
) 
SELECT 
    --Result, 
    Name = left(Result, idx - 1), 
    Value = substring(Result, idx + 1, len(Result) - idx - 1) 
FROM 
    T 
    CROSS APPLY (
    SELECT idx = charindex('=', Result) 
) calc6 
WHERE StartIdx != 0 
+0

这也适用大!!!谢谢 – 007

1

使用一个通用的字符串分割函数两次(见下文)。调用它来分割名称 - 值对,并再次将名称与值分开。

看到它在行动:http://sqlfiddle.com/#!3/3cce5/1/0

SELECT 
    t3.[1] AS name, 
    t3.[2] AS value 
FROM dbo.strsplit(@TestConnectionString,';') t1 
CROSS APPLY dbo.strsplit(t1.col,'=') t2 
PIVOT(MAX(t2.col) FOR t2.n IN ([1],[2])) t3 

我的字符串分割功能。

CREATE FUNCTION [dbo].[strsplit](
    @str varchar(max), --String to be split 
    @dlm char(1)  --Delimiting character 
) 
RETURNS TABLE 
RETURN (
WITH [cols] AS (
    SELECT 
    1 AS [n], 
    CAST(1 AS bigint) AS [idx], 
    CHARINDEX(@dlm,@str,1) AS [ndx] 
    UNION ALL 
    SELECT 
    [n] + 1, 
    CHARINDEX(@dlm,@str,[idx]) + 1, 
    CHARINDEX(@dlm,@str,[ndx] + 1) 
    FROM [cols] 
    WHERE CHARINDEX(@dlm,@str,[idx]) > 0 
) 
SELECT 
    [n], 
    CASE [ndx] 
    WHEN 0 THEN SUBSTRING(@str,[idx],LEN(@str)-[idx]+1) 
    ELSE SUBSTRING(@str,[idx],[ndx]-[idx]) 
    END AS [col] 
FROM [cols]) 
+0

感谢您分享这个方法,我会放弃它。 :) – 007