Use Split String Table Value Function in a Select statement

The name of the picture


Use Split String Table Value Function in a Select statement



I have a table from a third party source that I have no control over. Each address field contains the FULL address including postcode. Each address has a dynamic number of separate address fields included. I have got a Split() TVF that works in splitting the address fields into seperate rows. However, when I CROSS APPLY the TVF table back to the SELECT query it returns a row for each ro in the TVF table. How do I get it to return one row for the main table and separate COLUMNS from the rows in the TVF table?



Example of addresses supplied:



a. 1 The Street, The Locality, The Town, The County, The Postcode



b. 2 The Building, The Street, The Town, The Postcode



c. Floor 3, 3 The Street, The Locality, The Town, The County, The Postcode



The TVF returns these as one value per row using the ',' as a delimiter. I then need to join that data back to the original data as one column per address field.



This is my SELECT QUERY:


select DISTINCT TOP 5 ttp.ProjectID
,cac.ID
,cac1.Proprietor1Address1
--,CASE WHEN addr.ID = 1 THEN addr.Data END AS Address1
FROM ArdentTest.ardent.LRTitlesToProcess ttp

JOIN LandRegistryData.landreg.CommercialandCorporateOwnershipData cac
ON ttp.TitleNo = cac.TitleNumber

JOIN (SELECT TitleNumber
,Proprietor1Address1
FROM LandRegistryData.landreg.CommercialandCorporateOwnershipData
WHERE 1 = 1
AND ISNULL(Proprietor1Address1, '') <> '') cac1
ON ttp.TitleNo = cac1.TitleNumber

CROSS APPLY DBAdmin.resource.Split(cac1.Proprietor1Address1, ', ') addr

WHERE 1 = 1
AND ttp.DateLRRequestSent IS NULL
AND cac.ID IN (50764, 78800, 157089, 206049, 449112)
ORDER BY 1



Which produces the following results:


ProjectID ID Proprietor1Address1
1010 50764 Bridge House, 1 Walnut Tree Close, Guildford, Surrey GU1 4LZ
1010 78800 Bridge House, 1 Walnut Tree Close, Guildford, Surrey GU1 4LZ
1010 157089 Bridge House, 1 Walnut Tree Close, Guildford, Surrey GU1 4LZ
1010 206049 Bridge House, 1 Walnut Tree Close, Guildford, Surrey GU1 4LZ
1010 449112 Church House, Great Smith Street, London SW1P 3AZ



I need to use the rows from the function to add separate address columns to the result set and I cannot figure out how to do it.





You cannot write a query that will return an unknown number of columns. Every query always produces a result set with a fixed "shape" - the number of columns, their names and their types.
– Damien_The_Unbeliever
4 hours ago





@Damien_The_Unbeliever except when using dynamic pivot...
– Zohar Peled
4 hours ago





@ZoharPeled - but I would argue that is because when you're using dynamic SQL, you're constructing a new query to execute. Of course, that new query may have any shape you choose. It doesn't change the basic premise that any particular query gives results with a fixed shape.
– Damien_The_Unbeliever
4 hours ago





@Damien_The_Unbeliever I accept your claim. Still, a dynamic pivot can help the OP get the desired output.
– Zohar Peled
4 hours ago





I may not have been particularly clear in my original question. The use of the term DYNAMIC doesnot apply to the number of rows or columns in a table. It applies to the number of address fields contained within a SINGLE column from the external data source. I need to SPLIT this SINGLE column into different address columns to format it correctly for the database it is going into.
– Allen Jones
4 hours ago




2 Answers
2



If you can make some assumption like an address can contain max 10 parts. You can use this script. I used STRING_SPLIT instead of your split function.


DECLARE @Table TABLE (Id INT, Address VARCHAR(500))
INSERT INTO @Table VALUES
(1, '1 The Street, The Locality, The Town, The County, The Postcode'),
(2, '2 The Building, The Street, The Town, The Postcode'),
(3, 'Floor 3, 3 The Street, The Locality, The Town, The County, The Postcode')

SELECT * FROM @Table T
CROSS APPLY(
SELECT * FROM (SELECT ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) RN, * FROM STRING_SPLIT(T.Address, ',')) SRC
PIVOT (MAX(value) FOR RN IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])) PVT
) X



Result:


Id Address 1 2 3 4 5 6 7 8 9 10
----------- -------------------------------------------------------------------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
1 1 The Street, The Locality, The Town, The County, The Postcode 1 The Street The Locality The Town The County The Postcode NULL NULL NULL NULL NULL
2 2 The Building, The Street, The Town, The Postcode 2 The Building The Street The Town The Postcode NULL NULL NULL NULL NULL NULL
3 Floor 3, 3 The Street, The Locality, The Town, The County, The Postcode Floor 3 3 The Street The Locality The Town The County The Postcode NULL NULL NULL NULL





STRING_SPLIT is new in SQL 2016. Unfortunately we have not upgraded yet. Still on SQL 2014 :-(
– Allen Jones
4 hours ago





I used STRING_SPLIT for test purpose, you can use your own split function instead of it.
– Serkan Arslan
4 hours ago





One problem with this approach is ,that STRING_SPLIT does not guarantee to return the elements in the given order... There are alternatives returning the parts together with an index (e.g. Jeff Moden's DelimitedSplit8K)
– Shnugo
3 hours ago


STRING_SPLIT


DelimitedSplit8K



You can create an XML out of your concatenated string and address each part via index



--the table (thx Serkan, I copied your DDL)


DECLARE @Table TABLE (Id INT, Address VARCHAR(500))
INSERT INTO @Table VALUES
(1, '1 The Street, The Locality, The Town, The County, The Postcode'),
(2, '2 The Building, The Street, The Town, The Postcode'),
(3, 'Floor 3, 3 The Street, The Locality, The Town, The County, The Postcode');



--This query will return the parts one by one


WITH Splitted AS
(
SELECT *
,CAST('<x>' + REPLACE((SELECT [Address] AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML) AsXml
FROM @Table
)
SELECT *
,AsXml.value('/x[1]','nvarchar(max)') AS Col1
,AsXml.value('/x[2]','nvarchar(max)') AS Col2
,AsXml.value('/x[3]','nvarchar(max)') AS Col3
,AsXml.value('/x[4]','nvarchar(max)') AS Col4
,AsXml.value('/x[5]','nvarchar(max)') AS Col5
,AsXml.value('/x[6]','nvarchar(max)') AS Col6
FROM Splitted



--the result


/*+----+----------------+--------------+--------------+--------------+--------------+--------------+
| ID | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+----+----------------+--------------+--------------+--------------+--------------+--------------+
| 1 | 1 The Street | The Locality | The Town | The County | The Postcode | NULL |
+----+----------------+--------------+--------------+--------------+--------------+--------------+
| 2 | 2 The Building | The Street | The Town | The Postcode | NULL | NULL |
+----+----------------+--------------+--------------+--------------+--------------+--------------+
| 3 | Floor 3 | 3 The Street | The Locality | The Town | The County | The Postcode |
+----+----------------+--------------+--------------+--------------+--------------+--------------+*/



--You might use REVERSE on all steps. This will bring the Postcode in the first place in all cases (as long as the postcode is the last element there)


REVERSE


WITH Splitted AS
(
SELECT *
,CAST('<x>' + REPLACE((SELECT REVERSE([Address]) AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML) AsXml
FROM @Table
)
SELECT *
,REVERSE(AsXml.value('/x[1]','nvarchar(max)')) AS Col1
,REVERSE(AsXml.value('/x[2]','nvarchar(max)')) AS Col2
,REVERSE(AsXml.value('/x[3]','nvarchar(max)')) AS Col3
,REVERSE(AsXml.value('/x[4]','nvarchar(max)')) AS Col4
,REVERSE(AsXml.value('/x[5]','nvarchar(max)')) AS Col5
,REVERSE(AsXml.value('/x[6]','nvarchar(max)')) AS Col6
FROM Splitted;



--the result


/*+----+--------------+------------+------------+----------------+--------------+---------+
| ID | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+----+--------------+------------+------------+----------------+--------------+---------+
| 1 | The Postcode | The County | The Town | The Locality | 1 The Street | NULL |
+----+--------------+------------+------------+----------------+--------------+---------+
| 2 | The Postcode | The Town | The Street | 2 The Building | NULL | NULL |
+----+--------------+------------+------------+----------------+--------------+---------+
| 3 | The Postcode | The County | The Town | The Locality | 3 The Street | Floor 3 |
+----+--------------+------------+------------+----------------+--------------+---------+*/






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Keycloak server returning user_not_found error when user is already imported with LDAP

415 Unsupported Media Type while sending json file over REST Template

PHP parse/syntax errors; and how to solve them?