Split a string with no delimiters into columns - sql

I need to split a string in a column into one character each into it's own column in SQL Server 2012.
Example: if I have a column with 'ABCDE', I need to split it into 'A', 'B', 'C', 'D', 'E', with each of these into their own columns.
The length of the column to be split may vary, so I need this to be as dynamic as possible.
My question is different from the other post (Can Mysql Split a column?) since mine doesn't have any delimiters.
Thanks

You can do this like this:
DECLARE #t TABLE(id int, n VARCHAR(50))
INSERT INTO #t VALUES
(1, 'ABCDEF'),
(2, 'EFGHIJKLMNOPQ')
;WITH cte AS
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM #t
UNION ALL
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind
)
SELECT *
FROM cte
PIVOT (MAX(c) FOR ind IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[12],[13],[14],[15])) p
Output:
id n 1 2 3 4 5 6 7 8 9 10 12 13 14 15
1 ABCDEF A B C D E F NULL NULL NULL NULL NULL NULL NULL NULL
2 EFGHIJKLMNOPQ E F G H I J K L M N P Q NULL NULL
Here is dynamic version:
DECLARE #l INT, #c VARCHAR(MAX) = ''
SELECT #l = MAX(LEN(n)) FROM PivotTable
WHILE #l > 0
BEGIN
SET #c = ',[' + CAST(#l AS VARCHAR(MAX)) + ']' + #c
SET #l = #l - 1
END
SET #c = STUFF(#c, 1, 1,'')
DECLARE #s NVARCHAR(MAX) = '
;WITH cte AS
(SELECT id, n, SUBSTRING(n, 1, 1) c, 1 AS ind FROM PivotTable
UNION ALL
SELECT id, n, SUBSTRING(n, ind + 1, 1), ind + 1 FROM cte WHERE LEN(n) > ind
)
SELECT *
FROM cte
PIVOT (MAX(c) FOR ind IN(' + #c + ')) p'
EXEC (#s)

I am interpreting the question as putting the characters into one column ("split a string in a column into one character each into it's own column"). However, I realize that this might be ambiguous.
One method is with a recursive CTE:
with chars as (
select left(val, 1) as c, substring(val, 2, len(val)) as rest
from (select 'ABCDE' as val union all select '123') t
union all
select left(rest, 1), substring(rest, 2, len(rest))
from chars
where rest <> ''
)
select c
from chars;
Just plug in your table and column in the subquery. Note that you might want to include other columns as well.
Here is a SQL Fiddle.
If you want multiple columns and the number is not fixed, then you will need
dynamic SQL.

If you want a new column for every character you simply need:
SELECT [1] = SUBSTRING(Col, 1, 1),
[2] = SUBSTRING(Col, 2, 1),
[3] = SUBSTRING(Col, 3, 1),
[4] = SUBSTRING(Col, 4, 1),
[5] = SUBSTRING(Col, 5, 1),
[6] = SUBSTRING(Col, 6, 1),
[7] = SUBSTRING(Col, 7, 1),
[8] = SUBSTRING(Col, 8, 1),
[9] = SUBSTRING(Col, 9, 1)
FROM (VALUES ('ABCDE'), ('FGHIJKLMN')) t (Col);
Which is fine, if you have a know number of columns. If you have an unknown number of columns, then you just need to generate the same SQL with n columns. To do this you will need a numbers table, and since many people do not have one, I will do a quick demo on how to dynamically generate one.
The below will generate a sequential list of numbers, 1 - 100,000,000.
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT Number
FROM Numbers;
It simply uses a table valued constructor to generate 10 rows (N1), then cross joins these 10 rows to get 100 rows (N2), then cross joins these 100 rows to get 10,000 rows (N3) and so on and so on. It finally uses ROW_NUMBER() to get the sequential numbers.
This probably needs to be cut down for this use, I hope you are not splitting a string that is 100,000,000 characters long, but the principle applies. You can just use TOP and the maximum length of your string to limit it. For each number you can just build up the necessary repetetive SQL required, which is:
,[n] = SUBSTRING(Col, n, 1)
So you have something like:
SELECT Number,
[SQL] = ',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)'
FROM Numbers;
Which gives something like:
Number SQL
-----------------------------------
1 ,[1] = SUBSTRING(Col, 1, 1)
2 ,[2] = SUBSTRING(Col, 2, 1)
3 ,[3] = SUBSTRING(Col, 3, 1)
4 ,[4] = SUBSTRING(Col, 4, 1)
The final step is to build up your final statement by concatenating all the text in the column SQL; the best way to do this is using SQL Server's XML Extensions.
So your final query might end up like:
DECLARE #SQL NVARCHAR(MAX) = '';
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (Col VARCHAR(100));
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT #SQL = 'SELECT Col' +
( SELECT TOP (SELECT MAX(LEN(Col)) FROM #T)
',[' + CAST(Number AS VARCHAR(10)) + '] = SUBSTRING(Col, ' + CAST(Number AS VARCHAR(10)) + ', 1)'
FROM Numbers
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)') + '
FROM #T;';
EXECUTE sp_executesql #SQL;
Which gives:
Col 1 2 3 4 5 6 7 8 9
-------------------------------------------------
ABCDE A B C D E
FGHIJKLMN F G H I J K L M N
Finally, if you actually wanted to split it into rows, I would still use the same approach, with your adhoc numbers table, just join it to your original table:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T (Col VARCHAR(100));
INSERT #T (Col) VALUES ('ABCDE'), ('FGHIJKLMN');
WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (N)),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
Numbers (Number) AS (SELECT TOP (SELECT MAX(LEN(Col)) FROM #T) ROW_NUMBER() OVER(ORDER BY N1.N) FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT t.Col,
Position = n.Number,
Character = SUBSTRING(t.Col, n.Number, 1)
FROM #T AS t
INNER JOIN Numbers AS n
ON n.Number <= LEN(t.Col)
ORDER BY t.Col, n.Number;
Which gives something like:
Col Position Character
-------------------------------
ABCDE 1 A
ABCDE 2 B
ABCDE 3 C
ABCDE 4 D
ABCDE 5 E

One way
declare #str varchar(max) = 'ABCDE'
declare #sql nvarchar(max) = ''
declare #i int = 1
while (#i <= len(#str)) begin
set #sql += case when #i > 1 then ',' else '' end + '''' + substring(#str, #i, 1) + ''''
set #i += 1
end
exec('select ' + #sql)
(If ' can appear as a char you would need to substitute '')

This is a solution for a dynamic text length.
-- Generate demo data
CREATE TABLE #temp(col nvarchar(100))
INSERT INTO #temp(col)
VALUES(N'A'),(N'ABC'),(N'DEFGHI'),(N'AB'),(N'KLOMA')
-- Split all in multiple rows
CREATE TABLE #output (col nvarchar(100),part nchar(1), pos int)
;WITH cte AS(
SELECT col, LEFT(col, 1) as part, 1 as pos
FROM #temp
UNION ALL
SELECT col, SUBSTRING(col, pos+1,1) as part, pos+1 as part
FROM cte
WHERE LEN(col) > pos
)
INSERT INTO #output(col, part, pos)
SELECT col, part, pos
FROM cte
DECLARE #sql nvarchar(max), #columnlist nvarchar(max)
-- Generate Columlist for dynamic pivot
SELECT #columnlist = COALESCE(#columnlist + N',[' + CONVERT(nvarchar(max),pos) + ']', N'[' + CONVERT(nvarchar(max),pos) + ']')
FROM #output o
WHERE o.col = (SELECT TOP (1) col FROM #output ORDER BY LEN(col) DESC)
-- Pivoting for readability
SET #sql = N'
SELECT pvt.*
FROM #output o
PIVOT (
MAX(o.part)
FOR pos IN('+#columnlist+')
) as pvt'
EXEC (#sql)
-- Cleanup
DROP TABLE #temp
DROP TABLE #output
The keypart is the cte and the pivoting afterwards. If you have any questions, just give me a short feedback.

Related

Reverse order of elements in a string

I have the following string:
1119/2/483/11021
I would like to reverse the order of the elements in that string. Desired output:
11021/483/2/1119
T-SQL Version 2014
You need an ordered split function, e.g. (inspiration):
CREATE FUNCTION dbo.SplitOrdered
(
#list nvarchar(max),
#delim nvarchar(10)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH w(n) AS (SELECT 0 FROM (VALUES (0),(0),(0),(0)) w(n)),
k(n) AS (SELECT 0 FROM w a, w b),
r(n) AS (SELECT 0 FROM k a, k b, k c, k d, k e, k f, k g, k h),
p(n) AS (SELECT TOP (COALESCE(LEN(#list), 0))
ROW_NUMBER() OVER (ORDER BY ##SPID) -1 FROM r),
spots(p) AS
(
SELECT n FROM p
WHERE (SUBSTRING(#list, n, LEN(#delim + 'x') - 1) LIKE #delim OR n = 0)
),
parts(p,val) AS
(
SELECT p, SUBSTRING(#list, p + LEN(#delim + 'x') - 1,
LEAD(p, 1, 2147483647) OVER (ORDER BY p) - p - LEN(#delim))
FROM spots AS s
)
SELECT listpos = ROW_NUMBER() OVER (ORDER BY p),
Item = LTRIM(RTRIM(val))
FROM parts
);
Then you can reassemble using STRING_AGG() (if SQL Server 2017 or better) or FOR XML PATH on lower versions:
SQL Server 2017 +
DECLARE #OriginalString nvarchar(255) = N'1119/2/483/11021';
SELECT NewString = STRING_AGG(o.Item, N'/')
WITHIN GROUP (ORDER BY listpos DESC)
FROM dbo.SplitOrdered(#OriginalString, N'/') AS o;
SQL Server < 2017
DECLARE #OriginalString nvarchar(255) = N'1119/2/483/11021';
SELECT NewString = STUFF(
(SELECT N'/' + o.Item
FROM dbo.SplitOrdered(#OriginalString, N'/') AS o
ORDER BY o.listpos DESC
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'),1,1,N'');
Example db<>fiddle
Please try the following solution based on the built-in PARSENAME() T-SQL function.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, Tokens VARCHAR(MAX));
INSERT INTO #tbl (Tokens) VALUES
('1119/2/483/11021'),
('1120/25/484/1102');
-- DDL and sample data population, end
SELECT tbl.*
, PARSENAME(c, 1) + '/' +
PARSENAME(c, 2) + '/' +
PARSENAME(c, 3) + '/' +
PARSENAME(c, 4) AS Result
FROM #tbl AS tbl
CROSS APPLY (VALUES (REPLACE(Tokens, '/', '.') )) AS t(c);
Output
+----+------------------+------------------+
| ID | Tokens | Result |
+----+------------------+------------------+
| 1 | 1119/2/483/11021 | 11021/483/2/1119 |
| 2 | 1120/25/484/1102 | 1102/484/25/1120 |
+----+------------------+------------------+
First, split the string and convert it into a column then order by desc and display into multiple row values into a single row. In the following code, you can set any string and split char.
Try following way.
DECLARE #S varchar(max) ,
#Split char(1),
#X xml
DECLARE #Names VARCHAR(8000)
SELECT #S = '1119/2/483/11021',
#Split = '/'
SELECT #X = CONVERT(xml,' <root> <myvalue>' +
REPLACE(#S,#Split,'</myvalue> <myvalue>') + '</myvalue> </root> ')
select #Names = COALESCE(#Names + '/', '') + Value from (
select rowno,Value from (
select ROW_NUMBER() OVER(ORDER BY d) AS rowno , Value from (
SELECT T.c.value('.','varchar(20)') as Value,0 as d
FROM #X.nodes('/root/myvalue') T(c)
) m
) r
) t order by t.rowno desc
select #Names as ReverseString
Splitting the string into sub-strings, and then joining them back up, is most likely going to be a good approach.
Some comments mention using string-reverse, but that doesnt seem to be a good approach at all in your case, since you just want to reverse the order of words within the current string, not actually reverse the entire text-string character-by-character.
PS: string_split does not guarantee the order of the chunks!

List combination of numbers which have same sum

**N** is a prositive number
Need list of scenarios which have sum equal N
For example if N=4
ScenarioId Value
---------- -----
1 1
1 1
1 1
1 1
2 2
2 1
2 1
3 2
3 2
4 3
4 1
5 4
above list is required. If you sum by ScenarioId all sum must equal to N
UPDATE
Here is my own solution. however, I am not sure about the multiplication of two different number sets would not be equal at any time.
My current question is
Is there any possibilities a + b + c = d + e + f and a * b * c = d * e * f
Test link is here
DECLARE #N int = 4;
SELECT
[Value] = CAST(number + 1 as tinyint)
INTO #Values
FROM master.dbo.spt_values
WHERE number < #N
AND [Type] = 'p'
;WITH COMBINATIONS AS(
SELECT ScenarioKey = CAST(NULL AS nvarchar(MAX)), [Value], Total = 0, Multipication = 1, MemeberCount = 0
FROM #Values
UNION ALL
SELECT ScenarioKey = ISNULL(S.ScenarioKey, '') + IIF(S.ScenarioKey IS NULL, '', N'-') + CAST(P.[Value] AS nvarchar(10)), S.[Value], Total = S.Total + P.[Value], Multipication = S.Multipication * P.[Value], MemeberCount = MemeberCount + 1
FROM #Values P
JOIN COMBINATIONS AS S ON S.Total < S.[Value]
),
SCENARIOS AS(
SELECT
ScenarioKey
,ScenarioId = ROW_NUMBER() OVER(ORDER BY ScenarioKey)
,[Value]
FROM
(
SELECT
ScenarioKey
,[Value]
,Multipication
,MemeberCount
-- this will prevent dublications. because 1 * 2 * 3 = 3 * 2 * 1
-- however, I am not sure about multipication of two different number sets would not be equal any time
,RowNo = ROW_NUMBER() OVER(PARTITION BY [Value],Multipication,MemeberCount ORDER BY [Value],ScenarioKey)
FROM COMBINATIONS
WHERE Total = #N
) X
WHERE RowNo = 1 AND [Value] = #N
)
SELECT
R.ScenarioId
,[Value] = S.[value]
FROM SCENARIOS R
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(R.ScenarioKey, '-')) S
DROP TABLE #Values
It's too long for comment, so I post this as an answer. I want to note, that this is a static example, but I hope it can be easily translated as a dynamic statement.
Steps are written as comments in the statement:
WITH rcte AS
(
-- Recursive query to generate all numbers from 1 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), permutations AS (
-- All possible permutations with sum equal to 4
-- There is additional column DuplicateMarker.
-- It will be used later, because 0,0,0,4 and 0,4,0,0 are the same
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
CONCAT(LTRIM(STR(t1.Number)), '.', LTRIM(STR(t2.Number)), '.', LTRIM(STR(t3.Number)), '.', LTRIM(STR(t4.Number))) AS DuplicateMarker
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE (t1.Number + t2.Number + t3.Number + t4.Number) = 4
), duplicates AS (
-- Get data with splitted DuplicateMarker column
SELECT *
FROM permutations
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(DuplicateMarker, '.')) t
), results AS (
-- Get unique combinations
-- WITHIN GROUP (ORDER BY) will order strings and 0.0.0.4 and 0.4.0.0 will be the same
SELECT DISTINCT STRING_AGG([value], '.') WITHIN GROUP (ORDER BY [value]) AS ScenarioValue
FROM duplicates
GROUP BY Number1, Number2, Number3, Number4
)
SELECT
DENSE_RANK() OVER (ORDER BY r.ScenarioValue) AS ScenarioID,
s.[value]
FROM results r
CROSS APPLY (SELECT [value] FROM STRING_SPLIT(r.ScenarioValue, '.')) s
WHERE [value] <> '0'
Output:
ScenarioID value
1 4
2 1
2 3
3 2
3 2
4 1
4 1
4 2
5 1
5 1
5 1
5 1
Update:
Thanks to #AndriyM's comment, I've made some changes and now you can eliminate string manipulations:
WITH rcte AS
(
-- Recursive query to generate all numbers from 0 to 4
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < 4
), combinations AS (
-- All different combinations with sum equal to 4
SELECT
t1.Number AS Number1,
t2.Number AS Number2,
t3.Number AS Number3,
t4.Number AS Number4,
ROW_NUMBER() OVER (ORDER BY t1.Number, t2.Number, t3.Number, t4.NUmber) AS ScenarioID
FROM rcte t1, rcte t2, rcte t3, rcte t4
WHERE
((t1.Number + t2.Number + t3.Number + t4.Number) = 4) AND
(t1.Number <= t2.Number) AND
(t2.Number <= t3.Number) AND
(t3.Number <= t4.Number)
)
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES (c.NUmber1), (c.Number2), (c.Number3), (c.Number4)) AS v ([value])
WHERE v.[value] > 0
Update 2:
Approach using dynamic statement - probably not the best approach, but is based on statement from first update:
-- Set your #n value
DECLARE #n int
SET #n = 4
-- Declarations
DECLARE #combinationsSelect nvarchar(max)
DECLARE #combinationsRowNumber nvarchar(max)
DECLARE #combinationsFrom nvarchar(max)
DECLARE #combinationsWhere1 nvarchar(max)
DECLARE #combinationsWhere2 nvarchar(max)
DECLARE #combinationsValues nvarchar(max)
SET #combinationsSelect = N''
SET #combinationsRowNumber = N''
SET #combinationsFrom = N''
SET #combinationsValues = N''
SET #combinationsWhere1 = N''
SET #combinationsWhere2 = N''
-- Generate dynamic parts of the statement
;WITH numbers AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number + 1
FROM Numbers
WHERE Number < #n
)
SELECT
#combinationsSelect = #combinationsSelect + N', t' + LTRIM(STR(Number)) + N'.Number AS Number' + LTRIM(STR(Number)),
#combinationsRowNumber = #combinationsRowNumber + N', t' + LTRIM(STR(Number)) + N'.Number',
#combinationsValues = #combinationsValues + N', (c.Number' + LTRIM(STR(Number)) + N')',
#combinationsFrom = #combinationsFrom + N', rcte t' + LTRIM(STR(Number)),
#combinationsWhere1 = #combinationsWhere1 + N'+ t' + LTRIM(STR(Number)) + N'.Number ',
#combinationsWhere2 = #combinationsWhere2 +
CASE
WHEN Number = 1 THEN N''
ELSE N'AND (t' + LTRIM(STR(Number-1)) + N'.Number <= t' + + LTRIM(STR(Number)) + N'.Number) '
END
FROM
numbers
SET #combinationsSelect = STUFF(#combinationsSelect, 1, 2, N'')
SET #combinationsRowNumber = STUFF(#combinationsRowNumber, 1, 2, N'')
SET #combinationsValues = STUFF(#combinationsValues, 1, 2, N'')
SET #combinationsFrom = STUFF(#combinationsFrom, 1, 2, N'')
SET #combinationsWhere1 = STUFF(#combinationsWhere1, 1, 2, N'')
SET #combinationsWhere2 = STUFF(#combinationsWhere2, 1, 4, N'')
-- Dynamic statement
DECLARE #stm nvarchar(max)
SET #stm =
N'WITH rcte AS (
SELECT 0 AS Number
UNION ALL
SELECT Number + 1
FROM rcte
WHERE Number < ' + LTRIM(STR(#n)) +
N'), combinations AS (
SELECT ' +
#combinationsSelect +
N', ROW_NUMBER() OVER (ORDER BY ' + #combinationsRowNumber + N') AS ScenarioID
FROM ' + #combinationsFrom +
N' WHERE ((' + #combinationsWhere1 + N') = ' + LTRIM(STR(#n)) + ') AND ' + #combinationsWhere2 +
N')
SELECT c.ScenarioID, v.[value]
FROM combinations c
CROSS APPLY (VALUES ' + #combinationsValues + N') AS v ([value])
WHERE v.[value] > 0'
-- Execute dynamic statement
EXEC (#stm)
If you have sample data like below
You can write query like below
Declare #N int =4
Select T.*
From #T T
cross apply (
select S, SUM(V) Total
From #T
Group By S) Totals
Where Totals.Total=#N and T.S = Totals.S

Replace the even characters to upper case and the remaining characters to lower case

Is there an SQL query to replace the even characters to upper case and the remaining characters to lower case in a string?
For example if the string is 'sagar' the result should be like
sAgAr
What would be the appropriate solution for this?
I can't resist answering. This seems like such a natural for a recursive CTE:
with t as (
select 'abcdef' as str
),
cte as (
select cast(lower(str) as varchar(max)) as str, 1 as pos
from t
union all
select stuff(str, pos + 1, 1,
(case when pos % 2 = 1 then upper(substring(str, pos + 1, 1))
else lower(substring(str, pos + 1, 1))
end)
) as str, 1 + pos
from cte
where pos < len(str)
)
select top (1) *
from cte
order by pos desc;
Written the below code and it works fine
Tested on Master DB
declare #name nvarchar(50)
declare #i int
set #i=1
set #name='sagar'
while(#i<=LEN(#name))
begin
if(#i%2=0)
begin
print Upper(SUBSTRING(#name,#i,1))
set #i=#i+1
end
else
begin
print Lower(SUBSTRING(#name,#i,1))
set #i=#i+1
end
end
Give the name of your own choice while setting the #name parameter and you can get the required result
Using a tally table...
declare #table table ([name] varchar(64))
insert into #table
values
('sAgAr')
,('abcdefghijk')
,('LMNOPQ')
;WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
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
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select
t.[name]
,lower(left(t.[name],1))
+
STUFF((
SELECT '' + case
when c2.N%2 = 0 then upper(substring(t2.[name],c2.N,1))
else lower(substring(t2.[name],c2.N,1))
end
FROM #table t2
cross apply cteTally c2
where
len(t2.[name]) >= c2.N
and t2.name = t.name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from
#table t
This is by splitting into rows and recreating strings again:
declare #test table ([value] nvarchar(20))
insert into #test values ('sagar'), ('Blueprint'), ('turtLe')
;with cte as (
select [value]
, num
, iif(num % 2 = 0, upper(substring([value], num, 1)), lower(substring([value], num, 1))) as [char]
from #test
cross join (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) numbers(num) --add more for > 12 characters
where num <= len([Value]))
select distinct [Value], [CaseApplied] = STUFF(( SELECT '' + [char]
FROM cte AS c
WHERE c.[value]= cte.value
FOR XML PATH('')
), 1, 0, '')
from cte
Here's one way:
DECLARE #mystringLOW varchar(100) = 'sagar'
,#pos int = 2
WHILE #pos <= LEN(#mystringLOW)
BEGIN
SET #mystringLOW = (SELECT STUFF(#mystringLOW, #pos, 1, UPPER(SUBSTRING(#mystringLOW, #pos, 1))))
SET #pos += 2
END
SELECT #mystringLOW AS [my answer]
Produces:
my answer
---------
sAgAr

SQL Select Concat between 2 numbers

I work with SQL Server 2012 and need a concatenate between 2 different columns.
eg:
3 and 7 = 34567
or 1 and 4 = 1234
or 2 and 2 = 2
When I use the Concat Function, I am just able to Concate the first and the last number. But I need the numbers between, too.
Try this query. Here firstcolumn =3 and secondcolumn=7
SELECT t.Id,
,STUFF((SELECT '' + CAST( n AS VARCHAR(50)) [text()]
FROM (SELECT DISTINCT n = number
FROM master..[spt_values]
WHERE number >= firstcolumn AND number <= secondcolumn
)a
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,0,'') List_Output
FROM tablename t
There are multiple ways to generate sequences in sql-server. Here is a simple that doesn't need a number-table:
WITH Numbers AS
(
SELECT TOP (2000) n = ROW_NUMBER() OVER (ORDER BY object_id)
FROM sys.all_objects ORDER BY n
)
SELECT n FROM Numbers
WHERE n BETWEEN 3 AND 7
Here's a recursive query that will go from start to end recursively and generate the string you want or an INTEGER value:
DECLARE #start INT = 3
DECLARE #end INT = 7
DECLARE #int_value INT = 0
DECLARE #str_value VARCHAR(100) = '';
WITH rec AS (
SELECT #start AS val
UNION ALL
SELECT val + 1
FROM rec
WHERE val < #end
)
SELECT #str_value = CONCAT(#str_value, val),
#int_value = #int_value * 10 + val
FROM rec
SELECT #str_value, #int_value
This is Itzik's style
declare #values varchar(100)='', #from int, #to int
select #from=3, #to=7
;WITH
n0 AS (SELECT 0 AS number UNION ALL SELECT 0),
n1 AS (SELECT 0 AS number FROM n0 AS a CROSS JOIN n0 AS b),
n2 AS (SELECT 0 AS number FROM n1 AS a CROSS JOIN n1 AS b),
n3 AS (SELECT 0 AS number FROM n2 AS a CROSS JOIN n2 AS b)
select #values=#values+ltrim(sno) from
(select row_number() over (order by number) as sno from n3) as t
where sno between #from and #to
select #values as [values]
Thank you for the Answer.
I am going to use the answer from #Mukesh Kalgude.
So, my full query is the follow:
select
DayFrom,DayTo,
STUFF((SELECT TOP 7'' + CAST( n AS VARCHAR(50)) [text()]
FROM (SELECT DISTINCT n = number
FROM master..[spt_values]
WHERE number >= DayFrom AND DayTo <= 7
)a
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,0,'') List_Output
from SwitchProfilePairs
The result is
dayFrom = 1 day To = 1 But the List_Output is 1234567
Try this using SUBSTRING() function(Fiddle example):
--Declare sample table
DECLARE #T TABLE (id int identity, numCol1 int, numCol2 int)
--Add some values
INSERT #T (numCol1, numCol2)
VALUES (3, 7), (1, 4), (2, 2)
--Actual Query
SELECT *, SUBSTRING('123456789', numCol1, numCol2 - numCol1 + 1) Number
FROM #T
Above query works only with single digit numbers. Modified version (below) to work with numbers like 34, 78
SELECT *,
SUBSTRING('123456789', CONVERT(int, LEFT(numCol1,1)),
CONVERT(int, RIGHT(numCol2, 1)) - convert(int, LEFT(numCol1,1)) + 1) YourNumber
FROM #T
Note: Number column is returning a string, can be converted to an int using convert() function

Split string and divide value

I got a table Test with columns A and B.
The A column contains different values in one entry, e.g. abc;def;ghi, all separated by ;. And the B column contains numeric values, but only one.
What I want is to seperate the values from column A into multiple rows.
So:
abc;def;ghi;jkl
-->
abc
def
ghi
jkl
In column B is one value, e.g. 20 and I want that value split to the amount of rows,
So the final result shut be:
abc 5
def 5
ghi 5
jkl 5
The issue is that the amount of values in column A must be variable.
First you need to create this function
REATE FUNCTION Split
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max),
origVal nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val,origval)
select
r.value('.','varchar(max)') as item, #delimited
from #xml.nodes('//root/r') as records(r)
RETURN
END
GO
then this query might help
Select x.Val, test.B / (len(test.A) - len(replace(Test.A, ';', '')) + 1) from Test
inner join dbo.Split(Test.A,';') x on x.origVal = Test.A
this part len(test.A) - len(replace(Test.A, ';', '')) will count the number of ; in string
Be aware this query might have some malfunctioning if there will be duplicate strings in A column, in this situation you need to pass the unique value (for example ID) to split function and return it in the result table, then join it by this value (ie. x.origVal = Test.A => x.origID = Test.ID)
You can use some tricks with CTE, STUFF and windows functions
DECLARE #t TABLE
(
ID INT ,
A NVARCHAR(MAX) ,
B INT
)
INSERT INTO #t
VALUES ( 1, 'a;b;c;d;', 20 ),
( 2, 'x;y;z;', 40 );
WITH cte ( ID, B, D, A )
AS ( SELECT ID ,
B ,
LEFT(A, CHARINDEX(';', A + ';') - 1) ,
STUFF(A, 1, CHARINDEX(';', A + ';'), '')
FROM #t
UNION ALL
SELECT ID ,
B ,
LEFT(A, CHARINDEX(';', A + ';') - 1) ,
STUFF(A, 1, CHARINDEX(';', A + ';'), '')
FROM cte
WHERE A > ''
)
SELECT ID ,
B ,
D,
CAST(B AS DECIMAL) / COUNT(*) OVER (PARTITION BY ID) AS Portion
FROM cte
Output:
ID B D Portion
1 20 a 5.00000000000
1 20 b 5.00000000000
1 20 c 5.00000000000
1 20 d 5.00000000000
2 40 x 13.33333333333
2 40 y 13.33333333333
2 40 z 13.33333333333
this an example how you can achieve required result
DECLARE #table AS TABLE
(
ColumnA VARCHAR(100) ,
ColumnB FLOAT
)
INSERT INTO #table
( ColumnA, ColumnB )
VALUES ( 'abc;def;ghi;jkl', 20 ),
( 'asf;ret;gsd;jas', 30 ),
( 'dfa;aef;gffhi;fjfkl', 40 );
WITH C AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM C
WHERE n <= 100
),
SetForSplit
AS ( SELECT T.ColumnA ,
T.ColumnB ,
C.n ,
( CASE WHEN LEFT(SUBSTRING(T.ColumnA, n, 100), 1) = ';'
THEN SUBSTRING(T.ColumnA, n + 1, 100) + ';'
ELSE SUBSTRING(T.ColumnA, n, 100) + ';'
END ) AS SomeText
FROM #table AS T
JOIN C ON C.n <= LEN(T.ColumnA)
WHERE SUBSTRING(T.ColumnA, n, 1) = ';'
OR n = 1
)
SELECT ROW_NUMBER() OVER ( PARTITION BY columnA ORDER BY LEFT(SomeText,
CHARINDEX(';',
SomeText) - 1) ) AS RowN,
LEFT(SomeText, CHARINDEX(';', SomeText) - 1) AS ColA ,
ColumnB / COUNT(*) OVER ( PARTITION BY ColumnA ) AS ColB
FROM SetForSplit
ORDER BY ColumnA
This is full working exmaple:
DECLARE #DataSource TABLE
(
[A] VARCHAR(MAX)
,[B] INT
);
INSERT INTO #DataSource ([A], [B])
VALUES ('a;b;c;d', 20 ),
('x;y;z', 40 );
SELECT T.c.value('.', 'VARCHAR(100)')
,[B] / COUNT([B]) OVER (PARTITION BY [B])
FROM #DataSource
CROSS APPLY
(
SELECT CONVERT(XML, '<t>' + REPLACE([A], ';', '</t><t>') + '</t>')
) DS([Bxml])
CROSS APPLY [Bxml].nodes('/t') AS T(c)
and of couse you can ROUND the devision as you like.