September 7, 2022 at 5:27 am
I have string in table where column has the value 163 Bathroom {BATHROOM CLEANING}{2.5%}
I want to 2 new columns from this string
column1 BATHROOM CLEANING
column2 2.5%
The first curly bracket { is starting point for first new column and will end data closing curly } bracket and second curly bracket { for second new column and will end data closing curly } bracket
September 7, 2022 at 7:26 am
ah, the joys of would be JSON
SUBSTRING to the rescue
declare @wouldbejson NVARCHAR(200) = N'{BATHROOM CLEANING}{2.5%}'
Select charindex(N'{',@wouldbejson, 1) as firstoffset
, charindex(N'}',@wouldbejson,1) as firstEndOffset
, charindex(N'}{',@wouldbejson, 1) as Secondoffset
, charindex(N'}',@wouldbejson,charindex(N'}{',@wouldbejson, 1)+1) LastEndOffset
, SUBSTRING(@wouldbejson, charindex(N'{',@wouldbejson, 1)+1, charindex(N'}',@wouldbejson,1) -2) Firststring
, SUBSTRING(@wouldbejson
, charindex(N'}{',@wouldbejson, 1)+2
, charindex(N'}',@wouldbejson,charindex(N'}{',@wouldbejson, 1)+1) - (charindex(N'}{',@wouldbejson, 1)+2 )
) SecondString
This is of course no solution for larger sets of columns, in that case I suggest you have a look into
Tally OH! An Improved SQL 8K “CSV Splitter” Function by Jeff Moden
Select *
from master.dbo.fn_DBA_Split8k(@wouldbejson,'{') -- ref: Tally OH! An Improved SQL 8K “CSV Splitter” Function
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution 😀
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
September 7, 2022 at 11:26 am
Here is a quick sample that should get you over this hurdle
😎
USE TEEST;
GO
SET NOCOUNT ON;
GO
---------------------------------------------------------------------
-- EXAMPLE 1: SPLITTING A SINGLE STRING
---------------------------------------------------------------------
DECLARE @TSTR VARCHAR(100) = '163 Bathroom {BATHROOM CLEANING}{2.5%}';
DECLARE @STRNO INT = 1;
;WITH BASE_SPLIT(ItemNumber,Item,STRNO) AS
(
SELECT
X.ItemNumber
,X.Item
,@STRNO AS STRNO
FROM dbo.DelimitedSplit8K_LEAD(REPLACE(@TSTR,CHAR(125),''),CHAR(123)) AS X
)
,COLUMN_DATA AS
(
SELECT
BS.STRNO
,CASE WHEN BS.ItemNumber = 1 THEN BS.Item END AS COL_1
,CASE WHEN BS.ItemNumber = 2 THEN BS.Item END AS COL_2
,CASE WHEN BS.ItemNumber = 3 THEN BS.Item END AS COL_3
FROM BASE_SPLIT BS
)
SELECT
CD.STRNO
,MAX(CD.COL_1) AS COL1
,MAX(CD.COL_2) AS COL2
,MAX(CD.COL_3) AS COL3
FROM COLUMN_DATA CD
GROUP BY CD.STRNO;
GO
---------------------------------------------------------------------
-- EXAMPLE 2: SPLITTING A SET OF STRINGS
---------------------------------------------------------------------
;WITH SAMPLE_DATA(STRNO,TSTR)
AS
(
SELECT 1,'163 Bathroom {BATHROOM CLEANING}{2.5%}' UNION ALL
SELECT 2,'164 Bathroom {BATHROOM USAGE}{3.5%}' UNION ALL
SELECT 3,'165 Bathroom {BATHROOM ENTRY}{4.5%}'
)
,BASE_SPLIT(ItemNumber,Item,STRNO) AS
(
SELECT
X.ItemNumber
,X.Item
,SD.STRNO AS STRNO
FROM SAMPLE_DATA SD
CROSS APPLY dbo.DelimitedSplit8K_LEAD(REPLACE(SD.TSTR,CHAR(125),''),CHAR(123)) AS X
)
,COLUMN_DATA AS
(
SELECT
BS.STRNO
,CASE WHEN BS.ItemNumber = 1 THEN BS.Item END AS COL_1
,CASE WHEN BS.ItemNumber = 2 THEN BS.Item END AS COL_2
,CASE WHEN BS.ItemNumber = 3 THEN BS.Item END AS COL_3
FROM BASE_SPLIT BS
)
SELECT
CD.STRNO
,MAX(CD.COL_1) AS COL1
,MAX(CD.COL_2) AS COL2
,MAX(CD.COL_3) AS COL3
FROM COLUMN_DATA CD
GROUP BY CD.STRNO;
GO
---------------------------------------------------------------------
-- SOURCE 1: [dbo].[DelimitedSplit8K_LEAD]
-- REF: https://www.sqlservercentral.com/articles/reaping-the-benefits-of-the-window-functions-in-t-sql-2
-- IMPROVED VERSION OF Jeff Moden's outstanding DelimitedSplit8K
--
---------------------------------------------------------------------
CREATE OR ALTER FUNCTION [dbo].[DelimitedSplit8K_LEAD]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table” produces values from 0 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(@pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0)
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(@pString,s.N1,ISNULL(NULLIF((LEAD(s.N1,1,1) OVER (ORDER BY s.N1) - 1),0)-s.N1,8000))
FROM cteStart s
;
GO
Result sets
STRNO COL1 COL2 COL3
----------- --------------- ------------------- ------
1 163 Bathroom BATHROOM CLEANING 2.5%
STRNO COL1 COL2 COL3
----------- --------------- ------------------- ------
1 163 Bathroom BATHROOM CLEANING 2.5%
2 164 Bathroom BATHROOM USAGE 3.5%
3 165 Bathroom BATHROOM ENTRY 4.5%
September 7, 2022 at 4:18 pm
;WITH cte_test_data AS (
SELECT CAST('163 Bathroom {BATHROOM CLEANING}{2.5%}' AS varchar(100)) AS string
)
SELECT
string AS original_string,
CASE WHEN middle_delimiter = 0 THEN ''
ELSE SUBSTRING(string, first_delimiter + 1, middle_delimiter - first_delimiter - 1) END AS string1,
CASE WHEN middle_delimiter = 0 THEN ''
ELSE SUBSTRING(string, middle_delimiter + 2, CHARINDEX('}', SUBSTRING(string, middle_delimiter + 2, 500)) - 1) END AS string2
FROM cte_test_data
CROSS APPLY (
SELECT CHARINDEX('{', string) AS first_delimiter, CHARINDEX('}{', string) AS middle_delimiter
) AS calc1
SQL DBA,SQL Server MVP(07, 08, 09) "It's a dog-eat-dog world, and I'm wearing Milk-Bone underwear." "Norm", on "Cheers". Also from "Cheers", from "Carla": "You need to know 3 things about Tortelli men: Tortelli men draw women like flies; Tortelli men treat women like flies; Tortelli men's brains are in their flies".
September 8, 2022 at 1:24 pm
Thank you every one it work the way you guys told me. Appreciate all your help.
September 27, 2022 at 12:19 pm
I use a combo of CTE and XML to split the strings makes it super fast and easy.
DECLARE @DELIMITER VARCHAR(50)='|||'
;WITH CTE AS
(SELECT
S.NAME
,SOUCE=S.RAW_DATA
,XML=CAST('<M>' + REPLACE(S.RAW_DATA, @DELIMITER, '</M><M>') + '</M>' AS XML)
FROM ##SYS_INVENTORY AS S
WHERE
S.SERVER_NAME=@SERVER
AND S.CAT='LIST OS INFO'
AND S.NAME='MAC_INFO')
INSERT INTO #MACS
(INTERFACE_NAME
,MAC_ADDRESS
,INTERFACE_INDEX
,SPEED)
SELECT
INTERFACE_NAME= XML.value('/M[1]', 'VARCHAR(128)')
,MAC_ADDRESS= XML.value('/M[2]', 'VARCHAR(17)')
,INTERFACE_INDEX= XML.value('/M[3]', 'SMALLINT')
,SPEED= XML.value('/M[4]', 'BIGINT')
FROM CTE
WHERE
XML.value('/M[1]', 'VARCHAR(50)')<>''--BLANK NAMES ARE VMWARE INTERFACES AND ARE DUPLICATED ACCROSS ALL SYSTEMS ON THE HOST
AND XML.value('/M[2]', 'VARCHAR(50)') NOT IN ('00:05:9A:3C:7A:00','0A:00:27:00:00:09')
September 27, 2022 at 7:29 pm
I use a combo of CTE and XML to split the strings makes it super fast and easy.
DECLARE @DELIMITER VARCHAR(50)='|||'
;WITH CTE AS
(SELECT
S.NAME
,SOUCE=S.RAW_DATA
,XML=CAST('<M>' + REPLACE(S.RAW_DATA, @DELIMITER, '</M><M>') + '</M>' AS XML)
FROM ##SYS_INVENTORY AS S
WHERE
S.SERVER_NAME=@SERVER
AND S.CAT='LIST OS INFO'
AND S.NAME='MAC_INFO')
INSERT INTO #MACS
(INTERFACE_NAME
,MAC_ADDRESS
,INTERFACE_INDEX
,SPEED)
SELECT
INTERFACE_NAME= XML.value('/M[1]', 'VARCHAR(128)')
,MAC_ADDRESS= XML.value('/M[2]', 'VARCHAR(17)')
,INTERFACE_INDEX= XML.value('/M[3]', 'SMALLINT')
,SPEED= XML.value('/M[4]', 'BIGINT')
FROM CTE
WHERE
XML.value('/M[1]', 'VARCHAR(50)')<>''--BLANK NAMES ARE VMWARE INTERFACES AND ARE DUPLICATED ACCROSS ALL SYSTEMS ON THE HOST
AND XML.value('/M[2]', 'VARCHAR(50)') NOT IN ('00:05:9A:3C:7A:00','0A:00:27:00:00:09')
Easy... maybe. Super fast... probably not. It only looks that way if you're working with small numbers of rows. Have a look at the following chart and understand that the bright magenta line is the XML performance curve for such methods... especially those that need to have the XML delimiters added, like you did.
Please see the now ages old article for an explanation of what the ??? mark line is.
There's also a mod available to that code that makes it twice as fast. That mod can be found in "Part 1" of the article at the following link.
https://www.sqlservercentral.com/articles/reaping-the-benefits-of-the-window-functions-in-t-sql-2
--Jeff Moden
Change is inevitable... Change for the better is not.
Viewing 7 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic. Login to reply