I have some fields in a db, with values of 54D, 325A, 2E and so on, letters first and numbers last.
How can I split those in a select statement, or filter it to only show the letters or numbers? I cant use udf functions for this.
I need to be able to insert 54 into another column and D into another and so on.
Im on MS SQL Server
Thanks
One way using the position of the first non-digit:
;with T(f) as (
select '325A' union
select '54D' union
select '2E' union
select '555' union
select 'Z'
)
select
f,
rtrim(left(f, patindex('%[^0-9]%', f + ' ') - 1)),
rtrim(substring(f + ' ', patindex('%[^0-9]%', f + ' '), len(f)))
from T
----
2E 2 E
325A 325 A
54D 54 D
555 555
Z Z
declare #t table(col1 varchar(20))
insert #t values('54D'),('325A'),('2E'), ('A'), ('3')
SELECT
substring(col1, 0, patindex('%[^0-9]%', col1 + 'a')),
stuff('0' + col1, 1, patindex('%[^0-9]%', col1 + 'a'), '')
FROM #t
Result:
54 D
325 A
2 E
A
3
Here is some brutal solution, but it will be able to separate mixed strings also:
DECLARE #t TABLE(ID INT, S NVARCHAR(MAX))
INSERT INTO #t VALUES (1, '123AB'), (2, '45CDEF'), (3, '1AS^&*876YU')
DECLARE #m INT
SELECT #m = MAX(LEN(S)) FROM #t
;WITH cte AS
(SELECT ID, SUBSTRING(S, 1, 1) AS S, 1 AS N, ISNUMERIC(SUBSTRING(S, 1, 1)) AS NU FROM #t
UNION ALL
SELECT t.ID, SUBSTRING(t.S, N + 1, 1) AS S, N + 1 AS N, ISNUMERIC(SUBSTRING(t.S, N + 1, 1)) AS NU FROM cte
JOIN #t t ON t.ID = cte.ID
WHERE N < #m
)
SELECT
(SELECT S FROM cte c2 WHERE c2.ID = c1.ID AND NU = 1 FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)') AS NumericPart,
(SELECT S FROM cte c2 WHERE c2.ID = c1.ID AND NU = 0 FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)') AS TextPart
FROM cte c1
WHERE S <> ''
GROUP BY ID
Output:
NumericPart TextPart
123 AB
45 CDEF
1876 AS^&*YU
Related
I have a table with 3 nvarchar columns, 1 time column, and 2 columns Pass and Fail. I need to display the data by time column. On each milestone, there will be the number of Pass and Fail. I use Pivot and its only output Pass result without Fail. I tried everything. Please help
This is the input data:
Col1 Col2 Col3 Time Pass Fail
------------------------------------
A B C 08:30 80 0
A B C 09:30 60 2
A B C 10:30 80 0
A B C 11:30 70 0
I'm using this code:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT
#cols = STUFF((SELECT ',' + QUOTENAME(Time)
FROM Your_Table
GROUP BY Time
ORDER BY Time
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT Col1,Col2,Col3,' + #cols + ' from
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
from TD_SanLuong_CN
) x
pivot
(
sum(Pass)
for Time in (' + #cols + ')
) p1
pivot
(
sum(Fail)
for Time in (' + #cols + ')
) p2'
execute(#query);
Can I not use pivot to Fail?
I need output result:
Col1 Col2 Col3 08:30_Pass 08:30_Fail 09:30_Pass 09:30_Fail ...
A B C 80 0 60 2
Please help. Thank you!
Hi have a look at this code, i think this what you exactly needed as you expected Output
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
Drop table #Temp
CREATE TABLE #Temp (
Col1 CHAR(1),
Col2 CHAR(1),
Col3 CHAR(1),
[Time] TIME(0),
Pass INT,
Fail INT
);
INSERT #Temp (Col1, Col2, Col3, [Time], Pass, Fail) VALUES
('A', 'B', 'C', '08:30', 80, 0),
('A', 'B', 'C', '09:30', 60, 2),
('A', 'B', 'C', '10:30', 80, 0),
('A', 'B', 'C', '11:30', 70, 0);
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#Dyncols AS NVARCHAR(MAX)
SELECT
#cols = STUFF((SELECT ',' + QUOTENAME(Time)
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#cols2 = STUFF((SELECT ',' + 'MAX('+QUOTENAME(Time)+')' +' As '+ '['+CAST((Time) AS VARCHAR)+'_Pass'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#cols3 = STUFF((SELECT ',' + 'MAX('+QUOTENAME(Time)+')' +' As '+ '['+CAST((Time) AS VARCHAR)+'_Fail'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT
#Dyncols = STUFF((SELECT ',' + '['+CAST((Time) AS VARCHAR)+'_Pass'+']'+','+'['+CAST((Time) AS VARCHAR)+'_Fail'+']'
FROM #Temp
GROUP BY [Time]
ORDER BY [Time]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query=';with cte
AS
(
SELECT Col1,Col2,Col3,'+#cols2+' From
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
FROM #Temp
) AS X
PIVOT
(
SUM(Pass)
FOR [Time] IN ('+#cols+' )
) p1
Group by Col1,Col2,Col3
),Cte2
AS
(
SELECT '+#cols3+' From
(
SELECT Col1,Col2,Col3,Time,Pass,Fail
from #Temp
) x
PIVOT
(
SUM(Fail)
FOR Time IN ('+#cols+')
)p1
Group by Col1,Col2,Col3
)
SELECT Col1,Col2,Col3,'+#Dyncols+' FROM cte ,Cte2'
PRINT #query
EXEC(#query)
Result
Col1 Col2 Col3 08:30:00_Pass 08:30:00_Fail 09:30:00_Pass 09:30:00_Fail 10:30:00_Pass 10:30:00_Fail 11:30:00_Pass 11:30:00_Fail
A B C 80 0 60 2 80 0 70 0
This question already has answers here:
Concatenate row values T-SQL
(15 answers)
Closed 7 years ago.
I have the following table, sorted.
ID Value Amount
1 A 10.00
2 B 4.25
3 C 2.01
4 D 5.00
How can I concatenate only consecutive pairs of rows and turn it to this:
ID Col1 Col2
1,2 A,B 10.00,4.25
3,4 C,D 2.01,5.00
And I don't want to use user-defined tables or temp tables. I am open to using
the window functions provided in SQL Server 2012 and 2014 though.
I looked at the other solutions and thought it was overkill, so I reused some and excluded or rewrote the unnecessary parts. This should result in better performance.
;WITH cte (rn, id, Value, Amount)
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY id), id, Value, Amount
FROM yourtable
)
SELECT
( SELECT CAST(T.id AS VARCHAR(10)) + ','+ CAST(T1.id AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) ID,
( SELECT CAST(T.value AS VARCHAR(10)) + ','+ CAST(T1.value AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) COL1,
( SELECT CAST(T.Amount AS VARCHAR(10)) + ','+ CAST(T1.Amount AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn + 1) COL2
FROM cte AS T
WHERE rn % 2 = 1
Try this,if it do not work with other sample data then let me know,
Declare #t table(ID varchar(50), Value varchar(50), Amount float)
insert into #t values(1,'A',10.00),(2,'B', 4.25),(3,'C',2.01)
,(4,'D',5.00 )
--Get the maxid
declare #MaxID int=(Select max(id) from #t)
;WITh CTE AS
(
SELECT ID,
STUFF((select ','+ id from #t where id IN(1,2) for xml path('')),1,1,'') [id1]
,STUFF((select ','+ Value from #t where id IN(1,2) for xml path('')),1,1,'') [Value]
,STUFF((select ','+ cast(Amount as varchar) from #t where id IN(1,2) for xml path('')),1,1,'') [Amount]
,1 RN
FROM #T a WHERE ID=1
UNION ALL
SELECT B.ID,
STUFF((select ','+ id from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
,STUFF((select ','+ Value from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
,STUFF((select ','+ cast(Amount as varchar) from #t where id IN(RN+2,RN+3) for xml path('')),1,1,'')
, RN+2
FROM cte a
CROSS APPLY(SELECT * FROM #T WHERE ID=RN+2 AND ID<=#MaxID) B
)
SELECT ID1,Value,Amount FROM CTE
This works just fine.. We just generate unique IDs for each pair using row_number and concatenate using FOR XML PATH on these IDs:
DECLARE #Test TABLE
(
ID VARCHAR(50)
, Value VARCHAR(50)
, Amount FLOAT
);
INSERT INTO #Test
(ID, Value, Amount)
VALUES
(1, 'A', 10.00)
, (2, 'B', 4.25)
, (3, 'C', 2.01)
, (4, 'D', 5.00);
;WITH cte (rn, id, Value, Amount)
AS
(
SELECT (ROW_NUMBER() OVER(ORDER BY id) + 1) / 2, id, Value, Amount
FROM #Test
)
SELECT DISTINCT
STUFF((SELECT ',' + CAST(T1.id AS VARCHAR(10))
FROM cte AS T1
WHERE T1.rn = T.rn
ORDER BY T1.id
FOR XML PATH('')), 1, 1, '') AS ID
, STUFF((SELECT ',' + CAST(T2.Value AS VARCHAR(10))
FROM cte AS T2
WHERE T2.rn = T.rn
ORDER BY T2.id
FOR XML PATH('')), 1, 1, '') AS Col1
, STUFF((SELECT ',' + CAST(T3.Amount AS VARCHAR(10))
FROM cte AS T3
WHERE T3.rn = T.rn
ORDER BY T3.id
FOR XML PATH('')), 1, 1, '') AS Col2
FROM cte AS T
Output:
ID Col1 Col2
-------------------
1,2 A,B 10,4.25
3,4 C,D 2.01,5
I would like to split a string by commas (,) or pipe (|) to each character in SQL SERVER. Example 'APPLE'. Expected result: 'A|P|P|L|E'. Preferably without creating function.
You can do it with CTE:
DECLARE #s NVARCHAR(MAX) = 'APPLE'
DECLARE #result NVARCHAR(MAX)
;WITH cte(N, S) AS
(
SELECT 1 AS N, SUBSTRING(#s, 1, 1)
UNION ALL
SELECT N + 1, SUBSTRING(#s, N + 1, 1)
FROM cte
WHERE N < LEN(#s)
)
SELECT #result = COALESCE(#result + '|', '') + S FROM cte
SELECT #result
Output:
A|P|P|L|E
Or even shorter version:
DECLARE #s NVARCHAR(MAX) = 'APPLE'
;WITH cte(N, S, D) AS
(
SELECT 1 AS N, SUBSTRING(#s, 1, 1), D = SUBSTRING(#s, 1, 1)
UNION ALL
SELECT N + 1, SUBSTRING(#s, N + 1, 1), D = D + '|' + SUBSTRING(#s, N + 1, 1)
FROM cte
WHERE N < LEN(#s)
)
SELECT TOP 1 D FROM cte
ORDER BY N DESC
You could use a concept like the "Tally Table String Splitter" to achieve what you want.
http://www.sqlservercentral.com/articles/Tally+Table/72993/
DECLARE #txt varchar(50) ='APPLE'
;WITH cte(x) as
(
SELECT top (len(#txt)) ';'
+ substring(#txt, row_number() over (order by (select 1)), 1)
FROM master..spt_values x1
cross join
master..spt_values x2
FOR XML PATH('')
)
SELECT stuff(x, 1, 1, '')
FROM CTE
Result
A;P;P;L;E
I am trying to yield a string that reads like Cat1/Cat2/Cat3, etc, from a self-referencing table.
Data looks like this:
CategoryTable
CategoryID, [Name], ParentID
1, Root, 0
2, Cat1, 1
3, Cat2, 2
4, Cat3, 3
5, Cat4, 1
6, Cat5, 5
I want to yield:
Root/Cat1/Cat2/Cat3
Root/Cat4/Cat5
How do I do this in sql server?
You can use CTE
SQL Fiddler
WITH cte AS (
SELECT CategoryID,CAST(Name AS VARCHAR(4000)) AS Name
FROM Category
WHERE ParentID = 0
UNION ALL
SELECT c.CategoryID, CAST(e.Name + '/' + c.Name AS VARCHAR(4000))
FROM cte e
INNER JOIN Category c
ON c.ParentID = e.CategoryID
)
SELECT c1.Name
FrOM cte c1
WHERE NOT EXISTS (SELECT 1 FROM cte c2 where c1.name <> c2.name AND c2.name like c1.NAME + '%')
try this :
DECLARE #str VARCHAR(255) --Or whatever length you need
SET #str = ''
SELECT #str = #str + [Name] + '/' FROM CategoryTable
SET #str = SUBSTRING(#str,0,LEN(#str)) -- remove the trailing '/'
PRINT #str
Could you explain me the strange behaviour?
DECLARE #t VARCHAR(256) = ''
SELECT #t = #t + CAST(smb.symbol AS VARCHAR(256))
FROM (
SELECT 1,'7'
UNION ALL
SELECT 2,'8'
UNION all
SELECT 3,'9'
) AS smb(n, symbol)
ORDER BY n
SELECT #t
Outputs:
789
Thats OK for me.
DECLARE #t VARCHAR(256) = ''
SELECT #t = #t + CAST(smb.symbol AS VARCHAR(256))
FROM (
SELECT NUMS.N-1 AS N, CHAR(N-1) AS symbol
FROM (
SELECT 1 + n.n1 + nn.n2 * 10 + nnn.n3 * 100 as N
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS n(n1)
CROSS JOIN (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS nn(n2)
CROSS JOIN (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS nnn(n3)
) AS NUMS
WHERE NUMS.N BETWEEN 56 AND 58
) AS smb(N, symbol)
ORDER BY smb.N
SELECT #t
Outputs:
9
So why does the second example outputs the last symbol only?
Don't rely on order by when using mutlirows variable assignment.
try this for instance:
DECLARE #c INT = 0
SELECT
#c = #c + x
FROM (VALUES(1),(2),(3)) AS src(x)
WHERE x BETWEEN 1 AND 3
ORDER BY 1 - x DESC
SELECT #c
SET #c = 0
SELECT
#c = #c + x
FROM (VALUES(1),(2),(3)) AS src(x)
WHERE x BETWEEN 1 AND 3
ORDER BY x DESC
SELECT #c
http://sqlmag.com/sql-server/multi-row-variable-assignment-and-order