Combining rows and in SQL [duplicate] - sql

This question already has answers here:
How to use GROUP BY to concatenate strings in SQL Server?
(22 answers)
Closed 11 days ago.
My data is as follows:
**Name** **DOB** **B1** **C1**
cust A 01/06/99 ba1 ca1
cust A 01/06/99 ba2 ca2
I need to combine the rows for columns b1 and c1 within one row with the result looking like:
**Name** **DOB** **B1** **C1**
cust A 01/06/99 ba1, ba2 ca1, ca2
I'm tried different ways of concatenating the rows together, but it hasn't seemed to work. There can also be more than 2 rows for a single customer or only 1. Any help is appreciated.

If 2017+ you can use string_agg() in a simple aggregation query.
Select Name
,DOB
,B1 = string_agg(B1,', ')
,c1 = string_agg(c1,', ')
From YourTable
Group By Name,DOB

If you do not have SQL Server 2017, you can use stuff for XML to get a comma separated value of your table.
Like this :
; with mytable as (
select 'Cust A' as cust, '01/06/99' as dob, 'ba1' as b1, 'ca1' as c1
union all select
'Cust A' as cust, '01/06/99' as dob, 'ba2' as b1, 'ca2' as c1
)
select distinct
cust
,dob
,
STUFF(
(
SELECT ',' + b1
FROM mytable A1
WHERE A1.cust = A2.cust FOR XML PATH('')
), 1, 1, '') AS b1
,
STUFF(
(
SELECT ',' + c1
FROM mytable A1
WHERE A1.cust = A2.cust FOR XML PATH('')
), 1, 1, '') AS c1
FROM mytable A2;
Results :

Related

SQL Concatenate and group

I need a TSQL version of group_concat
Simmilar to the example found here:
Policy Destination ID
-------------------------
PolA DestA 1
PolA DestA 2
PolB DestB 3
PolB DestB 4
PolC DestC 5
PolC DestC 6
PolC DestD 7
The output should look like this:
PolA DestA 1,2
PolB DestB 3,4
PolC DestC 5,6
PolC DestD 7
The grouping is on the uniqueness of both the first 2 columns, and then a concatenated output on the third.
I found this link but it only take into account 2 columns
Any help would be appreciated.
You can try this :
SELECT G.Policy, G.Destination,
stuff(
(select cast(',' as varchar(max)) + U.ID
from yourtable U
WHERE U.Policy = G.Policy and U.Destination = G.Destination
order by U.Policy
for xml path('')
), 1, 1, '') AS IDs
FROM yourtable G group BY G.Policy, G.Destination
In MSSQL Synax:
SELECT Policy, Destination, STRING_AGG ( [ID], ',' ) IDs
FROM Table
I just create the PolA example table for you, just replace the CTE as your table, try below:
WITH ABC
as
(
select 'PolA' as Policy,'DestA' as Destination,'1' as ID
UNION ALL
select 'PolA','DestA','2'
)
SELECT Policy, Destination,
STUFF((SELECT ',' + A.ID FROM ABC as A WHERE A.Policy = B.Policy FOR XML PATH('')),1,1,'') as ID
FROM ABC as B
GROUP BY B.policy, B.Destination

get the rows of a table in 1 row

I have first table (the count of rows are varibale) and I want to create the second table. what is the efficient way?
First you have to bring your data to a more 'friendly' format:
;with
data as
(
-- replace this with your select
select * from
(
VALUES ('1', 'a', 'b'),
('2', 'c', 'd'),
('3', 'e', 'f')
) as data(aa,bb,cc)
--------------------------------
),
dataAsXml as
(
select CAST(STUFF((SELECT '<i>' + d.[aa] + '</i><i>' + d.[bb] + '</i><i>' + d.[cc] + '</i>' FROM data d FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'),1,0,'') as XML) as data
),
dataAsList as
(
select x.i.value('for $i in . return count(../*[. << $i]) + 1', 'int') as 'Ord',
x.i.value('.', 'NVARCHAR(100)') AS 'Value'
from dataAsXml
CROSS APPLY [data].nodes('//i') x(i)
),
normalized AS
(
select
case (Ord - 1) % 3 + 1
when 1 then 'aa'
when 2 then 'bb'
when 3 then 'cc'
end + cast((Ord - 1) / 3 + 1 as varchar(10)) as columnName, --fix here
value
from dataAsList
)
select * from normalized
In the query above you can plug in your data in data CTE to see the result.
The output will have two columns one that stores your column names and one with values.
SQL Fiddle
From here you have to use a dynamic query where you pivot the obtained table for columnName in the list of all the column names. I won't describe this process because it has been done many times. Take a look at this answer:
Convert Rows to columns using 'Pivot' in SQL Server
Note:
I didn't tested the performance of this method with large sets of data but
from some points of view it's efficient.
Try this one. I pivoted each of the columns then join them together in one row.
SELECT aa1,bb1,cc1,aa2,bb2,cc2,aa3,bb3,cc3 FROM
(SELECT 1 id,[2]aa1,[3]aa2,[4]aa3 FROM(SELECT aa FROM tablea) AS A
PIVOT(SUM(aa) FOR aa in([2],[3],[4])) AS pvt) A
INNER JOIN
(SELECT 1 id,[400]bb1,[200]bb2,[500]bb3 FROM(SELECT bb FROM tablea) AS A
PIVOT(SUM(bb) FOR bb in([400],[200],[500])) AS pvt) B ON A.id=B.id
INNER JOIN
(SELECT 1 id,[20]cc1,[25]cc2,[20]cc3 FROM(SELECT cc FROM tablea) AS A
PIVOT(MIN(cc) FOR cc in([20],[25])) AS pvt) C ON B.id=C.id

Reshape SQL date

following problem
I am using the command to join Names with project numbers
SELECT DDR_namen.vorname_nachname, DDR_Erfinder_final.pubnr
FROM DDR_namen
RIGHT JOIN DDR_Erfinder_final
ON DDR_namen.vorname_nachname=DDR_Erfinder_final.vorname_nachname
which gives me someting like this (small example)
vorname_nachname Pubnr (ID)
A. Heinrich 100
B. Müller 100
B. Müller 101
B. Müller 105
C. Krüger 120
C. Krüger 100
Now I want to reshape the data, so that the rows are unique and the ID are combined into a new collumn. Like this.
vorname_nachname Pubnr (ID)
A. Heinrich 100
B. Müller 100;101;105
C. Krüger 120;100
Anybody and ideas?
Try grouping like
select vorname_nachname,
(
SELECT STUFF((SELECT ';' + CAST(Pubnr AS VARCHAR(MAX))
FROM TestTable
WHERE vorname_nachname = t1.vorname_nachname
FOR XML PATH('') ), 1, 1, '')
) AS Pubnr
from TestTable t1
group by vorname_nachname
SQL Fiddle Demo : http://sqlfiddle.com/#!3/d0916/1
UPDATE : For JOIN you can use CTE like below :
;WITH CTE AS
(
SELECT DDR_namen.vorname_nachname, DDR_Erfinder_final.pubnr
FROM DDR_namen
RIGHT JOIN DDR_Erfinder_final
ON DDR_namen.vorname_nachname=DDR_Erfinder_final.vorname_nachname
)
select vorname_nachname,
(
SELECT STUFF((SELECT ';' + CAST(Pubnr AS VARCHAR(MAX))
FROM CTE
WHERE vorname_nachname = t1.vorname_nachname
FOR XML PATH('') ), 1, 1, '')
) AS Pubnr
from CTE t1
group by vorname_nachname

sql - getting the count of employee in each grade in a specific format

I am new to sql ,I have a table like this
Emp_id | Emp_NAME | EMP_GRADE
1 Test1 A1
2 Test2 A2
3 Test3 A3
4 Test4 A4
6 Test5 A1
7 Test6 A2
8 Test7 A3
I need to get the count of the employee in each grade , in which the final ouput will be
"2 - 2 - 2 - 1 " in a single column where output refers (Count of Employee in each Grade ie A1(2) - A2(2)- A3(2) -A4(1)) . can anyone give sql query for this. I hope we dont need cursor for this .
SELECT COUNT(Emp_id) FROM myTableName GROUP BY EMP_GRADE
Use:
DECLARE #Grades varchar(1000)
SELECT #Grades=coalesce(#Grades + ' ','') +Cast(COUNT(EMP_GRADE) as Varchar(2))+' -' From TableName
Group By EMP_GRADE
Select #Grades=SUBSTRING(#Grades,0,LEN(#Grades))
Select #Grades
Update:
SELECT #Grades=coalesce(#Grades + ' ','') +Cast(COUNT(t1.EMP_GRADE) as Varchar(2))+' -' From #tab1 t
Left Join #tab1 t1 On t1.EMP_GRADE= t.EMP_GRADE And t1.Emp_id= t.Emp_id
And t1.EMP_GRADE<>'A3' -- Replace conditions here
Group By t1.EMP_GRADE,t.EMP_GRADE
This should work:
SELECT EMP_GRADE, COUNT(EMP_Id) AS EMPS_COUNT
FROM TableName
GROUP BY EMP_GRADE
Hope that helps. Keep learning SQL.
SELECT STUFF((
SELECT ' - ' + CAST(COUNT(1) AS VARCHAR(max))
FROM myTable
GROUP BY EMP_GRADE
ORDER BY EMP_GRADE
FOR XML PATH('')
), 1, 3, '')
SQL Fiddle example
If you are filtering but still want to return results for every grade, you will need a self-join to get the full list of grades. Here's one way:
;WITH g AS (SELECT DISTINCT EMP_GRADE FROM myTable)
SELECT STUFF((
SELECT ' - ' + CAST(COUNT(t.Emp_id) AS VARCHAR(max))
FROM g
LEFT OUTER JOIN myTable t ON g.EMP_GRADE = t.EMP_GRADE
AND t.Emp_id % 2 = 1 --put your filter conditions here as part of the join
GROUP BY g.EMP_GRADE
ORDER BY g.EMP_GRADE
FOR XML PATH('')
), 1, 3, '')
SQL Fiddle example

SQL 2005 Merge / concatenate multiple rows to one column

We have a bit of a SQL quandry. Say I have a results that look like this...
61E77D90-D53D-4E2E-A09E-9D6F012EB59C | A
61E77D90-D53D-4E2E-A09E-9D6F012EB59C | B
61E77D90-D53D-4E2E-A09E-9D6F012EB59C | C
61E77D90-D53D-4E2E-A09E-9D6F012EB59C | D
7ce953ca-a55b-4c55-a52c-9d6f012ea903 | E
7ce953ca-a55b-4c55-a52c-9d6f012ea903 | F
is there a way I can group these results within SQL to return as
61E77D90-D53D-4E2E-A09E-9D6F012EB59C | A B C D
7ce953ca-a55b-4c55-a52c-9d6f012ea903 | E F
Any ideas people?
Many thanks
Dave
try this:
set nocount on;
declare #t table (id char(36), x char(1))
insert into #t (id, x)
select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'A' union
select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'B' union
select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'C' union
select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'D' union
select '7ce953ca-a55b-4c55-a52c-9d6f012ea903' , 'E' union
select '7ce953ca-a55b-4c55-a52c-9d6f012ea903' , 'F'
set nocount off
SELECT p1.id,
stuff(
(SELECT
' ' + x
FROM #t p2
WHERE p2.id=p1.id
ORDER BY id, x
FOR XML PATH('')
)
,1,1, ''
) AS YourValues
FROM #t p1
GROUP BY id
OUTPUT:
id YourValues
------------------------------------ --------------
61E77D90-D53D-4E2E-A09E-9D6F012EB59C A B C D
7ce953ca-a55b-4c55-a52c-9d6f012ea903 E F
(2 row(s) affected)
EDIT
based on OP's comment about this needing to run for an existing query, try this:
;WITH YourBugQuery AS
(
--replace this with your own query
select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' AS ColID , 'A' AS ColX
union select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'B'
union select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'C'
union select '61E77D90-D53D-4E2E-A09E-9D6F012EB59C' , 'D'
union select '7ce953ca-a55b-4c55-a52c-9d6f012ea903' , 'E'
union select '7ce953ca-a55b-4c55-a52c-9d6f012ea903' , 'F'
)
SELECT p1.ColID,
stuff(
(SELECT
' ' + ColX
FROM YourBugQuery p2
WHERE p2.ColID=p1.ColID
ORDER BY ColID, ColX
FOR XML PATH('')
)
,1,1, ''
) AS YourValues
FROM YourBugQuery p1
GROUP BY ColID
this has the same results set as displayed above.
I prefer to define a custom user-defined aggregate. Here's an example of a UDA which will accomplish something very close to what you're asking.
Why use a user-defined aggregate instead of a nested SELECT? It's all about performance, and what you are willing to put up with. For a small amount of elements, you can most certainly get away with a nested SELECT, but for large "n", you'll notice that the query plan essentially runs the nested SELECT once for every row in the output list. This can be the kiss of death if you're talking about a large number of rows. With a UDA, it's possible to aggregate these values in a single pass.
The tradeoff, of course, is that the UDA requires you to use the CLR to deploy it, and that's something not a lot of people do often. In Oracle, this particular situation is a bit nicer as you can use PL/SQL directly to create your user-defined aggregate, but I digress...
Another way of doing it is to use the FOR XML PATH option
SELECT
[ID],
(
SELECT
[Value] + ' '
FROM
[YourTable] [YourTable2]
WHERE
[YourTable2].[ID] = [YourTable].[ID]
ORDER BY
[Value]
FOR XML PATH('')
) [Values]
FROM
[YourTable]
GROUP BY
[YourTable].[ID]