Concat multiple results with left join not working - sql

In my project I have a user table with regular information such as name, age, and the phone numbers are listed on another table with a relationship table to connect them.
Something like this.
TBL_User (name, dateofbirth, mail)
TBL_PhoneType (id, phonetype) //like, cellphone, home phone, etc.
TBL_PhoneNumber (id, phonetype, phonenuber) //lists muliple values for each user
TBL_PhoneRelation (userid, pnid)
I'm trying to make a selection to return the user information and a CONCAT version of the phone numbers, but the problem is the result that I get.
My first try is the GROUP_CONCAT, something like
SELECT us.name, us.dateofbirth, GROUP_CONCAT(' ', pt.phonetype, ' ', p.phonenumber)
FROM TBL_User AS us
LEFT JOIN TBL_PhoneRelation AS pr ON pr.userid = us.id
LEFT JOIN TBL_PhoneNumber AS p ON p.id = pr.pnid
The problem is that I get only one row and not all the values from the database and a regular CONCAT show only one phone number, and sub selection gives a error because I have more than 1 row in my result.
I'm trying something like this
User name | Phone number | E-mail
Adrian | Cellphone 11..., Home phone 22... | adrian...
Suzan | Cellphone 32..., Commercial phone 44... | sue...

I would do something like this:
select
U.Name,
group_concat(', ', T.PhoneType + ' ' + N.PhoneNumber)
U.Mail
from
TBL_user as U
left join
TBL_PhoneRelation AS R ON
R.UserID = U.ID
LEFT JOIN
TBL_PhoneNumber AS N ON
N.ID = R.PhoneID
left join
TBL_PhoneType as T on
T.ID = N.PhoneTypeID
group by
U.Name

select
U.Name,
group_concat(CONCAT(T.PhoneType + ' ' + N.PhoneNumber) SEPARATOR ','),
U.Mail
from
TBL_user as U
left join
TBL_PhoneRelation AS R ON R.UserID = U.ID
LEFT JOIN
TBL_PhoneNumber AS N ON N.ID = R.PhoneID
left join
TBL_PhoneType as T on T.ID = N.PhoneTypeID
group by
U.ID

Related

Postgres - rows to columns

What i have
Query:
SELECT u.firstname,u.lastname, u.institution as department, u.department as company, c.shortname,
to_char(to_timestamp(p.timecompleted)::date,'YYYY-MM-DD') AS completed
FROM mdl_course_completions AS p
JOIN mdl_course AS c ON p.course = c.id
JOIN mdl_user AS u ON p.userid = u.id
WHERE c.enablecompletion = 1 AND u.firstname is NOT NULL AND p.timecompleted is NOT NULL
ORDER BY u.firstname
Results:
firstname lastname department company course completed
u1 u1 x x c1 date
u1 u1 x x c2 date
What i need
I need to be able to transport course to columns. Resulting in something similar to this:
firstname lastname department company c1 c2
u1 u1 x x date date
u2 u2 x x date date
I have tried using crosstab, but I am not skilled enough on SQL. Could someone please help?
EDIT: the number of courses are in the hundreds, so it needs to be dynamic.
(Also: English is not my first language, so please excuse any unclarities).
This is not what you request, but maybe be useful for you. Last column is an array with the structure: {'course_name: course_completed', ...}.
SELECT u.firstname,u.lastname, u.institution as department, u.department as company
array_agg(c.shortname || ': ' || to_char(to_timestamp(p.timecompleted)::date,'YYYY-MM-DD')) AS courses_completed
FROM mdl_course_completions AS p
JOIN mdl_course AS c ON p.course = c.id
JOIN mdl_user AS u ON p.userid = u.id
WHERE c.enablecompletion = 1 AND u.firstname is NOT NULL AND p.timecompleted is NOT NULL
GROUP BY u.firstname,u.lastname, u.institution, u.department
ORDER BY u.firstname
output will be:
firstname lastname department company courses_completed
u1 u1 x x {"c1: date", "c2: date"}
u2 u2 x x {"c1: date", "c2: date"}
Other option is to use crosstab function (like you said): Postgresql crosstab documentation. You have to install tablefunc module to use crosstab. Read the documentation and if you think it could be useful for you I could help you to write the query (but I will need to know the tables structure and some example data).
You can use conditional aggregate, for example by using the FILTER clause:
SELECT
firstname, lastname, department, company,
MAX(completed) FILTER (WHERE course = 'c1') as c1,
MAX(completed) FILTER (WHERE course = 'c2') as c2
FROM
-- <joins>
GROUP BY firstname, lastname, department, company
You need to group by the columns which are part of the same group. Then you can execute any aggregation function you like on the columns you want to pivot. With the appended FILTER clause you can filter all records which should be recognized for the aggregate.

SQL Union of 4 tables

I have 4 different tables:
Address: address_id, postcode.
Patient: address_id, name.
FocusArea: geom.
Postcode: geom, postcode.
I need to find the name of the patients that live within the focus area.
I have managed to get the postcodes where the patients live, and the postcodes within the focus area, but I don't know how to join both queries:
SELECT
air.address.postcode, air.patient.name
FROM
air.address
INNER JOIN
air.patient ON patient.address_id = address.address_id;
SELECT
postcode as postcode
FROM
air.postcode, air.focusarea
WHERE
air.focusarea.objectid = 1
AND ST_Intersects(air.postcode.geom, air.focusarea.geom);
air.focusarea.objectid = 1 as there are different boundaries of the focus area.
Any ideas?
Thank you
Tania
select
a.postcode, p.name
from air.address as a
inner join air.patient as p on
p.address_id = a.address_id
inner join air.postcode as pc on
pc.postcode = a.postcode
inner join air.focusarea as fa on
fa.objectid = 1 and
ST_Intersects(pc.geom, fa.geom);

Get data from 3 database tables

I have 3 tables
Users - columns id, userName,...
Comment - columns id, text, userId, roomId
CommentRate - columns userId, commmentId
CommentRate table contain userId's that liked this comment identified by commentId.
I want to select all comments (id, text, roomId, userId, userName, []ListOfLikes) from Comment table by roomId where []ListOfLikes should contain userId, userName.
You can refer the same table ( USER in this case) more times with different join criteria using aliases, but It is not possible to convert values in field names. This statement will extract all that you need, but repeating rows and not adding columns:
SELECT C.ID, C.TEXT, C.ROOMID, U.USERNAME, U2.USERNAME
FROM COMMENT C
INNER JOIN COMMENTRATE CR ON C.ID = CR.COMMENTID
INNER JOIN USERS U ON C.USERID = U.ID
INNER JOIN USERS U2 ON CR.USERID = U2.ID
WHERE U.USERNAME IN ('username1', 'username2')
ORDER BY C.ROOMID
If you want to filter by users that likes, you could replace U with U2 in the WHERE line of code.

SQL Statement that Subs 0 for no Results

I am having an issue getting the results I want from my SQL statement. I know I'm probably missing something simple but I just can't see it.
Here are my tables.
Table: Users (RoleID is linked to ID in Roles Table)
ID, FirstName, LastName, RoleID
1, Matt, Ryan, 1
2, Chipper, Jones, 1
3, Julio, Jones, 2
4, Jason, Bourn, 3
Table: Roles
ID, Name
1, Field Rep
2, Tech
3, Admin
Table: FRrequests (UserID is linked to ID in Users table)
ID, UserID, Status
1, 1, Open
2, 1, Submitted
3, 1, Delayed
4, 1, Complete
What I want is an SQL statement that shows me a count of all the "Submitted" & "Delayed" requests for all the Field Reps. Below is an example of desired results.
Name Count
Chipper Jones 0
Matt Ryan 2
Here is the statement I have so far and the results it gives me.
SELECT Users.FirstName + ' ' + Users.LastName AS Name, COUNT(FRrequests.ID) AS 'Open Requests'
FROM Users INNER JOIN
Roles ON Users.RoleID = Roles.ID LEFT OUTER JOIN
FRrequests ON Users.ID = FRrequests.UserID
WHERE (Roles.Name = N'Field Rep') AND (FRrequests.Status = 'Submitted' OR FRrequests.Status = 'Delayed')
GROUP BY Users.FirstName, Users.LastName
Name Count
Matt Ryan 2
I know that the "AND (FRrequests.Status = 'Submitted' OR FRrequests.Status = 'Delayed')" part is what is breaking it. If I run it without that in the statement I get all the users but it counts all status not just submitted and delayed. I just can't figure out what I'm missing to get this to work. Any help would be greatly appreciated.
You are really close, try the following:
SELECT U.FirstName + ' ' + U.LastName AS Name, COUNT(F.ID) AS 'Open Requests'
FROM Users U
INNER JOIN Roles R
ON U.RoleID = R.ID
LEFT JOIN ( SELECT * FROM FRrequests
WHERE Status IN ('Submitted','Delayed')) F
ON U.ID = F.UserID
WHERE R.Name = N'Field Rep'
GROUP BY U.FirstName, U.LastName
You need to have a row for each Field Rep if you wish to include Field Reps that have no requests. Therefore you need to use a left outer join along the following lines:
SELECT
u.FirstName || ' ' || u.LastName as Name
, COALESCE(frr.Counter,0) as Count
FROM
Users u
LEFT OUTER JOIN (
SELECT
UserID
,count(*) as Counter
FROM
FRrequests
WHERE
Status IN ('Submitted', 'Delayed')
GROUP BY
UserID
) frr ON frr.UserID = u.ID
, Roles r
WHERE
u.ID = f.UserID
AND u.RoleID = r.ID
AND r.Name = 'Field Rep'
;
A left outer join will provide a join only where a match occurs, so it does not remove rows from the naturally joined set. In this case you will get a list of all Users who are Field Reps, together with a count of their FRrequests with a 'Submitted' or 'Delayed' status if any.
COALESCE is used (in Postgresql at least) to check for a null value and then coerce it to the supplied value, in this case 0. So if a NULL value occurs (because, for instance, there was no 'Counter' value returned from the left outer join) it is replaced by 0.

Join 2 tables based on a single table

I can't seem to get the join / query that I need!
Lets say I have 3 tables (trimmed for this post...)
user_courses
(int) user_id
(int) course_id
users
(int) user_id
(txt) name
(txt) access
courses
(int) course_id
(txt) course_desc
What I am trying to do is select select all users with a certain access type that are taking a specific course (course_id)
something like...
SELECT *
FROM user_courses uc
JOIN users u
ON uc.user_id = u.user_id
JOIN courses c
ON uc.course_id = c.course_id
WHERE u.access = "foobar"
... but working like I want it to :)
I can get close, but will have extra users that don't have the correct access type.
Use inner join.
SELECT *
FROM user_courses uc
INNER JOIN users u
ON uc.user_id = u.user_id
LEFT JOIN courses c
ON uc.course_id = c.course_id
WHERE u.access = "foobar"
perhaps:
select * from users u
where u.access='foobar'
and exists (select 1 from user_courses uc where uc.user_id=u.user_id)
Cheers
Try
...
WHERE ISNULL(u.access, '') = 'foobar'
Not sure if your 'access' field can be null?