I have a table named TableA which has many rows. Sample structure given below
CID int, Col1 int, Col2 int, Col3 int, Col4 int
when I run a query (say when CID=5) i will get only one row with Col1,Col2 etc having different values.
I want to get the digit of column name where the row value is -1.
For more clarity
CID, Col1, Col2 , Col3 , Col4
5 0 -1 0 -1
in this example i should get result as
MyRes
2
4
Is there a any way to achieve it
You may use unpivot then filter your record on basis of result value which is -1 in your case.
; with cte as (
select CID, result, col from
(
select * from table
) as t
unpivot
(
result for col in ( Col1, col2, col3, col4 )
) as p
)
select CId, col from cte where result = -1
Just a bit of homework for you to get the number part from the column name.
In case you find any problem in that part please comment I'll do that also but lets give a try first.
I got the answer
declare #ID int
set #ID = 1
select substring( T1.ColName, 2, LEN(T1.ColName)) as MyRes from (
select
Col.value('local-name(.)', 'varchar(10)') as ColName
from (select *
from TableA
for xml path(''), type) as T(XMLCol)
cross apply
T.XMLCol.nodes('*') as n(Col)
where Col.value('.', 'varchar(10)') = '-1'
)T1
Related
I have a very particular problem at hand.
Brief introduction: I have two columns at a database that I need to "group concatenate", in MySQL I would simply use GROUP_CONCAT to get the desired result, in SQL Server 2017 and on I would use STRING_AGG, the problem that I have is in the SQL Server 2012, which doesn't have this function.
Now, under normal circumstances I would use FOR XML PATH('') to get the solution, this is not viable since I'm running the query from the editor inside a third source application, the error that I get is
FOR XML PATH('') can't be used inside a cursor
For the sake of the argument let's assume that it's completely out of question to use this function.
I have tried using recursive CTE, however, it's not viable due to execution time, UNION ALL takes too much resources and can't execute properly (I am using the data for reporting).
I will no post the screenshots of the data due to the sensitivity of the same, imagine just having two columns, one with an id (multiple same id's), and a column with the data that needs to be concatenated (some string). The goal is to concatenate the second columns for all of the same id's in the first columns, obviously make it distinct in the process.
Example:
Input:
col1 col2
1 a
1 b
2 a
3 c
Output:
col1 col2
1 a/b
2 a
3 c
Does anyone have a creative idea on how to do this?
If you know the maximum number of values that need to be concatenated together, you can use conditional aggregation:
select col1,
stuff( concat(max(case when seqnum = 1 then '/' + col2 end),
max(case when seqnum = 2 then '/' + col2 end),
max(case when seqnum = 3 then '/' + col2 end),
max(case when seqnum = 4 then '/' + col2 end),
max(case when seqnum = 5 then '/' + col2 end)
), 1, 1, ''
) as col2s
from (select t.*,
row_number() over (partition by col1 order by col2) as seqnum
from t
) t
group by col1;
You can get the maximum number using:
select top (1) count(*)
from t
group by col1;
Your sample output seems wrong as 'a/b' should come for value 2.
try the following:
declare #t table (col1 int, col2 varchar(100))
insert into #t select 1, 'a'
insert into #t select 2, 'b'
insert into #t select 2, 'a'
insert into #t select 3, 'c'
declare #final_table table (col1 int, col2 varchar(100), col2_all varchar(1000))
insert into #final_table (col1, col2)
select * from #t
declare #col2_all varchar(1000)
declare #Name sysname
update #final_table
SET #col2_all = col2_all = COALESCE(CASE COALESCE(#Name, '')
WHEN col1 THEN #col2_all + '/' + col2
ELSE col2 END, ''),
#Name = col1;
select col1, col2_grouped = MAX(col2_all)
from #final_table
group by col1
Using CTE:
;with cte(col1,col2_grouped,rn)
as
(
select col1, col2 , rn=ROW_NUMBER() over (PARTITION by col1 order by col1)
from #t
)
,cte2(col1,final_grouped,rn)
as
(
select col1, convert(varchar(max),col2_grouped), 1 from cte where rn=1
union all
select cte2.col1, convert(varchar(max),cte2.final_grouped+'/'+cte.col2_grouped), cte2.rn+1
from cte2
inner join cte on cte.col1 = cte2.col1 and cte.rn=cte2.rn+1
)
select col1, MAX(final_grouped) col2_grouped from cte2 group by col1
Please see db<>fiddle here.
I am looking to split a string into words in columns in SQL Server 2014. I have found a few solutions but all of them are giving the results in rows. How can I break the below string into columns?
"First Second Third Fourth Fifth"
You can use XML and grab the elements by their position:
DECLARE #YourString VARCHAR(100)='First Second Third Fourth Fifth';
WITH StringAsXML AS
(
SELECT CAST('<x>' + REPLACE((SELECT #YourString AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML) TheXml
)
SELECT TheXml.value('x[1]/text()[1]','nvarchar(max)') AS FirstElement
,TheXml.value('x[2]/text()[1]','nvarchar(max)') AS SecondElement
,TheXml.value('x[3]/text()[1]','nvarchar(max)') AS ThirdElement
,TheXml.value('x[4]/text()[1]','nvarchar(max)') AS FourthElement
,TheXml.value('x[5]/text()[1]','nvarchar(max)') AS FifthElement
FROM StringAsXML;
Remark
You can use PIVOT, conditional aggregation, FROM(VALUES()) or the above. but any of these approaches will need a known set of columns (a known count of elements or at least a maximum count of elements).
If you cannot rely on such a knowledge, you can use dynamically created SQL. This would mean to create one of the working statements on string base and use EXEC for a dynamic execution.
UPDATE: A dynamic approach
This approach will deal with a variable number of elements
DECLARE #YourString VARCHAR(100)='First Second Third Fourth Fifth';
DECLARE #Delimiter CHAR(1)=' ';
DECLARE #countElements INT = LEN(#YourString)-LEN(REPLACE(#YourString,#Delimiter,''));
DECLARE #Statement VARCHAR(MAX)=
'WITH StringAsXML AS
(
SELECT CAST(''<x>'' + REPLACE((SELECT ''ReplaceYourString'' AS [*] FOR XML PATH('''')),'' '',''</x><x>'') + ''</x>'' AS XML) TheXml
)
SELECT ReplaceColumnList
FROM StringAsXML;';
DECLARE #columnList VARCHAR(MAX);
WITH cte AS
(
SELECT 1 AS ElementCounter
,CAST('TheXml.value(''x[1]/text()[1]'',''nvarchar(max)'') AS Element_01' AS VARCHAR(MAX)) AS ColStatement
UNION ALL
SELECT cte.ElementCounter+1
,cte.ColStatement + CAST(',TheXml.value(''x[' + CAST(cte.ElementCounter+1 AS VARCHAR(10)) + ']/text()[1]'',''nvarchar(max)'') AS Element_' + REPLACE(STR(cte.ElementCounter + 1,2),' ','0') AS VARCHAR(MAX))
FROM cte
WHERE cte.ElementCounter <= #countElements
)
SELECT #columnList=(SELECT TOP 1 cte.ColStatement FROM cte ORDER BY cte.ElementCounter DESC)
--replace the string you want to split
SET #Statement = REPLACE(#Statement,'ReplaceYourString',#YourString);
--replace the columnList
SET #Statement = REPLACE(#Statement,'ReplaceColumnList',#columnList);
EXEC(#Statement);
UPDATE 2: The smallest fully inlined and position-safe splitter I know of
Try this out:
DECLARE #inp VARCHAR(200) = 'First Second Third Fourth Fifth';
DECLARE #dlmt VARCHAR(100)=' ';
;WITH
a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(#dlmt, #inp, j+1) FROM a WHERE j > i),
b AS (SELECT n, SUBSTRING(#inp, i+1, IIF(j>0, j, LEN(#inp)+1)-i-1) s FROM a WHERE i >= 0)
SELECT * FROM b;
And just to get it complete: The above tiny splitter combined with PIVOT:
;WITH
a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(#dlmt, #inp, j+1) FROM a WHERE j > i),
b AS (SELECT n, SUBSTRING(#inp, i+1, IIF(j>0, j, LEN(#inp)+1)-i-1) s FROM a WHERE i >= 0)
SELECT p.*
FROM b
PIVOT(MAX(s) FOR n IN([1],[2],[3],[4],[5])) p;
You can use a SQL split string function to seperate the string into words and using the order of the word in the original string, you can use CASE statements like a PIVOT query and display as columns
Here is a sample
declare #string varchar(max) = 'First Second Third Fourth Fifth'
;with cte as (
select
case when id = 1 then val end as Col1,
case when id = 2 then val end as Col2,
case when id = 3 then val end as Col3,
case when id = 4 then val end as Col4,
case when id = 5 then val end as Col5
from dbo.split( #string,' ')
)
select
max(Col1) as Col1,
max(Col2) as Col2,
max(Col3) as Col3,
max(Col4) as Col4,
max(Col5) as Col5
from cte
If you cannot create a UDF, you can use the logic in your SQL code as follows
Please note that if you have your data in a database table column, you can simply replace column content in the first SQL CTE expression
declare #string varchar(max) = 'First Second Third Fourth Fifth'
;with cte1 as (
select convert(xml, N'<root><r>' + replace(#string,' ','</r><r>') + '</r></root>') as rawdata
), cte2 as (
select
ROW_NUMBER() over (order by getdate()) as id,
r.value('.','varchar(max)') as val
from cte1
cross apply rawdata.nodes('//root/r') as records(r)
)
select
max(Col1) as Col1,
max(Col2) as Col2,
max(Col3) as Col3,
max(Col4) as Col4,
max(Col5) as Col5
from (
select
case when id = 1 then val end as Col1,
case when id = 2 then val end as Col2,
case when id = 3 then val end as Col3,
case when id = 4 then val end as Col4,
case when id = 5 then val end as Col5
from cte2
) t
You may use parsename function as :
create table tab ( str varchar(100));
insert into tab values('First Second Third Fourth Fifth');
with t as
(
select replace(str,' ','.') as str
from tab
)
Select substring(str,1,charindex('.',str)-1) as col_first,
parsename(substring(str,charindex('.',str)+1,len(str)),4) as col_second,
parsename(substring(str,charindex('.',str)+1,len(str)),3) as col_third,
parsename(substring(str,charindex('.',str)+1,len(str)),2) as col_fourth,
parsename(substring(str,charindex('.',str)+1,len(str)),1) as col_fifth
from t;
col_first col_second col_third col_fourth col_fifth
--------- ---------- --------- ---------- ---------
First Second Third Fourth Fifth
P.S. firstly, need to split the main string into the parts with at most 3 three dot(.) character(otherwise the function doesn't work). It's a restriction for parsename.
Rextester Demo
I have a table with two columns like this
col1 col2
a b
b a
c d
d a
I want to get distinct values of these two columns combined with comma separated.
Expected out put is like this
a,b,c,d
The following example concatenate row values into a variable
DECLARE #val nvarchar(max)
SELECT #val = COALESCE(#val + ',' + col1, col1)
FROM (SELECT col1
FROM dbo.twoColumns
UNION
SELECT col2
FROM dbo.twoColumns
) x
SELECT #val
Demo on SQLFiddle
try this , its very much easy i think
select group_concat(distinct(c)) as d
from
(
select col1 c from your_table
union
select col2 c from your_table
) as d
I have the following input:
Data
-----
A,10
A,20
A,30
B,23
B,45
Expected output:
col1 Col2
---- -----
A 10
A 20
A 30
B 23
B 45
How can I split the string to produce the desired output?
SELECT substring(data, 1, CHARINDEX(',',data)-1) col1,
substring(data, CHARINDEX(',',data)+1, LEN(data)) col2
FROM table
I know the points has already been given, going to post it anyway because i think it is slightly better
DECLARE #t TABLE (DATA VARCHAR(20))
INSERT #t VALUES ('A,10');INSERT #t VALUES ('AB,101');INSERT #t VALUES ('ABC,1011')
SELECT LEFT(DATA, CHARINDEX(',',data) - 1) col1,
RIGHT(DATA, LEN(DATA) - CHARINDEX(',', data)) col2
FROM #t
if the values in column 1 are always one character long, and the values in column 2 are always 2, you can use the SQL Left and SQL Right functions:
SELECT LEFT(data, 1) col1, RIGHT(data, 2) col2
FROM <table_name>
declare #string nvarchar(50)
set #string='AA,12'
select substring(#string,1,(charindex(',',#string)-1) ) as col1
, substring(#string,(charindex(',',#string)+1),len(#string) ) as col2![my sql server image which i tried.][1]
it is so easy, you can take it by below query:
SELECT LEFT(DATA, CHARINDEX(',',DATA)-1) col1,RIGHT(Data,LEN(DATA)-(CHARINDEX(',',DATA))) col2 from Table
I trying to get a grouping done between 2 rows without a cursor, can some one help me reg this
Col1(int) Col2(int)
--------- ---------
1 20
2 30
3 40
I want output like this
Col1 Col2
---- ----
1-2 50
2-3 70
Are you sure you aren't missing any rows...
Select cast(a.col1 as varchar(10)) + '-' + cast(b.col1 as varchar(10)) as col1,
a.col2 + b.Col2 as Col2
From mytable a
Inner Join mytable b on b.col1 = (a.col1 + 1)
if you might be missing rows, you might need to be more complicated.
That's a tricky one if you don't want to repeat the rows (1-2, 2-3) and you can expect there to be some missing ids (as would be normal if you have an identity field).
Try this:
CREATE TABLE #temp (id INT, value INT)
INSERT INTO #temp
SELECT 1,2
UNION ALL
SELECT 2,8
UNION ALL
SELECT 3,8
UNION ALL
SELECT 5,19
SELECT id, value, ROW_NUMBER() OVER (ORDER BY id) AS rownumber
INTO #temp2
FROM #temp
SELECT * FROM #temp2
SELECT CAST(b.id AS VARCHAR(10)) + '-' + CAST(a.id AS VARCHAR(10)) AS col1,
a.value + b.value as Col2
FROM #temp2 a
JOIN #temp2 b
ON a.rownumber = b.rownumber+1
WHERE ABS(a.rownumber)%2 = 0
Assuming that col1 is integer
SELECT CAST(a.col1 as VARCHAR(10))+ '-' + CAST(b.col1 as VARCHAR(10)), COALESCE(a.col2,0)+COALESCE(b.col2,0)
FROM table a
JOIN table b a.col1 = b.col1 + 1
you can test following query also...
I have oracle in my machine that's why I can run and say only oracle queries..
please check whether this will work on sql server also or not and tell me about ...
select * from
(Select lag (col1) over (order by col1)|| '-' || col1 as col1
col2 + lag (col2) over (order by col1) as Col2
From mytable
)
where col2 is not null;
in oracle lag () function used to fatch last row values.. and if it is first row then this function will give null values.. so that by appling addition on null values you will get null only
by this concept we will get desired output...