create while loop with cte - sql

how to create sql server cte from a while loop
my loop like this
declare #ind as int
declare #code as nvarchar
set #ind = 0
while #ind < 884
begin
select #ind = #ind + 1
--here execute Procedure
--and set return value to variable
set #code = cast (#ind as nvarchar)
end

If you need table:
;WITH Sec(Number) AS
(
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM Sec
WHERE Number < 884
)
SELECT * FROM Sec
OPTION(MAXRECURSION 0)
If you need one string:
;WITH Sec(Number) AS
(
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM Sec
WHERE Number < 884
)
SELECT STUFF(a.[Str], 1, 1, '')
FROM
(
SELECT (SELECT ',' + CAST(Number AS NVARCHAR(3))
FROM Sec
FOR XML PATH(''), TYPE
).value('.','varchar(max)') AS [Str]
) AS a
OPTION(MAXRECURSION 0)

Below query selects values from 0 to 884:
;WITH T(Num)AS
(
SELECT 0
UNION ALL
SELECT Num+1 FROM T WHERE T.Num < 884
)SELECT Num FROM T
OPTION (MAXRECURSION 0);

Related

number of ascii characters > number of non-ascii characters in sql

I am using the following codes to detect the description contains non-ascii.
Now I want to set the condition to be number of ascii characters > number of non-ascii characters in description, how should I write it in SQL statement?
declare #str varchar(1024)
set #str = '|' + char(9) + '|' + char(10) + '|' + char(13)
-- Add all normal ASCII characters (32 -> 127)
declare #i int
set #i = 32
while #i <= 127
begin
-- Uses | to escape, could be any character
set #str = #str + '|' + char(#i)
set #i = #i + 1
end
--select description, locale
select description, productlocale, locale
FROM [DataExtraction].[dbo].[Feedback]
where
and description like '%[^' + #str + ']%' escape '|'
Could use a function:
create function dbo.fn_CountAscii(#str varchar(8000))
returns int
as
begin
declare #i int = 1
declare #n int = 0
while #i <= len(#str)
begin
if ascii(substring(#str, #i, 1)) between 32 and 127
begin
set #n = #n + 1
end
set #i = #i + 1
end
return #n
end
go
declare #str varchar(8000)
set #str = '|' + char(9) + '|' + char(10) + '|' + char(13)
select #str,
convert(float, dbo.fn_CountAscii(#str)) / len(#str) as Ratio,
case when convert(float, dbo.fn_CountAscii(#str)) / len(#str) > 0.5 then 1 else 0 end as MoreAsciiThanNonAscii
This might be horribly slow if your data set is large, but suck it and see
The most efficient way to do something like this that I can think of is to use a Tally (aka Numbers) table. If you are not familiar with the concept you can read about it here: http://www.sqlservercentral.com/articles/T-SQL/62867/
You can either create and populate a Tally table in your database (I create one in my utility database from the get-go because it is so useful) or you can build one on the fly using a CTE but that makes for more code you have to generate. For this post I will use the CTE method so you can just copy and paste the solution to try it out:
DECLARE #pString VARCHAR(8000);
SELECT #pString = 'Çüé the quick brown fox which gave me 50¢.';
--===== "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
TallyCTE(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
)
--===== Return all t.N+1 positions and calculate the length of the element for each starting position
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N),
StartPosition = s.N,
TheCharacter = SUBSTRING(#pString, N, 1)
FROM TallyCTE s
WHERE N <= LEN(#pString)
AND ASCII(SUBSTRING(#pString, N, 1)) > 127
If you were to persist the Tally table your query could be simplifed like this:
DECLARE #Parameter VARCHAR(8000);
SET #Parameter = 'Çüé,Element01,Element02,Element03,Element04,Element05,50¢';
SELECT N,
SUBSTRING(#Parameter, N, 1)
FROM dbo.Tally
WHERE N <= LEN(#Parameter)
AND ASCII(SUBSTRING(#Parameter, N, 1)) > 127
ORDER BY N;
Results from both are:

SQL Every combination of ID's

Quite a tricky scenario. I have a table as below. Basically I want to get all combinations of ranges from each RangeSet in SQL Server 2012.
Best I show an example of structure and desired output. The problem is the number of RangeSetID's can be dynamic and the number of RangeID's can be dynamic in each range set
RangeID RangeSetID
------------------
1 4
2 4
3 4
4 4
5 2
6 2
7 2
8 2
9 2
10 2
11 1
12 1
13 1
14 1
15 1
16 1
17 3
18 3
19 3
20 3
I need the output to recursively create the below dataset of rates:
1 5 11 17 (first from range4, first from range2, first from range1, first from range3)
1 5 11 18 (first from range4, first from range2, first from range1, second from range3)
1 5 11 19 (first from range4, first from range2, first from range1, third from range3)
1 5 11 20 (first from range4, first from range2, first from range1, fourth from range3)
1 5 12 17 (first from range4, first from range2, second from range1, first from range3)
1 5 12 18 (first from range4, first from range2, second from range1, second from range3)
1 5 12 19
1 5 12 20
And so on until I reach the last RangeID from each RangeSetID and result in
4 10 16 20 (last from range4, last from range2, last from range1, last from range3)
Which will ultimately result in the below where RateID 1 is showing the first result vertically to allow for the dynamic number of RangeSetID's
RateID RangeID
------------------
1 1
1 5
1 11
1 17
2 1
2 5
2 11
2 18
This should result in 11,000 rows (approx). I have tried CROSS JOIN's etc but I cannot get this working at all.
Any geniuses out there please?
Thanks
Guess this should help. Happy coding!
;WITH CTE
AS
(
SELECT * FROM (
SELECT ROW_NUMBER() over (order by [RangeID1] , [RangeID2], [RangeID3], [RangeID4]) as 'RateID', [RangeID1] , [RangeID2], [RangeID3], [RangeID4] FROM
(
select A.RangeID as [RangeID1], B.RangeID as [RangeID2], C.RangeID as [RangeID3], D.RangeID as [RangeID4]
from [Range] as A
inner join [Range] as B on (A.RangeID <= B.RangeID)
inner join [Range] as C on (B.RangeID <= C.RangeID)
inner join [Range] as D on (C.RangeID <= D.RangeID)
where A.RangeSetID <> B.RangeSetID
and B.RangeSetID <> C.RangeSetID
and C.RangeSetID <> D.RangeSetID
) as A) T
UNPIVOT ( RangeID FOR N IN ([RangeID1] , [RangeID2], [RangeID3], [RangeID4] ))P
)
SELECT RateID, RangeID
FROM CTE
This sort of works, but it's way overcomplicated and could do with some tweaking. Note that I changed your sample data, to include gaps to test this works properly.
DECLARE #table TABLE (
range_id INT,
range_set_id INT);
INSERT INTO #table SELECT 1, 4;
INSERT INTO #table SELECT 2, 4;
INSERT INTO #table SELECT 3, 4;
INSERT INTO #table SELECT 5, 4;
INSERT INTO #table SELECT 8, 2;
INSERT INTO #table SELECT 10, 2;
INSERT INTO #table SELECT 17, 2;
INSERT INTO #table SELECT 18, 2;
INSERT INTO #table SELECT 19, 2;
INSERT INTO #table SELECT 20, 2;
INSERT INTO #table SELECT 21, 1;
INSERT INTO #table SELECT 23, 1;
INSERT INTO #table SELECT 28, 1;
INSERT INTO #table SELECT 29, 1;
INSERT INTO #table SELECT 30, 1;
INSERT INTO #table SELECT 33, 1;
INSERT INTO #table SELECT 35, 3;
INSERT INTO #table SELECT 38, 3;
INSERT INTO #table SELECT 39, 3;
INSERT INTO #table SELECT 40, 3;
--Work out the order of the range_set_ids
WITH ordered AS (
SELECT
range_set_id,
range_id,
ROW_NUMBER() OVER (PARTITION BY range_set_id ORDER BY range_id) AS sequential_id
FROM
#table),
ranges AS (
SELECT
range_set_id,
MIN(range_id) AS range_id
FROM
#table
GROUP BY
range_set_id),
range_order AS (
SELECT
range_set_id,
ROW_NUMBER() OVER (ORDER BY range_id) AS order_id
FROM
ranges),
set_count AS (
SELECT
MAX(order_id) AS max_order_id
FROM
range_order),
start_and_end AS (
SELECT
o.range_set_id,
o.order_id,
MIN(range_id) AS min_range_id,
MAX(range_id) AS max_range_id,
COUNT(range_id) AS iterations
FROM
range_order o
INNER JOIN #table t ON t.range_set_id = o.range_set_id
GROUP BY
o.range_set_id,
o.order_id),
toggles AS (
SELECT
s.range_set_id,
s.order_id,
s.iterations AS toggle
FROM
start_and_end s
CROSS JOIN set_count c
WHERE
s.order_id = c.max_order_id
UNION ALL
SELECT
s.range_set_id,
s.order_id,
t.toggle * (s.iterations) AS toggle
FROM
toggles t
INNER JOIN start_and_end s ON s.order_id = t.order_id - 1
WHERE
s.order_id > 0),
toggle_count AS (
SELECT
MAX(toggle * s.iterations) AS max_toggle
FROM
toggles t
CROSS JOIN set_count c
INNER JOIN start_and_end s ON s.order_id = c.max_order_id),
all_combos AS (
SELECT
1 AS rate_set_id,
o.range_set_id,
1 AS sequential_id,
o.order_id,
lt.toggle AS reset_toggle,
ISNULL(t.toggle, 1) AS increment_toggle,
1 AS current_toggle
FROM
range_order o
CROSS JOIN set_count c
INNER JOIN toggles lt ON lt.order_id = o.order_id
LEFT JOIN toggles t ON t.order_id = o.order_id + 1
UNION ALL
SELECT
a.rate_set_id + 1,
a.range_set_id,
CASE
WHEN a.current_toggle = a.reset_toggle THEN 1 --flip back at the end
WHEN a.current_toggle % a.increment_toggle != 0 THEN a.sequential_id --something lower is still toggling
ELSE a.sequential_id + 1 --toggle
END,
a.order_id,
a.reset_toggle,
a.increment_toggle,
CASE
WHEN a.current_toggle < a.reset_toggle THEN a.current_toggle + 1
ELSE 1
END
FROM
all_combos a
CROSS JOIN set_count sc
CROSS JOIN toggle_count tc
WHERE
a.rate_set_id < tc.max_toggle)
SELECT
a.rate_set_id,
a.range_set_id,
o.range_id
FROM
all_combos a
INNER JOIN ordered o ON o.range_set_id = a.range_set_id AND o.sequential_id = a.sequential_id
ORDER BY
a.rate_set_id,
a.order_id
OPTION (MAXRECURSION 0);
Implemented same logic in dynamic query. This should work for you, I guess
declare #i int = 1;
declare #count int = 0;
declare #cols varchar(max) = '';
declare #select varchar(max) = 'select ';
declare #join varchar(max);
declare #where varchar(max);
declare #query varchar(max);
declare #range varchar(100);
declare #prevrange varchar(100);
declare #rangeid varchar(100);
select #count =count(distinct RangeSetID) from [Range];
while #count > 0
begin
set #range = 'Range' + cast(#i as varchar(max));
set #rangeid = 'RangeID' + cast(#i as varchar(max));
set #cols = #cols + #rangeid + ', ';
set #select = #select + #range + '.RangeID as '+#rangeid + ', ';
if #i = 1
begin
set #join = ' from [Range] as ' + #range;
set #where = 'where ' + #range + '.RangeSetID <> ';
end
else
begin
set #prevrange = 'Range' + cast((#i - 1) as varchar(max));
set #join = #join + ' inner join [Range] as ' + #range + ' on (' + #prevrange + '.RangeID <= ' + #range + '.RangeID)';
if(#count = 1)
set #where = #where + #range+ '.RangeSetID';
else
set #where = #where + #range+ '.RangeSetID and '+ #range+ '.RangeSetID <> ';
end
set #i = #i + 1;
set #count = #count - 1;
end
set #query = '
;WITH CTE
AS
(
SELECT * FROM (
SELECT ROW_NUMBER() over (order by '+ SUBSTRING(#cols, 0, LEN(#cols)) + ') as ''RateID'', '+ SUBSTRING(#cols, 0, LEN(#cols)) +' FROM
(
' + SUBSTRING(#select, 0, LEN(#select)) + char(13) + #join + char(13) + #where + '
) as A) T
UNPIVOT ( RangeID FOR N IN ('+(SUBSTRING(#cols, 0, LEN(#cols))) +' ))P
)
SELECT RateID, RangeID
FROM CTE
';
exec (#query);

How to extract URL querystring parameters in SQL Server without writing a function?

I'm having trouble identifying all the querystring parameters that are used on a site. I want to write a T-SQL query that extracts all parameters and counts them, but I don't have permission to write SQL functions, so this solution isn't much help.
The field that I'm working with (Query) includes data that looks like this:
_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15
...
The query I need to write would return the result:
querystring | count
___________________
_ | 1
tab | 2
sort | 2
page | 2
pagesize | 1
...
Any help is greatly appreciated.
You can borrow one of the functions from here and just inline it into the query.
An example below. I would not expect good performance. Creating a CLR function is by far the most efficient way of splitting strings prior to SQL Server 2016.
DECLARE #QueryStrings Table
(
Query VARCHAR(8000)
)
INSERT INTO #QueryStrings
VALUES
('INVALID'),
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15');
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),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b)
SELECT parameter, count(*)
FROM #QueryStrings qs
CROSS APPLY (SELECT SUBSTRING(qs.Query, t.N + 1, ISNULL(NULLIF(CHARINDEX('&', qs.Query, t.N + 1), 0) - t.N - 1, 8000))
FROM (SELECT 0
UNION ALL
SELECT TOP (DATALENGTH(ISNULL(qs.Query, 1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM E42) t(N)
WHERE ( SUBSTRING(qs.Query, t.N, 1) = '&'
OR t.N = 0 )) ca1(split_result)
CROSS APPLY (SELECT CHARINDEX('=',split_result)) ca2(pos)
CROSS APPLY (SELECT CASE WHEN pos > 0 THEN LEFT(split_result,pos-1) END,
CASE WHEN pos > 0 THEN SUBSTRING(split_result, pos+1,8000) END
WHERE pos > 0) ca3(parameter,value)
GROUP BY parameter
More sexy way to approach that:
DECLARE #xml xml
;WITH cte AS (
SELECT *
FROM (VALUES
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15')
) as T(Query))
SELECT #xml = (
SELECT CAST(
(
SELECT '<d><param>' + REPLACE(REPLACE((STUFF((
SELECT '/' + REPLACE(REPLACE(Query,'&','/'),'=','!')
FROM cte
FOR XML PATH('')
),1,1,'')),'/','</value><param>'),'!','</param><value>') + '</value></d>') as xml))
;WITH final AS (
SELECT t.v.value('.','nvarchar(20)') as querystring
FROM #xml.nodes('/d/param') as t(v)
)
SELECT querystring, COUNT(*) as [count]
FROM final
GROUP BY querystring
Result:
querystring count
-------------------- -----------
_ 1
page 2
pagesize 1
sort 2
tab 2
(5 row(s) affected)
Necromancing.
This can now be done easily with SQL Server 2016+ (13.x+)
-- Required for STRING_SPLIT:
-- ALTER DATABASE <db_name> SET COMPATIBILITY_LEVEL = 130 -- >= 130
BEGIN TRY
DECLARE #sql nvarchar(MAX);
-- https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-database-transact-sql-compatibility-level?view=sql-server-ver15
-- SET #sql = N'ALTER DATABASE [COR_Basic_Demo_V4] SET COMPATIBILITY_LEVEL = 130; ';
SET #sql = N'ALTER DATABASE ' + QUOTENAME(DB_NAME()) + N' SET COMPATIBILITY_LEVEL = ' + (SELECT CAST(MAX(compatibility_level) AS nvarchar(10)) FROM sys.databases) + '; ';
-- PRINT #sql;
EXECUTE(#sql);
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
-- EXECUTE usp_GetErrorInfo;
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
;
END CATCH
-- Here comes the actual computation
DECLARE #input nvarchar(4000)
SET #input = N'_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15'
;WITH CTE AS
(
SELECT
value
,SUBSTRING(splitted.value, 1, NULLIF(CHARINDEX('=', splitted.value), 0) -1) AS k
,SUBSTRING(splitted.value, NULLIF(CHARINDEX('=', splitted.value), 0) + 1, LEN(splitted.value)) AS v
FROM STRING_SPLIT
(
REPLACE
(
REPLACE(#input, CHAR(13), '')
,CHAR(10)
,'&'
)
, '&'
) AS splitted
)
SELECT
k
,COUNT(v) AS cnt
,COUNT(DISTINCT v) AS dist_cnt
FROM CTE
GROUP BY k
For earlier versions, or if you actually need to decompose a full-url:
DECLARE #String nvarchar(4000)
DECLARE #path nvarchar(MAX)
DECLARE #hash nvarchar(MAX)
DECLARE #Delimiter nchar(1)
SET #String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
SET #String = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
SET #Delimiter = '&'
SELECT
#path = SUBSTRING(#String, 1, NULLIF(CHARINDEX('?', #String) - 1, -1)) -- AS path
,#hash = RIGHT(#String, LEN(#String) - NULLIF(CHARINDEX(N'#', #String), 0) ) -- AS hash
SELECT -- remove hash
#String = SUBSTRING
(
#String
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #String), 0) - 1, LEN(#String) )
) -- AS xxx
;
SELECT -- remove path
#String = SUBSTRING
( #String
,CHARINDEX(N'?', #String) + 1
,100000
)
;
;WITH Split(id, stpos, endpos, data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(#Delimiter, #String) AS endpos
,SUBSTRING(#String, 0, COALESCE(NULLIF(CHARINDEX(#Delimiter, #String), 0), LEN(#String)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(#Delimiter, #String, Split.endpos+1) AS endpos
,SUBSTRING(#String, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(#Delimiter, #String, Split.endpos+1), 0), LEN(#String)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(#String, stpos, COALESCE(NULLIF(endpos, 0), LEN(#String)+1) - stpos) AS data_simple
,data
,#path AS path
,#hash AS hash
,SUBSTRING(data, 1, NULLIF(charindex('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(charindex('=', data), 0) + 1, LEN(data)) AS v
FROM Split
And if you want a function:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tfu_DecomposeUrl]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_DecomposeUrl( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_DecomposeUrl
(
#input_string NVARCHAR(4000)
)
RETURNS TABLE
AS
RETURN
(
WITH CTE AS
(
SELECT
SUBSTRING(#input_string, 1, NULLIF(CHARINDEX('?', #input_string) - 1, -1)) AS query_path
,RIGHT(#input_string, LEN(#input_string) - NULLIF(CHARINDEX(N'#', #input_string), 0) ) AS query_hash
,SUBSTRING
(
#input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #input_string), 0) - 1, LEN(#input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
,Split(id, stpos, endpos, data, query_path, query_hash)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString) AS endpos
,SUBSTRING(CTE2.KeyValueString, 0, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString), 0), LEN(CTE2.KeyValueString)+1) ) AS data
,CTE2.query_path
,CTE2.query_hash
FROM CTE2
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1) AS endpos
,SUBSTRING(CTE2.KeyValueString, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1), 0), LEN(CTE2.KeyValueString)+1) - Split.endpos - 1) AS data
,CTE2.query_path
,CTE2.query_hash
FROM Split
CROSS JOIN CTE2
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(#String, stpos, COALESCE(NULLIF(endpos, 0), LEN(#String)+1) - stpos) AS data_simple
,data
,query_path
,query_hash
,SUBSTRING(data, 1, NULLIF(CHARINDEX('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(CHARINDEX('=', data), 0) + 1, LEN(data)) AS v
FROM Split
)
GO
Which can be simplified into this
DECLARE #input_string nvarchar(4000)
SET #input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET #input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
-- SET #input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
;WITH CTE AS
(
SELECT
SUBSTRING(#input_string, 1, NULLIF(CHARINDEX('?', #input_string) - 1, -1)) AS query_path
,RIGHT(#input_string, LEN(#input_string) - NULLIF(CHARINDEX(N'#', #input_string), 0) ) AS query_hash
,SUBSTRING
(
#input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', #input_string), 0) - 1, LEN(#input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
SELECT
t.id
,t.data
,CTE2.query_path
,CTE2.query_hash
,SUBSTRING(t.data, 1, NULLIF(CHARINDEX('=', t.data), 0) -1) AS k
,SUBSTRING(t.data, NULLIF(CHARINDEX('=', t.data), 0) + 1, LEN(t.data)) AS v
FROM CTE2
OUTER APPLY dbo.tfu_FastSplitString(CTE2.KeyValueString, N'&') AS t
And a table-valued string-splitting function:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.tfu_FastSplitString') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_FastSplitString( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_FastSplitString
(
#input_string nvarchar(4000)
,#delimiter nchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(id, stpos, endpos) -- , data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(#delimiter, #input_string) AS endpos
-- ,SUBSTRING(#input_string, 0, COALESCE(NULLIF(CHARINDEX(#delimiter, #input_string), 0), LEN(#input_string)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(#delimiter, #input_string, Split.endpos+1) AS endpos
-- ,SUBSTRING(#input_string, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(#delimiter, #input_string, Split.endpos+1), 0), LEN(#input_string)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,data
,SUBSTRING(#input_string, stpos, COALESCE(NULLIF(endpos, 0), LEN(#input_string)+1) - stpos) AS data
FROM Split
)
GO
These are inline table-valued functions, so they should be fast.
If it were a multi-statement table-valued function, it would be slow.

Hot to convert a variable with value '1,2,3' to a table (every number as a record)

Working on SQL (2005 and 2008)
the variable with value '1,2,3' would be call #cedis and this could to have N number for example
set #cedis='1' or set #cedis='1,2,3,4,5,6,7' or set #cedis='125,98,91'
so important, its this must to be a select only, a loop could not to be use, only a select!
this must to return a (result as ) table with values for example
set #cedis='1,2,3,4' this must to return a result
number 1 2 3 4
declare #cedis varchar(max)
set #cedis='1,58,123,8'
;with datos as
(
my select with is going to return me the table
)
select * from datos
result set is
number
1
58
123
8
If am not wrong this is what you need
DECLARE #cedis VARCHAR(500)='1,2,3,4'
SELECT Split.a.value('.', 'VARCHAR(100)') Numbers
FROM (SELECT Cast ('<M>' + Replace(#cedis, ',', '</M><M>') + '</M>' AS XML) AS Numbers) AS A
CROSS APPLY Numbers.nodes ('/M') AS Split(a)
Result:
Numbers
-------
1
2
3
4
A table valued function would do it.
CREATE FUNCTION [dbo].[fn_Split](#text VARCHAR(MAX), #delimiter VARCHAR(5) = ',')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value VARCHAR(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
END
SET #text = RIGHT(#text, (LEN(#text) - (#index+LEN(#delimiter)-1)))
END
RETURN
END
You can call it as follows:
SELECT *
FROM dbo.fn_Split(#cedis,',')
Here is a more generic solution that breaks any given string into a table based on any given separator:
http://rextester.com/VSRDLS48817
Not an original idea, but I've found it very useful.
create function [dbo].[SplitString]
(
#str nvarchar(255),
#separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
select
cast(1 as int),
cast(1 as int),
charindex(#separator, #str)
union all
select
p + 1,
b + 1,
charindex(#separator, #str, b + 1)
from tokens
where b > 0
)
select
p-1 ItemIndex,
substring(
#str,
a,
case when b > 0 then b-a ELSE LEN(#str) end)
AS Item
from tokens
);
This is another one approach to get required output result
DECLARE #cedis VARCHAR(MAX) ,
#delimeter VARCHAR(10)
SET #cedis = '1,58,123,8,14144,15,155231,15,3647,2347,45,76,68,2354,577,5'
SET #delimeter = ','
SET #cedis = #cedis + #delimeter;
WITH datos
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM datos
WHERE n <= LEN(#cedis)
),
cte
AS ( SELECT T.N ,
ROW_NUMBER() OVER ( ORDER BY T.N ) AS RN
FROM datos AS T
WHERE SUBSTRING(#cedis, T.N, LEN(#delimeter)) = #delimeter
AND LEN(#cedis) >= T.N
)
SELECT SUBSTRING(#cedis, COALESCE(R.N + LEN(#delimeter), 1),
L.N - COALESCE(R.N + LEN(#delimeter), 1)) AS part ,
L.RN AS ID
FROM cte AS L
LEFT JOIN cte AS R ON L.RN = R.RN + 1
OPTION ( MAXRECURSION 1000 )

SQL insert data to other table after split string

I have a table WCA:
ID TYPE ..
1 *1*3*5*
2 *1*5*
..
Now i want move data to new table WCA_TYPE:
ID WCA_ID TYPE
1 1 1
2 1 3
3 1 5
4 2 1
5 2 5
..
ID here is auto increase.
How to write sql in MS SQL server to split type from old table to multi type & insert it into new table.
DECLARE #CurID INT, #MaxID INT, #t VARCHAR(200)
SELECT #CurID = 1, #MaxID = MAX(ID) FROM WCA
WHILE #CurID <= #MaxID
BEGIN
SELECT #t = TYPE
FROM WCA
WHERE ID = #CurID
;WITH Pieces([Pos], [start], [stop]) AS (
SELECT 1, 1, CHARINDEX('*', #t)
UNION ALL
SELECT [Pos] + 1, [stop] + 1, CHARINDEX('*', #t, [stop] + 1)
FROM Pieces
WHERE [stop] > 0
)
INSERT INTO WCA_TYPE(WCA_ID, TYPE)
SELECT #CurID, T.Value
FROM
( SELECT [Pos], SUBSTRING(#t, start, CASE WHEN [stop] > 0 THEN [stop]-[start] ELSE 4000 END) AS [Value]
FROM Pieces
) T
WHERE T.Value <> ''
SET #CurID = #CurID + 1
END
SELECT *
FROM WCA_TYPE
look this fiddle
Assuming the your type column always have 3 digits you can use this.
INSERT INTO wca_type (WCA_id,type)
SELECT 1,SUBSTRING(CONVERT(nvarchar(MAX),type),1,1) FROM wca
UNION ALL
SELECT 1,SUBSTRING(CONVERT(nvarchar(MAX),type),2,1) FROM wca
UNION ALL
SELECT 1,SUBSTRING(CONVERT(nvarchar(MAX),type),3,1) FROM wca
You could use a recursive cte - e.g.
CREATE TABLE #WCA_TYPE
(ID INT IDENTITY(1, 1) PRIMARY KEY
,WCA_ID INT
,TYPE INT);
WITH sampleData(WCA_ID, TYPE) AS
(
SELECT
*
FROM ( VALUES ('1', '1*3*5')
,('2', '1*5')
) nTab(nCol1, nCol2)
)
,rep(WCA_ID, item, delim) AS
(
SELECT
WCA_ID
,TYPE item
,'*' delim
FROM sampleData
UNION ALL
SELECT
WCA_ID
,LEFT(item, CHARINDEX(delim, item, 1) - 1) item
,delim
FROM rep
WHERE (CHARINDEX(delim, item, 1) > 0)
UNION ALL
SELECT
WCA_ID
,RIGHT(item, LEN(item) - CHARINDEX(delim, item, 1)) item
,delim
FROM rep
WHERE (CHARINDEX(delim, item, 1) > 0)
)
INSERT #WCA_TYPE
(TYPE
,WCA_ID)
SELECT
item
,WCA_ID
FROM rep
WHERE (CHARINDEX(delim, item, 1) = 0)
ORDER BY WCA_ID
OPTION (MAXRECURSION 0);
SELECT * FROM #WCA_TYPE;
WITH CTE AS
(
select id,Type,0 as startPos,
CHARINDEX('*',TYPE)-1 as endPos from WCA
UNION ALL
select id,Type,endPos+2 as startPos,
CHARINDEX('*',TYPE,endPos+2)-1 as endPos from CTE
where CHARINDEX('*',TYPE,endPos+2)>0
)
INSERT INTO WCA_TYPE (WCA_ID, TYPE)
select ID,
CASE WHEN EndPos>0
THEN
Substring(Type,StartPos,EndPos-StartPos+1)
else
Type
end as Type
from CTE
where EndPos<>0
SQLFiddle Select demo