Reputation: 87
Read the question posed here, but mine is a little more complicated.
I have a string that is variable in length, and the delimiter can sometimes be two dashes, or sometimes it can be just one. Let's say in my table the data that I want to break out is stored in a single column like this:
+ -----------------------------------------+
| Category |
+------------------------------------------+
| Zoo - Animals - Lions |
| Zoo - Personnel |
| Zoo - Operating Costs - Power / Cooling |
+------------------------------------------+
But I want to output the data string from that single column into three separate columns like this:
+----------+--------------------+-----------------+
| Location | Category | Sub-Category |
+----------+--------------------+-----------------+
| Zoo | Animals | Lions |
| Zoo | Personnel | |
| Zoo | Operating Costs | Power / Cooling |
+----------+--------------------+-----------------+
Hoping for some guidance as the samples I've been finding on Google seem to be simpler than this.
Upvotes: 0
Views: 1409
Reputation: 67311
You've tagged this with [sql-server-2017]
. That means, that you can use JSON-support (this was introduced with v2016).
Currently JSON is the best built-in approach for position- and type-safe string splitting:
A mockup, to simulate your issue
DECLARE @mockup TABLE (ID INT IDENTITY, Category VARCHAR(MAX))
INSERT INTO @mockup (Category)
VALUES ('Zoo - Animals - Lions')
,('Zoo - Personnel')
,('Zoo - Operating Costs - Power / Cooling');
--The query
SELECT t.ID
,A.[Location]
,A.Category
,A.subCategory
FROM @mockup t
CROSS APPLY OPENJSON(CONCAT('[["',REPLACE(t.Category,'-','","'),'"]]'))
WITH ([Location] VARCHAR(MAX) '$[0]'
,Category VARCHAR(MAX) '$[1]'
,SubCategory VARCHAR(MAX) '$[2]') A;
The result (might need some TRIM()
ing)
ID Location Category subCategory
1 Zoo Animals Lions
2 Zoo Personnel NULL
3 Zoo Operating Costs Power / Cooling
The idea in short:
We use some simple string operations to transform your string into a JSON array:
a b c => [["a","b","c"]]
Now we can use OPENJSON()
together with a WITH
-clause to return each fragment by its position with a fixed type.
Upvotes: 1
Reputation: 33581
You can also use a string splitter. Here is an excellent one that works with your version. DelimitedSplit8K
Now we need some sample data.
declare @Something table
(
Category varchar(100)
)
insert @Something values
('Zoo - Animals - Lions')
, ('Zoo - Personnel')
, ('Zoo - Operating Costs - Power / Cooling')
Now that we have a function and sample data the code for this is quite nice and tidy.
select s.Category
, Location = max(case when x.ItemNumber = 1 then Item end)
, Category = max(case when x.ItemNumber = 2 then Item end)
, SubCategory = max(case when x.ItemNumber = 3 then Item end)
from @Something s
cross apply dbo.DelimitedSplit8K(s.Category, '-') x
group by s.Category
And this will return:
Category |Location|Category |SubCategory
Zoo - Animals - Lions |Zoo |Animals |Lions
Zoo - Operating Costs - Power / Cooling |Zoo |Operating Costs|Power / Cooling
Zoo - Personnel |Zoo |Personnel |NULL
Upvotes: 2
Reputation: 222482
Here is a solution that uses solely string functions:
select
left(
category,
charindex('-', category) - 2
) location,
substring(
category,
charindex('-', category) + 2,
len(category) - charindex('-', category, charindex('-', category) + 1)
) category,
case when charindex('-', category, charindex('-', category) + 1) > 0
then right(category, charindex('-', reverse(category)) - 2)
end sub_category
from t
location | category | sub_category :------- | :--------------- | :-------------- Zoo | Animal | Lions Zoo | Personnel | null Zoo | Operating Costs | Power / Cooling
Upvotes: 2
Reputation: 13641
Bit of hack, but it works:
DECLARE @t TABLE (Category VARCHAR(255))
INSERT @t (Category)
VALUES ('Zoo - Animals - Lions'),('Zoo - Personnel'),('Zoo - Operating Costs - Power / Cooling')
;WITH split_vals AS (
SELECT Category AS Cat,TRIM(Value) AS Value,ROW_NUMBER() OVER (PARTITION BY Category ORDER BY Category) AS RowNum
FROM @t
CROSS APPLY STRING_SPLIT(Category,'-')
), cols AS (
SELECT
Cat,
CASE WHEN RowNum = 1 THEN Value END AS Location,
CASE WHEN RowNum = 2 THEN Value END AS Category,
CASE WHEN RowNum = 3 THEN Value END AS [Sub-Category]
FROM split_vals
)
SELECT STRING_AGG(Location, '') AS Location,
STRING_AGG(Category, '') AS Category,
STRING_AGG([Sub-Category], '') AS [Sub-Category]
FROM cols
GROUP BY Cat;
Upvotes: 0