2

SQL Server. Compatibility level 120.

I have a table where one VARCHAR column contains semicolon-separated integral values. I would like to SELECT against the values in this column as if they existed in a column of ints somewhere.

Because of the compatibility level, I can't use STRING_SPLIT. I also cannot create any globally available functions.

I can however create a temporary table if needed. What's my course of action? Some sort of nesting of WHILE loops (One for the rows of the table, another for the semicolons in the string)?

update: This is perhaps a better explanation of what I want to achieve:

Let's say I have the following tables.

CREATE TABLE Service
(
      ServiceId int
      , CustomerIds varchar(255)
);
CREATE TABLE Customer
(
    CustomerId int
    , CustomerName varchar(30)
);

With some data

INSERT INTO Customer (CustomerId, CustomerName)
VALUES  (1, 'John')
    ,(2, 'Niels')
    ,(3, 'Frank')
    ,(4, 'Barbie')
    ,(5, 'Holly')
    ,(6, 'Ebeneezer');
INSERT INTO Service (ServiceId, CustomerIds)
VALUES  (1, '4')
    ,(2, '2;3');

And let's say I want to select Customer.CustomerName for any Customer who's ID shows up in the CustomerIds in any line in that table.

That is, I want to retrieve Niels, Frank and Barbie.

  • 2
    Welcome to DBA.SE and thanks for participating. Could you post more details on your question? Example data? Things like that? Please also consider having a look at minimal reproducible example. Thanks a lot. – John aka hot2use Dec 6 '19 at 15:55
  • Thanks for the pointes. I'll update my question accordingly as soon as I find the time. Hopefully that is quite soon. – Christofer Ohlsson Dec 6 '19 at 16:40
5

A possible solution might be Splitting Delimited Strings Using XML in SQL Server. From that post:

Problem

This article will help developers looking for a way to split delimited strings in a single query using XML. We generally use a user defined function to do this, which you have probably found in many places that splits the string based on the delimiter passed. But, when it comes to separating the string in a single query without any help of a user defined function there are not many options. I have found a much simpler and shorter way of splitting any string based on a delimiter. I will be using the power of XML to do the splitting of the string instead of a user defined function.

Solution

Let's say for example there is a string 'A,B,C,D,E' and I want to split it based on the delimiter ','.

The first step would be to convert that string into XML and replace the delimiter with some start and end XML tags.

DECLARE @xml as xml,@str as varchar(100),@delimiter as varchar(10)
SET @str='A,B,C,D,E'
SET @delimiter =','
SET @xml = cast(('<X>'+replace(@str,@delimiter ,'</X><X>')+'</X>') as xml)
SELECT @xml

Once the string is converted into XML you can easily query that using XQuery

DECLARE @xml as xml,@str as varchar(100),@delimiter as varchar(10)
SET @str='A,B,C,D,E'
SET @delimiter =','
SET @xml = cast(('<X>'+replace(@str,@delimiter ,'</X><X>')+'</X>') as xml)
SELECT N.value('.', 'varchar(10)') as value FROM @xml.nodes('X') as T(N)

| value |
|-------|
| A     |
| B     |
| C     |
| D     |
| E     |

UPDATE 2019-12-09

Based on your updated question, here is a sample solution

set nocount on;
drop table if exists Service;
CREATE TABLE Service
(
      ServiceId int
      , CustomerIds varchar(255)
);
drop table if exists Customer;
CREATE TABLE Customer
(
    CustomerId int
    , CustomerName varchar(30)
);
INSERT INTO Customer (CustomerId, CustomerName)
VALUES  (1, 'John')
    ,(2, 'Niels')
    ,(3, 'Frank')
    ,(4, 'Barbie')
    ,(5, 'Holly')
    ,(6, 'Ebeneezer');
INSERT INTO Service (ServiceId, CustomerIds)
VALUES  (1, '4')
    ,(2, '2;3');
-----------------------
;WITH ServicesAsXml
AS (
    SELECT ServiceId
        ,cast(('<X>' + replace(CustomerIds, ';', '</X><X>') + '</X>') AS XML) AS xmlcol
    FROM Service
    )
    ,ServicesSplit
AS (
    SELECT ServiceId
        ,T.N.value('.', 'varchar(10)') AS CustomerId
    FROM ServicesAsXml
    CROSS APPLY xmlcol.nodes('X') T(N)
    )
SELECT CustomerName
FROM Customer c
JOIN ServicesSplit ss ON ss.CustomerId = c.CustomerId

| CustomerName |
|--------------|
| Barbie       |
| Niels        |
| Frank        |
  • Thank you! I will try this as soon as possible. – Christofer Ohlsson Dec 6 '19 at 16:40
  • I understand how to make this work for one value (i.e splitting one cell into the respective integers. But I have yet to understand how to aggregate the results from all rows into the same result. – Christofer Ohlsson Dec 9 '19 at 9:00
0

You can use a solution based on Itzik Ben-Gan's tally tables.

  • Basically you create a numbers table (either physical or virtual)
  • You will find the position of each separator in the string.
  • You will find the position of next separator starting from the current position
  • You'll use SUBSTRING to parse the text using start and length (end - start) positions.

    use tempdb
    drop table if exists dbo.DelimitedData
    create table dbo.DelimitedData
    (
        PK int identity(1,1) primary key
        , TextData varchar(4000) not null
    )
    
    insert into dbo.DelimitedData (TextData)
    VALUES ('111;222;333;444'), ('AA;BB;CC;DD;EE'), ('X;;Z');
    
    ;WITH lv0 AS (SELECT 0 g UNION ALL SELECT 0)
        ,lv1 AS (SELECT 0 g FROM lv0 a CROSS JOIN lv0 b) -- 4
        ,lv2 AS (SELECT 0 g FROM lv1 a CROSS JOIN lv1 b) -- 16
        ,lv3 AS (SELECT 0 g FROM lv2 a CROSS JOIN lv2 b) -- 256
        ,lv4 AS (SELECT 0 g FROM lv3 a CROSS JOIN lv3 b) -- 65,536
        ,lv5 AS (SELECT 0 g FROM lv4 a CROSS JOIN lv4 b) -- 4,294,967,296
        ,Tally (n) AS (SELECT TOP(500) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM lv5)
    
    SELECT
        *
        , SUBSTRING(x.TextData, x.StartText, x.EndText - x.StartText)
    from 
    (
    SELECT 
        dd.*
        , n                                 as StartText
        , CHARINDEX(';',dd.TextData + ';', n) as EndText
    FROM Tally as t
    cross join dbo.DelimitedData as dd
    where SUBSTRING(';' + dd.TextData,n,1) = ';'
    ) as x
    order by x.PK, x.StartText
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.