SQL - How to Group by and then Concatenate joins? - sql

I have the made a join between 2 tables but the issue is that I need to see unique
CustomerId and concatenate Accreditations with comma separated e,i Gold,Silver,Platinum
Here is my SQL script
SELECT TOP (1000)
Customer.CustomerID,
Customer.CustomerName,
Customer_Accreditations.AccreditationNumber
FROM
Customer INNER JOIN
Customer_Accreditations ON Customer.CustomerID =
Customer_Accreditations.CustomerID
My Result with this script is as follows
CustomerID CustomerName AccreditationNumber
1272 Bree Gold
1272 Bree Gold
1272 Bree Gold
4071 Anneke Silver
4071 Anneke Silver
4071 Anneke Platinum
My desired result should be
CustomerID CustomerName AccreditationNumber
1272 Bree Gold,Gold,Gold
4071 Anneke Silver,Silver,Platinum
So far I have only been able to get unique values and concatenate Accreditations
Here is the script:
SELECT CustomerID, displayname =
STUFF((SELECT DISTINCT ',' + [AccreditationNumber]
FROM Customer_Accreditations b
WHERE b.CustomerID = a.CustomerID
FOR XML PATH('')), 1, 2, '')
FROM [DATABASE_NAME].[dbo].[Customer_Accreditations] a
GROUP BY CustomerID
This returns the following results
______________________________
|CustomerId | displayname |
|____________________________|
|1272 | Gold,Gold,Gold|
______________________________
This is great but if only I could join more of Customer tables/
Many thanks!

Try This
SELECT CustomerID,
CustomerName ,
STUFF(SELECT DISTINCT ', ' + AccreditationNumber
FROM <Table> i
WHERE i.CustomerID = o.CustomerID
FOR XML PATH ('')),1,1,'') AS AccreditationNumber
FROM <Table> o

I think you should be using the customers table to get one row per customer. I think this should be doing what you want:
SELECT c.*,
STUFF( (SELECT ',' + [AccreditationNumber]
FROM Customer_Accreditations ca
WHERE ca.CustomerID = c.CustomerID
FOR XML PATH('')
), 1, 2, ''
) as displayname
FROM [DATABASE_NAME].[dbo].[Customer] c;

SELECT a.CustomerID, a.CustomerName, displayname =
STUFF((SELECT DISTINCT ',' + [AccreditationNumber]
FROM Customer_Accreditations b
WHERE b.CustomerID = a.CustomerID
FOR XML PATH('')), 1, 2, '')
FROM [DATABASE_NAME].[dbo].[Customer] a
GROUP BY a.CustomerID, a.CustomerName

Related

Convert a column of Rows to only one column column in SQL Server query

I have this query in SQL Server 2012 :
select
tblplantitle.id, tblplantoproductpayment.productid
from
tblplantitle
inner join
tblplan on tblplan.plantitleid = tblplantitle.id
inner join
tblplantoproductpayment on tblplantoproductpayment.planid = tblplan.id
group by
tblplantitle.id, tblplantoproductpayment.productid
The result is like this :
id productid
1 1
1 2
1 3
1 10
2 5
2 1
3 4
3 11
but I want this result :
1 1,2,3,10
2 5,1
3 4,11
How can I get that result ?
Try this:
WITH cte as
(
select
tblplantitle.id, tblplantoproductpayment.productid
from
tblplantitle
inner join
tblplan on tblplan.plantitleid = tblplantitle.id
inner join
tblplantoproductpayment on tblplantoproductpayment.planid = tblplan.id
group by
tblplantitle.id, tblplantoproductpayment.productid
)
SELECT id, productid =
STUFF((SELECT ', ' + productid
FROM cte b
WHERE b.id= a.id
FOR XML PATH('')), 1, 2, '')
FROM cte a
GROUP BY id
Use the below query for the desired output.
SELECT tblplantitle.id
, STUFF((SELECT ', ' + CAST(tblplantoproductpayment.productid AS VARCHAR(10)) [text()]
FROM tblplantoproductpayment
WHERE tblplantoproductpayment.planid = tblplan.id
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,' ') List_ProductIds
FROM tblplantitle
INNER JOIN tblplan on tblplan.plantitleid = tblplantitle.id
GROUP BY tblplantitle.id

SQL add STUFF function into this query

I have the following query
SELECT TOP 1000
o.BuyerEMail,
COUNT(*) HowMany,
o.Name, o2.OrderID
FROM
Orders o
JOIN
(SELECT
BuyerEmail, MAX(OrderDate) Latest
FROM Orders
GROUP BY BuyerEmail) l ON o.BuyerEmail = l.BuyerEmail
JOIN
Orders o2 ON l.BuyerEmail = o2.BuyerEmail
AND l.OrderDate = o2.OrderDate
WHERE
Pay != 'PayPal'
GROUP BY
o.BuyerEmail, o.Name, l.Latest
ORDER BY
It's just producing a report about customers and how many orders they have with us and the latest order.
What I want to add is a list of the products of their last order which are in lines table.
Lines table is linked to order table by OrderID.
I was looking for something like:
JOIN
(SELECT
OrderID,
STUFF((SELECT ', ' + li.Code
FROM tblLines li
WHERE li.OrderID = o2.OrderID
FOR XML PATH ('')), 1, 1, '') AS [Codes]
GROUP BY
OrderID, Code
So the final table displays as
BuyerEmail | HowMany | Name | Latest | Codes
---------------------------------------------
Bob#bob | 4 | bob | 10000 | 123,10,201231
As you posted the query from another question (SQL most recent order? MS SQL), I will use my answer as it is cleaner than the above query:
SELECT o.*
, OrderID as LastOrderID
FROM (
SELECT BuyerEMail
, Name
, COUNT(*) as TotalOrders
FROM Orders
WHERE Pay != 'PayPal'
GROUP BY BuyerEmail, Name
) o
CROSS APPLY (
SELECT TOP 1 OrderID, OrderDate
FROM Orders s
WHERE s.BuyerEmail = o.BuyerEmail
ORDER BY OrderDate DESC
) ca
You posted a good example, it wasn't complete thou. You would need the following xmlpath query:
SELECT OrderID
, Codes
FROM tblLines r1
CROSS APPLY (
SELECT
STUFF((SELECT ',' + CAST(Code AS NVARCHAR)
FROM tblLines r2
WHERE r2.OrderID = r1.OrderID
GROUP BY OrderID, Code
ORDER BY Code
FOR XML PATH (''), TYPE)
.value('.', 'varchar(max)')
, 1, 1, '')) OrderLines(Codes)
GROUP BY OrderID, OrderList
Add it to the previous statement with a simple join:
SELECT o.BuyerEMail
,o.Name
,o.TotalOrders
, OrderID as LastOrderID
, c.Codes
FROM (
SELECT BuyerEMail
, Name
, COUNT(*) as TotalOrders
FROM Orders
WHERE Pay != 'PayPal'
GROUP BY BuyerEmail, Name
) o
CROSS APPLY (
SELECT TOP 1 OrderID, OrderDate
FROM Orders s
WHERE s.BuyerEmail = o.BuyerEmail
ORDER BY OrderDate DESC
) ca
INNER JOIN (
SELECT OrderID
, Codes
FROM tblLines r1
CROSS APPLY (
SELECT
STUFF((SELECT ',' + CAST(Code AS NVARCHAR)
FROM tblLines r2
WHERE r2.OrderID = r1.OrderID
GROUP BY OrderID, Code
ORDER BY Code
FOR XML PATH (''), TYPE)
.value('.', 'varchar(max)')
, 1, 1, '')) OrderLines(Codes)
GROUP BY OrderID, OrderList
) c
ON ca.OrderID = c.OrderID
SELECT TOP 1000
o.BuyerEMail,
COUNT(*) HowMany,
o.Name, l.Latest,
STUFF((SELECT ', ' + li.Code
FROM tblLines li
WHERE li.OrderID = o2.OrderID
FOR XML PATH ('')), 1, 1, '') AS [Codes]
FROM
Orders o
JOIN
(SELECT
BuyerEmail, MAX(OrderDate) Latest
FROM Orders
GROUP BY BuyerEmail) l ON o.BuyerEmail = l.BuyerEmail
JOIN
Orders o2 ON l.BuyerEmail = o2.BuyerEmail
AND l.OrderDate = o2.OrderDate
WHERE
Pay != 'PayPal'
GROUP BY
o.BuyerEmail, o.Name, l.Latest
ORDER BY

Query for comma-separated ids to comma-separated values

I have 2 tables
Departments
ID Dept
---------
1 HR
2 Accts
3 IT
Employee
ID Name Depts
-------------------
1 Kevin 2,1
2 Michelle 1
3 Troy 1,3
4 Rheesa 2,3,1
I am looking for an output like the following with a SQL query.
Employee depts
ID Name Depts
-------------------------
1 Kevin Accts,HR
2 Michelle HR
3 Troy HR,IT
4 Rheesa Accts,IT,HR
I have tried the following that join s with depts but results in one row for each dept only. How do i get the above results using a query?
select
name, depts, dept
from
employee
CROSS APPLY
dbo.split_list(employee.depts ,',') split
inner join
dbo.department on depts= split.value
order by
name
DECLARE #Departments TABLE
(
ID INT PRIMARY KEY,
Dept VARCHAR(32) NOT NULL UNIQUE
);
DECLARE #Employees TABLE
(
ID INT PRIMARY KEY,
Name NVARCHAR(64) NOT NULL,
Depts VARCHAR(255) NOT NULL
);
INSERT #Departments VALUES
(1,'HR'), (2,'Accts'), (3,'IT');
INSERT #Employees VALUES
(1,'Kevin','2,1'), (2,'Michelle','1'),
(3,'Troy','1,3'), (4,'Rheesa','2,3,1');
SELECT ID, Name, Depts = STUFF((SELECT ',' + d.Dept
FROM #Departments AS d
INNER JOIN #Employees AS ei
ON ',' + ei.Depts + ',' LIKE '%,' + CONVERT(VARCHAR(12), d.id) + ',%'
WHERE ei.ID = e.ID
ORDER BY Dept
FOR XML PATH,
TYPE).value(N'/text().[1]', N'nvarchar(max)'), 1, 1, N'')
FROM #Employees AS e
ORDER BY ID;
The results don't quite match your required results, as the ordering is deterministic (ordered by department name):
ID Name Depts
---- -------- ----
1 Kevin Accts,HR
2 Michelle HR
3 Troy HR,IT
4 Rheesa Accts,HR,IT
If you want them ordered by the appearance in the comma-separated list, just change:
ORDER BY Dept
To:
ORDER BY CHARINDEX( ',' + CONVERT(VARCHAR(12), d.id) + ',', ',' + ei.Depts + ',')
Results:
ID Name Depts
---- -------- ----
1 Kevin Accts,HR
2 Michelle HR
3 Troy HR,IT
4 Rheesa Accts,IT,HR -- this is the only one affected as it turns out
However, in reality, you should normalize your database. This is an absolute nightmare.
Looking beyond how you're storing your data, let me try to help you out.
Well, you're asking a lot of questions here. First, to split the data, you can format it as XML and use CROSS APPLY -- trick I saw a while back that didn't require built in functions.
That will convert your comma delimited string to a list of strings. You can then use FOR XML to put them back together.
Give this a shot:
SELECT
E.Id,
E.Name,
STUFF(
(
SELECT ',' + D.Department AS [text()]
FROM (
SELECT A.[id],
Split.a.value('.', 'VARCHAR(100)') AS DeptId
FROM
(SELECT [id],
CAST ('<M>' + REPLACE(Depts, ',', '</M><M>') + '</M>' AS XML) AS String
FROM Employee
) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)) A
JOIN Departments D ON A.DeptId = D.Id
WHERE E.Id = A.Id
FOR XML PATH('')
), 1, 1, '') AS Departments
FROM Employee E
And here is the SQL Fiddle.
Good luck.
You can also use a recursive CTE to split the data and then use FOR XML PATH to concatenate the rows into a single row:
;with cte (id, name, deptid, depts) as
(
select id, name,
cast(left(depts, charindex(',',depts+',')-1) as varchar(50)) deptid,
stuff(depts, 1, charindex(',',depts+','), '') depts
from employee
union all
select id, name,
cast(left(depts, charindex(',',depts+',')-1) as varchar(50)) deptid,
stuff(depts, 1, charindex(',',depts+','), '') depts
from cte
where depts > ''
)
select e.id, e.name,
stuff((
select distinct ', '+ d.dept
from cte c
inner join departments d
on c.deptid = d.id
where e.id = c.id
for XML path('')),1,1,'') Depts
from employee e
See SQL Fiddle with Demo
Result:
| ID | NAME | DEPTS |
----------------------------------
| 1 | Kevin | Accts, HR |
| 2 | Michelle | HR |
| 3 | Troy | HR, IT |
| 4 | Rheesa | Accts, HR, IT |
Also you can use option with dynamic management function sys.dm_fts_parser
Before script execution you need check full-text component is installed:
SELECT FULLTEXTSERVICEPROPERTY ('IsFulltextInstalled')
0 = Full-text is not installed.
1 = Full-text is installed.
NULL = Invalid input, or error.
If 0 = Full-text is not installed then this post is necessary to you How to install fulltext on sql server 2008?
SELECT b.ID, b.Name, STUFF((
SELECT ',' + d.Dept
FROM Employees e
JOIN Departments d ON d.ID IN(
SELECT display_term
FROM sys.dm_fts_parser('"' + e.Depts + '"', 1033, NULL, 0)
WHERE display_term NOT LIKE 'nn%'
)
WHERE b.ID = e.ID
ORDER BY d.Dept
FOR XML PATH('')), 1, 1, '') AS Depts
FROM Employees b
OR
SELECT e.ID, e.Name,
(
STUFF((
SELECT ',' + Dept
FROM sys.dm_fts_parser('"' + e.Depts + '"', 1033, NULL, 0) p JOIN Departments d ON p.display_term = d.ID
WHERE display_term NOT LIKE 'nn%'
FOR XML PATH('')), 1, 1, '')
) AS Depts
FROM Employees e
Write a function for splitting comma separated values. I wrote dbo.split
select * from dbo.split('1,2,3',',')
Will return as -
Data
1
2
3
SELECT tact.ActivityID,CONVERT(NVARCHAR(20),tact.createddate,103) AS CallDate,
ActivityOriginatedByPartyID , (ISNULL(p.firstname,'')+' '+ISNULL(p.lastname,'')) AS PartnerName,
u.UserName AS PSTName, ccv.codevalue AS CallType
**, ccv1.codevalue AS CallContext**
,tact.ActivityNote AS CallTrackerNote,
(CONVERT(VARCHAR(20),tact.ActivityTimeSpend) + ' Min') AS CallDuration
FROM txn_activity tact (NOLOCK)
INNER JOIN TXN_Party p (NOLOCK) ON p.PartyID = tact.ActivityOriginatedByPartyID
INNER JOIN TXN_User u (NOLOCK) ON u.userid = tact.createdby
INNER JOIN CFG_CodeValue ccv (NOLOCK) ON ccv.codevalueid = tact.ActivityTypeID
--INNER JOIN CFG_CodeValue ccv1 (NOLOCK) ON ccv1.codevalueid = tact.ActivityContext
**CROSS APPLY
dbo.split(tact.ActivityContext,',') split
inner join
dbo.CFG_CodeValue ccv1 (NOLOCK) ON ccv1.codevalueid = split.data**

SQL: Combining Rows with Some Conditions And Delete Rest of Them

I have a table like this;
Table: PROSPECT
ID CUSTOMER NUMBER
1 A XXXA1
1 B XXXA1
1 G XXX00
2 C XXX32
2 D XXX32
2 E XXX32
2 F XXX66
I want to combine rows' customer's name with ; where they has same ID and NUMBER. (and delete other rows.)
I want to it like this;
Table: PROSPECT
ID CUSTOMER NUMBER
1 A;B XXXA1
1 G XXX00
2 C;D;E XXX32
2 F XXX66
How can I do that?
Disclaimer
I agree with #Dems that you should not change your normalized data to this.
A better alternative might be to create a VIEW that presents you the data in the format you need.
SQL Statement
CREATE VIEW dbo.VIEW_PROSPECT AS
SELECT po.ID
, STUFF((SELECT '; ' + CUSTOMER FROM PROSPECT pi WHERE pi.ID = po.ID AND pi.N UMBER = po.NUMBER ORDER BY CUSTOMER FOR XML PATH('')),1,2,'') AS CUSTOMER
, po.NUMBER
FROM PROSPECT po
GROUP BY
po.ID
, po.NUMBER
Test script
;WITH PROSPECT(ID, CUSTOMER, NUMBER) AS (
SELECT 1, 'A', 'XXXA1'
UNION ALL SELECT 1, 'B', 'XXXA1'
UNION ALL SELECT 1, 'G', 'XXX00'
UNION ALL SELECT 2, 'C', 'XXX32'
UNION ALL SELECT 2, 'D', 'XXX32'
UNION ALL SELECT 2, 'E', 'XXX32'
UNION ALL SELECT 2, 'F', 'XXX66'
)
SELECT po.ID
, STUFF((SELECT '; ' + CUSTOMER FROM PROSPECT pi WHERE pi.ID = po.ID AND pi.NUMBER = po.NUMBER ORDER BY CUSTOMER FOR XML PATH('')),1,2,'') AS CUSTOMER
, po.NUMBER
FROM PROSPECT po
GROUP BY
po.ID
, po.NUMBER
ORDER BY
ID
, CUSTOMER
, NUMBER
SELECT DISTINCT ID, LEFT(T.CUSTOMER,LEN(T.CUSTOMER)-1), NUMBER
FROM PROSPECT A CROSS APPLY
(SELECT CUSTOMER + ';' AS [text()] FROM PROSPECT B
WHERE A.ID = B.ID and A.NUMBER = B.NUMBER
ORDER BY ID FOR XML PATH('')) T (CUSTOMER)

TSQL in SQL 2005: Query

I have 3 tables: Customer, CustomerTypes, CustomerCustomerTypes. CustomerCustomerTypes is basically is a bridge table between the Customer and CustomerTypes.
Table structure:
Customers:
CustomerID
CustomerName
CustomerTypes:
CustomerTypeID
CusctomerTypeName
CustomerCustomerTypeID
CustomerID
CustomerTypeID
Sample Data:
Customers:
1, ABC
2, CBA
CustomerTypes:
1, Broadcast
2, Banking
3, Retailer
CustomerCustomerTypes:
1, 1
2, 2
2, 3
I want to be able to return query as follow:
ABC; "Broadcasting"
CustomerCustomerTypes; "Banking, Retailer"
as well as to be able to search that string let say "CustomerTypeID = 2"
It will be ruturned as :
CustomerCustomerTypes; "Banking, Retailer"
I can do this with cursor type of query BUT i am just wondering maybe there is a better way.
Thanks
Use:
SELECT c.customername,
STUFF((SELECT ','+ ct.customertypename
FROM CUSTOMERTYPES ct
JOIN CUSTOMERCUSTOMERTYPES cct ON cct.customertypeid = ct.customertypeid
WHERE cct.customerid = C.customerid
GROUP BY ct.customertypename
FOR XML PATH('')), 1, 1, '')
FROM CUSTOMERS c
To be able to search for a specific customer type:
SELECT c.customername,
STUFF(SELECT ','+ ct.customertypename
FROM CUSTOMERTYPES ct
JOIN CUSTOMERCUSTOMERTYPES cct ON cct.customertypeid = ct.customertypeid
WHERE cct.customerid = C.customerid
GROUP BY ct.customertypename
FOR XML PATH(''), 1, 1, '')
FROM CUSTOMERS c
JOIN CUSTOMERCUSTOMERTYPES cct ON cct.customertypeid = ct.customertypeid
WHERE cct.customertypeid = #customertypeid
A SQL Server FOR XML query will give you what you want. I haven't had a chance to check the below but it should point you in the right direction.
SELECT c1.CustomerTypeId,
( SELECT CustomerTypeName + ','
FROM CustomerTypes c2
WHERE c2.CustomerTypeId = c1.CustomerTypeIdId
ORDER BY CustomerTypeName
FOR XML PATH('') ) AS CustomerTypess
FROM CustomerCustomerTypes c1
GROUP BY CustomerTypeId;