Duplicate GROUP_CONCAT when using JOIN - sql

I'm creating a sort of geocache team building system and I'm having trouble with my query.
To collate and display the individuals against the teams I'm using GROUP_CONCAT, however when I try to include the locations (listed as markers) the individuals names are being duplicated based on the number of markers.
I have put together a SQL Fiddle to explain my workings.
Here is my query:
SELECT SQL_CALC_FOUND_ROWS
map_team.id AS team_id,
map_team.name,
GROUP_CONCAT(firstname, ' ', surname SEPARATOR ', ') AS full_name
FROM map_team
LEFT JOIN members
ON members.map_team=map_team.id
WHERE members.map_team IS NOT NULL
GROUP BY map_team.name
ORDER BY map_team.name ASC
Any advice would be appreciated.

GROUP_CONCAT( distinct ... ) will not give correct answers as the team member names in a team can be same. If you want to get it in a single query you can use:
SELECT SQL_CALC_FOUND_ROWS
map_team.id AS team_id,
map_team.name,
GROUP_CONCAT(firstname, ' ', surname SEPARATOR ', ') AS full_name,
(select count(*) from map_markers where team_id = map_team.id) AS total_markers,
(select count(*) from map_markers where team_id = map_team.id and status = 'completed') AS total_completed
FROM map_team
LEFT JOIN members
ON members.map_team=map_team.id
WHERE members.map_team IS NOT NULL
GROUP BY map_team.name
ORDER BY map_team.name ASC
If you don't like the idea of a subquery in select. You need to do it separately.

Use
GROUP_CONCAT(distinct firstname ...

use following query:
SELECT SQL_CALC_FOUND_ROWS
map_team.id AS team_id,
map_team.name,
GROUP_CONCAT(distinct firstname, ' ', surname SEPARATOR ', ') AS full_name,
count(*) AS total_markers,
SUM(IF(status = 'completed', 1, 0)) AS total_completed
FROM map_team
LEFT JOIN members
ON members.map_team=map_team.id
LEFT JOIN map_markers
ON map_markers.team_id=map_team.id
WHERE members.map_team IS NOT NULL
GROUP BY map_team.name
ORDER BY map_team.name ASC

Related

Join table with another table and fetch and replace the values which are ';' seperated

I am trying to join a table that has a column with data as a string and replace the values with the values from the joined table.
Tab 1
ID
Name
Categories
1
Programmer
1;2
2
Analyst
3;2
Tab 2
id
Firstname
lastname
1
john
kele
2
ajay
kashid
3
shubham
sharma
I need a query that will fetch the "Id,name and categories" from the first table but in the form like:
Id
Name
Categories
1
Programmer
john,kele ajay,kashid
2
Analyst
shubham,sharma ajay,kashid
I have written this one but this gives only the first entry, not for all the entries
SELECT
sc.Id,sc.Application,u.u_LastName + ', ' + u.u_FirstName 'coeowner '
FROM
Supportcentral AS sc
outer apply [dbo].[FN_split](sc.CoeOwner, ';',0) s
left join udcenter.dbo.[Users] u on u.u_Login COLLATE DATABASE_DEFAULT in (select s.item COLLATE DATABASE_DEFAULT)
For SQL Server 2017+, you may try the following:
SELECT T.ID, T.Name,
STRING_AGG(CONCAT(D.Firstname,' ',D.lastname),',') Categories
FROM
tab1 T JOIN tab2 D
ON D.ID IN (SELECT value FROM STRING_SPLIT(T.Categories, ';'))
GROUP BY T.ID, T.Name
ORDER BY T.ID
See a demo.
I think that you misplaced the comma in your posted output, if you want exactly the posted output use this STRING_AGG(CONCAT(D.Firstname,',',D.lastname),' ').
For older versions of SQL Server you may use for xml path to simulate the STRING_AGG function as the following:
WITH CTE AS
(
SELECT T.ID, T.Name, CONCAT(D.Firstname,' ',D.lastname) fullname FROM
tab1 T JOIN tab2 D
ON CONCAT(';', T.Categories,';') LIKE CONCAT('%;', D.ID, ';%')
)
SELECT id, name, STUFF(
(SELECT ',' + CAST(T.fullname as VARCHAR(MAX))
FROM CTE T WHERE T.ID = D.ID
FOR xml PATH ('')
), 1, 1, ''
) Categories
FROM CTE D
GROUP BY ID, Name
ORDER BY ID
See a demo.
Try this Query,
SELECT Id, Name, (SELECT STRING_AGG(Name,',') FROM #Users WHERE Id IN (SELECT Value FROM string_split(SC.Categories,';'))) AS Categories
FROM #Supportcentral SC

How do I write a subquery to concatenate Last name and First name using another table in database in SQL Server?

I'm using 2 tables, Orders and Employees. Both have IDs of employees. I need to get a total amount of orders completed by each separate employee.
I want my result to be this: full name, total orders.
What I could get is: employee ID, total orders.
I can't connect Order.EmployeeID with Employees.EmployeeID, even when using CAST or CONVERT.
I can make separate queries to concat names, and to show orders by employee ID, but I can't wrap my mind how to make a subquery to give me full name of each employee and total amount of their orders.
SELECT
[dbo].[Orders].EmployeeID AS Seller,
COUNT(OrderID) AS Amount
FROM
[dbo].[Orders]
JOIN
[dbo].[Employees] ON [dbo].[Employees].EmployeeID = [dbo].[Orders].EmployeeID
GROUP BY
[dbo].[Orders].EmployeeID;
I expect the following result:
|Name |TotalOrders|
+------------+-----------+
|Johnny Bravo| 120 |
Current result however is:
|ID|TotalOrders|
+--+-----------+
|1 |120 |
Simply concatenate them, like this for example
SELECT
o.EmployeeID AS Seller,
CONCAT(e.FirstName + ' ', e.LastName) AS FullName,
COUNT(o.OrderID) AS Amount
FROM
[dbo].[Orders] o
JOIN
[dbo].[Employees] e ON o.EmployeeID = e.EmployeeID
GROUP BY
o.EmployeeID, e.FirstName, e.LastName
You could also do this
(e.FirstName + ' ' + e.LastName) as FullName
But beware of this, when one of the columns is NULL then the result will also be NULL
Then you would have to do this
(isnull(e.FirstName, '') + ' ' + isnull(e.LastName, '')) as FullName
Therefore, the CONCAT function is easier, it will convert NULL values into empty strings for you.
See also this Documentation
EDIT
As noted by Sean Lange, it is better to do
Concat(e.FirstName + ' ', e.LastName) as FullName,
in stead of
Concat(e.FirstName, ' ', e.LastName) as FullName,
Because this way you will have no leading spaces when the firstname is null
SELECT [dbo].[Orders].EmployeeID AS Seller,Concat ([Orders].FirstName,[Orders].Lastname) fullname,
COUNT(OrderID) AS Amount
FROM [dbo].[Orders]
JOIN [dbo].[Employees] ON [dbo].[Employees].EmployeeID = [dbo].[Orders].EmployeeID
GROUP BY [dbo].[Orders].EmployeeID,[Orders].FirstName,[Orders].Lastname;

COALESCE() function with join

I have three tables,
comments: id, user_id, place_id, text
places: id, name
users: id, name
I'd like to show a list of all the places with a list of all the users who commented on that place.
McDonalds Jill, Suzy, Bob
Walmart Fred, Joe, Suzy, Larry
Library Joe, Suzy
...
I am trying to use the coalesce function to achieve this but I am running into trouble. What am I doing wrong?
SELECT places.name, COALESCE(users.name+",")
FROM comments
JOIN places
ON comments.place_id = places.id
WHERE user_id = users.id
GROUP BY places.name
Thanks.
Try this !
Using Coalesce is not relevant here,try using STUFF with XML PATH('')
SELECT t1.name,
STUFF(
(
SELECT ',' + [places.name] FROM comments
JOIN places
ON comments.place_id = places.id
WHERE user_id = users.id
GROUP BY places.name
for xml path('')
),1,1,'') as t1 from table <group by>

SQL select, 3 tables

How can I use select if I have 3 tables?
Tables:
school_subject(ID_of_subject, workplace, name)
student(ID_of_student, firstName, surname, adress)
writing(ID_of_action, ID_of_sbuject, ID_of_student)
I want to write the student's name and surname (alphabetically) who have workplace=home.
Is this possible? And how to alphabetize?
SELECT s.firstName, s.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home'
AND P.ID_of_subject = Z.ID_of_subject
AND Z.ID_of_student = s.ID_of_student;
SELECT s.firstName, s.surname
FROM student S INNER JOIN writing Z
ON Z.ID_of_student = s.ID_of_student
INNER JOIN school_subject P
ON P.ID_of_subject = Z.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName, S.surname // Sort the list
To order alphabetically the result it is possible to use ORDER BY keyword. So your query becomes:
SELECT DISTINCT S.firstName, S.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home' AND
P.ID_of_subject = Z.ID_of_subject AND
Z.ID_of_student = S.ID_of_student
ORDER BY S.surname, S.firstName
The DISTINCT keyword is necessary, because in writing table there are eventually more tuples given keys ID_of_subject and ID_of_student.
So this is necessary to avoid repeating firstName and surname many times.
Note that each student is identified by ID_of_student, not by firstName and surname, so as #danjok said use DISTINCT if you only want the name and surname.
If you want to select all students that satisfy your requirement (even if two or more students have the same firstName and surname), you should including ID_of_student on SELECT clause:
SELECT S.ID_of_student, S.firstName, S.surname
FROM student S
INNER JOIN writing W ON W.ID_of_student = S.ID_of_student
INNER JOIN school_subject P ON P.ID_of_subject = W.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName asc, S.surname asc

sql join multiple tables, to return rows as columns

apologies if this has been answered somewhere, I've had a search but can't find the answer.
I've got 3 SQL tables:
employee table (ID, name, etc....)
key skills table (id, skill name)
link table (employee ID, skill ID)
An employee can obviously have multiple key skills, but I'm trying to report them in 1 row as an 'employee report', like this:
Row 1 - name, dob, key skill 1, key skill 2, etc....
Row 2 - name, dob, key skill 1, key skill 2, etc....
I can get the skills to return as a number of rows using:
SELECT DISTINCT kst.KeySkillName FROM KeySkillsTable
INNER JOIN KeySkillsLinkTable kslt ON kslt.EmployeeId = 2
INNER JOIN KeySkillsTable kst ON kst.Id = kslt.KeySkillsId
but when I put this into a larger select as a subquery I get the following error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I presume this is because the subquery returns multiple rows, rather than multiple columns which I need.
Any help anyone can give would be greatly appreciated, thanks in advance.
You can perform this type of transformation with a PIVOT. There are two ways, a static pivot where you hard-code the values of the columns or a dynamic pivot where the columns are determined at run-time. Here is an example of how you can do this for your situation.
Static Pivot (See SQL Fiddle with Demo)
select *
from
(
select e.id, e.name, s.skillname, count(*) cnt
from employee e
left join emp_skill es
on e.id = es.emp_id
left join skills s
on es.skill_id = s.id
group by e.id, e.name, s.skillname
) x
pivot
(
sum(cnt)
for skillname in([skill 1], [skill 2], [skill 3])
) p
Dynamic Pivot (See SQL Fiddle with Demo)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(skillname)
from skills
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT id, name,' + #cols + ' from
(
select e.id, e.name, s.skillname, count(*) cnt
from employee e
left join emp_skill es
on e.id = es.emp_id
left join skills s
on es.skill_id = s.id
group by e.id, e.name, s.skillname
) x
pivot
(
sum(cnt)
for skillname in(' + #cols + ')
) p '
execute(#query)
Both queries will produce the same results. The difference is that in the first you have to code all of the values that you want to become columns.