0

I have the following variable.

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;'

I want to separate out each property's values from this connection string.

I am sure that I have to use SUBSTRING and CHARINDEX but not sure how. I don't want to hard-code the length for each property as user_id could be "Comeonedude"

Can someone show me how I can extract few of these properties as an example?

In meanwhile, I will try to see if I can figure out anything.

Thank you

Serg
  • 2,346
  • 3
  • 29
  • 38
007
  • 2,136
  • 4
  • 26
  • 46

5 Answers5

2

First split the string at ';' .. You can find many Split functions online. Use one that splits it into a table.

Following Code is from: 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

assuming the order is always the same, split each of the resutls at the '='. take the right part of every string (the length of the remaining string after '=')..

et voilà, you have every property with its value.

-- EDIT: With the Split Function from above:

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

The result will be in the #ressult table:

╔═══════════════════════╦═════════════════╗
║       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           ║
╚═══════════════════════╩═════════════════╝

EDIT: Or you can create a stored procedure:

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

have fun, You're wellcome = )

Community
  • 1
  • 1
Hagbart Celine
  • 470
  • 1
  • 9
  • 26
  • Unfortunately, you can't assume the order is the same. – Anon Jul 16 '13 at 22:35
  • Still you will have one table with the properties as "property=value, property=value ..." so you can easily take the part before the '"' as the name of the variable for the next split. – Hagbart Celine Jul 17 '13 at 09:40
  • Now all you have to do is tweak it a little, so empty values and other special stuff will be accepted. – Hagbart Celine Jul 17 '13 at 10:50
  • added a stored procedure.. – Hagbart Celine Jul 17 '13 at 11:00
  • 1
    You're using a cursor and three(!) temp tables. That's a little excessive. I'd recommend reading about Set-Based Solutions and [RBAR](https://www.simple-talk.com/sql/t-sql-programming/rbar--row-by-agonizing-row/) – Anon Jul 17 '13 at 17:04
  • @Anon: I know it is quite the overkill.. But it does the trick. Do you have any good links explaining Set-Bases Solutions? – Hagbart Celine Jul 18 '13 at 07:42
2

I like using XML casting to split strings in TSQL. This method is preferred because it doesn't require you to create string split functions all over the place and in my experience it performs and scales well. Here is a SQLFiddle example.

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)

Explanation:

The @TestConnectionString is formatted as an XML document by this select statement:

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

The XML string begins with <root><pair><property>, then the REPLACE function replaces each of the delimiting semicolons with </value></pair><pair><property> and replaces each of the separating equal signs with </property><value>. The @TestConnectionString ends with a semicolon, so that semicolon must first be removed by the LEFT function, or else we would end up with an extra </value></pair><pair><property> at the end of our XML string. The XML string is completed by appending </value></pair></root>, and we end up with this:

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

The XML string is converted to the XML data type with the CAST function. The CROSS APPLY operator can be used to turn the nodes of a XML document into a table-like object (aliased as t) with rows and columns (aliased as c).

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

Now we have a table with rows representing each pair node in the XML document. This table can be selected from, using the value function to assign a data type to each column that we want to select out.

SELECT
     t.c.value('(property)[1]','VARCHAR(200)') AS [property]
    ,t.c.value('(value)[1]','VARCHAR(200)') AS [value]
supergrady
  • 1,322
  • 10
  • 13
  • this is a very nice approach. – Hagbart Celine Jul 17 '13 at 13:27
  • Thanks for this...small code but a very unique approach. :) – 007 Jul 17 '13 at 21:55
  • Trying to figure out what is happening...very confused yet very curious..mind explaining your method a little bit more? This is amazing...no need for other objects (i.e. udf) which would be very handy while working on multiple sql environments...wish I could mark multiple comments as answers. – 007 Jul 17 '13 at 22:03
  • I added a SQLFiddle and some better explanation of what is going on in the XML `CAST`, `SELECT` and `CROSS APPLY`. Hope it helps – supergrady Jul 18 '13 at 01:10
1

Here is the general approach you could use if you really wanted to use SUBSTRING without hard-coded numbers:

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'

An approach like this can fail if there are inconsistencies in your strings, it may be worth splitting the string to fields based on the ; delimiter.

Hart CO
  • 34,064
  • 6
  • 48
  • 63
1

If you care about recursion, SQL server can handle that. I rewrote rCTE query (once more) I use in another project to extract the values:

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
OzrenTkalcecKrznaric
  • 5,535
  • 4
  • 34
  • 57
1

Use a generic string-splitting function twice (see below). Call it once to split the name-value pairs and again to separate the names from the values.

See it in action: 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

My string-split function.

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])
Anon
  • 10,660
  • 1
  • 29
  • 31