-- splitting a string (SQL Server 2016+) SELECT * FROM STRING_SPLIT('100001::M:black:', ':')
The table-valued function object allowed also earlier to implement the same functionality, either by looping through the string or by using a common table expression. Here’s the implementation based on a loop (it was modified from a previous version to include an index):
-- split table-valued function with an index CREATE FUNCTION [dbo].[SplitListWithIndex]( @ListValues varchar(500) , @Delimiter char(1)) RETURNS @Temp TABLE( Ind int , Value varchar(50)) AS BEGIN DECLARE @Index int DECLARE @Length int DECLARE @Ind int SET @Index = CharIndex(@Delimiter, @ListValues) SET @Length = Len(@ListValues) - @Index SET @Ind = 1 WHILE @Index > 0 --if the fatch was successful BEGIN INSERT @Temp VALUES(@Ind, Substring(@ListValues, 0, @Index)) SET @ListValues = Substring(@ListValues, @Index+1, @Length) SET @Index = CharIndex(@Delimiter, @ListValues) SET @Length = @Length - @Index SET @Ind = @Ind + 1 END INSERT @Temp VALUES(@Ind, @ListValues) RETURN END GO
The function could be called in code same as the STRING_SPLIT:
-- splitting a string (SQL Server 2000+) SELECT * FROM dbo.SplitListWithIndex('100001::M:black:', ':')
The two functions are helpful when a column stores delimited values. It’s the case of Dynamics 365 which stores a SKU (Stock Keeping Unit) – the Product Numbers together with its Dimensions (ItemId, Configid, InventSizeId, InventColorId, StyleId) delimited by “:”, like in the above examples. Therefore, to parse the records one could write such code:
-- parsing delimited values (SQL Server 2000+) SELECT DAT.ProductNumber , Max(CASE WHEN LV.Ind = 1 THEN LV.Value END) ItemId , Max(CASE WHEN LV.Ind = 2 THEN LV.Value END) Configid , Max(CASE WHEN LV.Ind = 3 THEN LV.Value END) InventSizeId , Max(CASE WHEN LV.Ind = 4 THEN LV.Value END) InventColorId , Max(CASE WHEN LV.Ind = 5 THEN LV.Value END) StyleId FROM ( VALUES ('100001::S:black:') , ('100001::M:black:') , ('100001::L:black:') , ('100001::XL:black:') , ('100001::S:white:') , ('100001::M:white:') , ('100001::L:white:') , ('100001::XL:white:') ) DAT (ProductNumber) CROSS APPLY dbo.SplitListWithIndex(DAT.ProductNumber, ':') LV GROUP BY DAT.ProductNumber ORDER BY DAT.ProductNumber
Similar output can be obtained via the STRING_SPLIT with the help of row_number() ranking window function introduced with SQL Server 2005:
-- parsing delimited values (SQL Server 2016+) SELECT LV.ProductNumber , Max(CASE WHEN LV.Ind = 1 THEN LV.Value END) ItemId , Max(CASE WHEN LV.Ind = 2 THEN LV.Value END) Configid , Max(CASE WHEN LV.Ind = 3 THEN LV.Value END) InventSizeId , Max(CASE WHEN LV.Ind = 4 THEN LV.Value END) InventColorId , Max(CASE WHEN LV.Ind = 5 THEN LV.Value END) StyleId FROM ( SELECT DAT.ProductNumber , XT.VALUE , ROW_NUMBER() OVER (PARTITION BY DAT.ProductNumber ORDER BY DAT.ProductNumber) Ind FROM ( VALUES ('100001::S:black:') , ('100001::M:black:') , ('100001::L:black:') , ('100001::XL:black:') , ('100001::S:white:') , ('100001::M:white:') , ('100001::L:white:') , ('100001::XL:white:') ) DAT (ProductNumber) CROSS APPLY STRING_SPLIT(DAT.ProductNumber, ':') XT ) LV GROUP BY LV.ProductNumber ORDER BY LV.ProductNumber
As can be seen the introduction of an index into the dbo.SplitListWithIndex function simplified the code, making the use of a ranking window function unnecessary. It would be useful for the STRING_SPLIT to provide the same, as this time of processing is pretty common.
Here’s another example based on the PIVOT clause introduced also in SQL 2005:
-- parsing delimited values (SQL Server 2016+) SELECT P.ProductNumber , Cast(Trim([1]) as nvarchar(20)) ItemId , Cast(Trim([2]) as nvarchar(20)) ConfigId , Cast(Trim([3]) as nvarchar(20)) InventSizeid , Cast(Trim([4]) as nvarchar(20)) InventColorId , Cast(Trim([5]) as nvarchar(20)) StyleId FROM ( SELECT DAT.ProductNumber , XT.VALUE , ROW_NUMBER() OVER (PARTITION BY DAT.ProductNumber ORDER BY DAT.ProductNumber) Ranking FROM ( VALUES ('100001::S:black:') , ('100001::M:black:') , ('100001::L:black:') , ('100001::XL:black:') , ('100001::S:white:') , ('100001::M:white:') , ('100001::L:white:') , ('100001::XL:white:') ) DAT (ProductNumber) CROSS APPLY STRING_SPLIT(DAT.ProductNumber, ':') XT ) DAT PIVOT (MAX(DAT.[VALUE]) FOR DAT.Ranking IN ([1],[2],[3],[4],[5])) P
The queries work also in SQL databases in Microsoft Fabric (see file in GitHub repository). You might want to use another schema (e.g. Test), not to interfere with the existing code.
Happy coding!