2

I'm looking for a way to do this ...

SELECT FirstName, LastName, Split(AddressBlock, '  ', 1), Split(AddressBlock, ' ', 2), PostCode 
FROM Contacts

The arguments I want to pass are ...

  1. The address
  2. The separator (current situation requires 2 spaces but this might be a comma or a space followed by a comma) or something else (it varies).
  3. The address part I want to return (i don't always need all parts of the split result).

I seem to be able to find a few examples of splitting functions about the internet but they return a table containing the entire set of split parts.

My SQL skills aren't that great so I need the answer to be ultra simple. I'm always working with nvarchar data and the function needs to be reusable.

Scott W
  • 9,742
  • 2
  • 38
  • 53
War
  • 8,539
  • 4
  • 46
  • 98
  • possibly a duplicate of http://stackoverflow.com/questions/314824/t-sql-opposite-to-string-concatenation-how-to-split-string-into-multiple-recor – Unreason May 20 '10 at 09:18
  • I disagree, on the basis that I only want to extract certain parts of the plist result and do not wish to retrieve a table variable. I want a single string each time the function is called. However there is a solution on there that might be a good start. – War May 20 '10 at 09:26

3 Answers3

4

If you want a user-defined function to do this, this should work. Not that pretty, but...

CREATE FUNCTION dbo.SplitStringPart (
    @input nvarchar(MAX),
    @separator nvarchar(10),
    @index int
) RETURNS nvarchar(MAX)
BEGIN

DECLARE @counter int,
        @position int,
        @oldposition int,
        @separatorlength int,
        @result nvarchar(MAX)

SET @separatorlength = DATALENGTH(@separator) / 2
IF @separatorlength = 0 RETURN NULL

SET @result = NULL

SET @counter = 1
SET @position = -2

WHILE (@counter <= @index)
BEGIN

    SET @oldposition = @position
    SET @position = CHARINDEX(@separator, @input, @position + 1)
    IF @position = 0 AND @counter < @index
    BEGIN
        SET @oldposition = 0
        BREAK
    END
    SET @counter = @counter + 1

END

IF @oldposition = 0 AND @position = 0
    RETURN NULL
ELSE IF @oldposition < 0
BEGIN
    IF @position = 0 AND @index = 1
        SET @result = @input
    ELSE
        SET @result = SUBSTRING(@input, 0, @position)
END
ELSE IF @position <= 0
    SET @result = SUBSTRING(@input, @oldposition + @separatorlength, LEN(@input) - @oldposition - @separatorlength)
ELSE
    SET @result = SUBSTRING(@input, @oldposition + @separatorlength, @position - @oldposition - @separatorlength)

RETURN @result

END
GO
David M
  • 71,481
  • 13
  • 158
  • 186
  • It does break when the string only has 1 part and i ask for part 2 though ... not ideal. – War May 20 '10 at 09:43
1

It's not pretty, but add this to you SQL statement and it should work:

CASE 
WHEN charindex(' ', substring(AddressBlock, (charindex(' ', AddressBlock) + 1), len(AddressBlock))) > 0 THEN substring(AddressBlock, (charindex(' ', AddressBlock) + 1), charindex(' ', substring(AddressBlock, (charindex(' ', AddressBlock) + 1), len(AddressBlock))) - 1)
ELSE substring(AddressBlock, (charindex(' ', AddressBlock) + 1), len(AddressBlock))    
END AS 'Address 1', 
CASE WHEN charindex(' ', substring(AddressBlock, (charindex(' ', AddressBlock) + 1), len(AddressBlock))) > 0 THEN substring(AddressBlock, charindex(' ', AddressBlock) + charindex(' ', substring(AddressBlock, (charindex(' ', AddressBlock) + 1), len(AddressBlock))) + 1, Len(AddressBlock))
ELSE ''
END AS 'Address 2'
Neil Knight
  • 47,437
  • 25
  • 129
  • 188
  • Doesn't this look for a single char on which to perform a split? I need to split on a string value (eg ", ") ... interesting though ... – War May 20 '10 at 09:26
  • This looks up the string value within the existing column and strips out everything up to the string value. So, change the charindex(' ' to charindex(',' if you want comma seperated. – Neil Knight May 20 '10 at 09:28
  • It wasn't exactly what I asked for but a good effort so you got my vote :) – War May 20 '10 at 09:41
0

Here is my version of the answer. This is MUCH faster and robust. No need to fuss around with substrings, charindex, etc.

CREATE FUNCTION [dbo].[SplitArray]
(
    @RowToSplit nvarchar(MAX),
    @Delimeter nvarchar(MAX)
)  
RETURNS @RtnValue table (ID bigint IDENTITY, Data nvarchar(MAX)) 
AS  
BEGIN 
    DECLARE @xml xml
    SET @xml = '<field>' + REPLACE(@RowToSplit, @Delimeter, '</field><field>') + '</field>'
    INSERT INTO @RtnValue(data)
    SELECT  tbl.c.value('.','nvarchar(max)')
    FROM @xml.nodes('/field') tbl(c)
    RETURN
END

You would simply use the resultant split values in a table, like this:

SELECT Data FROM dbo.SplitArray('this is great',' ')

This will return:

Data
============
this
is
great
Vinnie Amir
  • 557
  • 5
  • 10
  • So let me get this right ... To get a piece of a string you split it up then build a bigger string of xml, then parse that xml, then query it, and sql will do this faster ... What a weird tech SQL is if this true! – War Mar 18 '17 at 11:17
  • The xml is a binary object so it's not quite correct. Run this with query analyser and you will see. Character index is slow as it is string based, using xml it is binary based. – Vinnie Amir May 03 '17 at 10:39