How to show multiple columns in a single column in SQL? - sql

I have a table attendee, when joining table attendee with table Session, attendee can attend many sessions. So when showing the attendees and what sessions they attended, I am getting multiple rows of the same Attendee, example :
John Doe-BMW Conference
John Doe-Blockchain conference
Jane Doe-blabla
John Doe- Mercedess
Is there a way in sql to display
John Doe- BMW Conference,Mercedes,Block chain?
SELECT Distinct
attendee.Id, attendee.Firstname, attendee.PhoneNumber,
attendee.Email,attendee.Town, attendee.BloodType, session.Id ,
session.LocationId , session.Name as SessionName , location.Id ,
location.Name as Location_Name , sessionattended.SessionAttendedId,
sessionattended.SessionId, sessionattended.AttendeeId,
attendee.Lastname
FROM `session`, `attendee`, `sessionattended`, `location`
WHERE attendee.Id = sessionattended.AttendeeId
and session.Id = sessionattended.SessionId
and session.LocationId = location.Id;

use GROUP_CONCAT MySQL (available since a long time)
or STRING_AGG MSSQL (available in version 2017 and later)
for a comparison of both, see: https://database.guide/mysql-group_concat-vs-t-sql-string_agg/
Google will find info on how to do this in other DBMS's, or even how to do it 'the old way', like here: GROUP BY to combine/concat a column

Try this. Basically the idea is to use FOR XML PATH for this type of result.
SELECT Distinct
a.Id, a.Firstname, a.PhoneNumber, a.Email,a.Town, a.BloodType,
all_conferences = STUFF((SELECT ',' + CAST(s1.Name as Varchar)
FROM sessionattended sa1
JOIN Session s1 ON sa1.SessionId = s1.Id
WHERE sa1.AttendeeId = sa.AttendeeId FOR XML PATH('') ), 1, 1, '')
FROM `session` s
JOIN `sessionattended` sa ON sa.SessionId = s.Id
JOIN `attendee` a ON a.Id = sa.AttendeeId
JOIN `location` l ON s.LocationId = l.Id

Related

Many Duplicates, caused by a phone number column. Need to cut down duplicates

See query below returning approx 38K rows. When 'phone' join and column are removed, it cuts down to the correct 15.5K rows.
SELECT
tc.customer_no
,fdn.display_name_short 'name'
,tc.cont_amt
,tc.ref_no
,tc.cont_dt
,tc.cont_type
,tca.fyear
,(ISNULL(street1, 'none') + ' ' + ISNULL(city, 'none') + ' ' + ISNULL(state, 'none')
+ ', ' + ISNULL(postal_code, 'none')) 'address'
,ISNULL(tp.phone, 'none')
,ISNULL(te.address, 'none')
FROM T_CONTRIBUTION tc
JOIN FT_CONSTITUENT_DISPLAY_NAME() fdn
ON tc.customer_no = fdn.customer_no
JOIN T_CAMPAIGN tca
ON tc.campaign_no = tca.campaign_no
LEFT JOIN T_ADDRESS ta
ON tc.customer_no = ta.customer_no AND ta.primary_ind = 'y'
LEFT JOIN T_EADDRESS te
ON tc.customer_no = te.customer_no AND te.primary_ind = 'y'
LEFT JOIN T_PHONE tp
ON tc.customer_no = tp.customer_no
WHERE tca.fyear BETWEEN 2018 AND 2022
AND tc.cont_amt > 0
AND te.inactive = 'N'
AND ta.inactive = 'N'
Any advice as to how i can include the phone number column, while eliminating as many duplicates as possible? I don't have to be highly precise with this query, but need to get the row count down as much as possible. The phone table has about 50 different phone types (ex. 1,2,or 22), and the PK is the phone number. The DB has since moved to using only phone type 1 or 2, but i am searching 4 yrs back which is before they switched to only using two different phone types.
Followed suggestions in comments, ended up with:
CTE to create numbered and grouped rows
WITH cte AS (
SELECT customer_no, phone
, row_number() OVER(PARTITION BY customer_no ORDER BY phone) AS rn
FROM T_PHONE
)
Then referenced said cte in the main query's select.
Finally added
WHERE cte.rn = 1
Which selected the first phone number at random, in each group of customer's phones numbers.

How to query all values from a column in one table that have all values in another column in a different table?

I'm trying to get all the Hikers that traveled in all treks in Nepal.
My tables in the data base are:
Hiker: (hikerID, fullName).
HikerInTrek:(hikerID, trekName).
NepalTreks: (trekName).
I tried the following code:
SELECT fullName
FROM Hiker
WHERE hikerID = (
SELECT hikerID
FROM hikerInTrek
WHERE hikerID not in( SELECT hikerID
FROM ( ( SELECT hikerID, trekName
FROM (SELECT hikerID
FROM hikerInTrek)
cross join
(SELECT trekName
FROM NepalTreks)
)
EXCEPT
( SELECT hikerID, trekName
FROM hikerInTrek
)
)
)
);
I'm working on a website part of a homework and I've wrote this code in the PHP, but I got an error from the "if($queryResult === false) condition after the query. So I guess the query is not correct.
I would use aggregation:
SELECT h.hikerID, h.fullName
FROM Hiker h JOIN
HikerInTrek ht
ON h.hikerID = ht.hikerID JOIN
Nepal_Treks nt
ON nt.TrekName = ht.TrekName
GROUP BY h.hikerID, h.fullName
HAVING COUNT(DISTINCT nt.TrekName) = (SELECT COUNT(*) FROM Nepal_Treks);
Note that this includes hikerID as well. I see no reason to assume that hikers never share the same name.
Depending on how your trekName column is set up, and depending on the SQL dialect it could be something like..
SELECT fullName
FROM Hiker h
JOIN HikerInTrek ht on h.hikerID = ht.hikerID
JOIN Nepal_Treks nt on nt.TrekName = ht.TrekName
Although I'd restructure my tables to have a format like the below (using sql naming conventions)
Hiker
- id
- full_name
HikerTrek
- id
- hiker_id
- trek_id
Trek
- id
- name
- location_id
TrekLocation
- id
- name
Create a row in Hiker for each hiker
Create a row in TrekLocation for Nepal,
Create a row in Trek for each Trek, with location id the one for Nepal
Create a row in HikerTrek for each trek a hiker has been on,
Then you can query it like this
SELECT h.full_name
FROM Hiker h
JOIN HikerTrek ht on h.id = ht.hiker_id
JOIN Trek t on ht.trek_id = t.id
JOIN TrekLocation tl on t.location_id = tl.id
WHERE tl.name = "Nepal"
Hope this clears things up a little.
Try this.
SELECT
A.fullName
FROM
Hiker A
INNER JOIN
HikerInTrek B ON B.hikerID = A.hikerID
WHERE
B.trekName = 'Nepal'

Invalid Column name while running a query

I am new to SQL and I don't know what's wrong in this query,
SELECT
wo.WORKORDERID "Request ID", (wo.CREATEDTIME) "Created on",
aau.FIRST_NAME "Requester", aac.EMAILID 'From',
[To] = STUFF((SELECT ', ' + Recipient_email
FROM workorder_recipients wor2
WHERE wor2.Workorderid = wor.Workorderid and wor2.To_cc_bcc='To'
FOR XML PATH('')), 1, 2, ''),
[CC] = STUFF((SELECT ', ' + Recipient_email
FROM workorder_recipients wor2
WHERE wor2.Workorderid = wor.Workorderid and wor2.To_cc_bcc='CC'
FOR XML PATH('')), 1, 2, ''),
cd.CATEGORYNAME "Category"
FROM
workorder_recipients wor
LEFT JOIN
workorder wo ON wor.workorderid = wo.workorderid
LEFT JOIN
ModeDefinition mdd ON wo.MODEID = mdd.MODEID
LEFT JOIN
SDUser sdu ON wo.REQUESTERID = sdu.USERID
LEFT JOIN
AaaUser aau ON sdu.USERID = aau.USER_ID
LEFT JOIN
SDUser crd ON wo.CREATEDBYID = crd.USERID
LEFT JOIN
AaaUser cri ON crd.USERID = cri.USER_ID
LEFT JOIN
AaaUserContactInfo aauc ON aau.USER_ID = aauc.USER_ID
LEFT JOIN
AaaContactInfo aac ON aauc.CONTACTINFO_ID = aac.CONTACTINFO_ID
LEFT JOIN
WorkOrderStates wos ON wo.WORKORDERID = wos.WORKORDERID
LEFT JOIN
CategoryDefinition cd ON wos.CATEGORYID = cd.CATEGORYID
WHERE
mdd.MODENAME = 'E-Mail'
AND cd.CATEGORYNAME in ('Agent Operational Technology (EMEA/UK/IE)','Client Technology')
AND wo.IS_CATALOG_TEMPLATE='0'
AND wo.CREATEDTIME >= 1416783600000
AND wo.CREATEDTIME <= 1417388399000
AND wo.ISPARENT='1'
GROUP BY
wo.workorderid
But I keep getting this error:
Column 'workorder_recipients.WORKORDERID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Thanks,
Atul
Imagine the following simple table (T) where ID is the primary key:
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
2 | A | Y |
Then you write the following query
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1;
This breaks the SQL Standard, and if it were to run without errors (which it would in MySQL), the result:
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
Is no more or less correct than
ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
So what you are saying is give me one row for each distinct value of Column1, which both results sets satisfy, so how do you know which one you will get? Well you don't.
For simplicity sake (and the way it is implemented in SQL Server) we state the rule that if an column is not contained in an aggregate function, it must be in the GROUP BY clause for it to appear in the select list. This is not strictly true, the SQL-Standard does allow columns in the select list not contained in the GROUP BY or an aggregate function, however these columns must be functionally dependent on a column in the GROUP BY. From the SQL-2003-Standard (5WD-02-Foundation-2003-09 - page 346) - http://www.wiscorp.com/sql_2003_standard.zip
15) If T is a grouped table, then let G be the set of grouping columns of T. In each contained
in , each column reference that references a column of T shall reference some column C that
is functionally dependent on G or shall be contained in an aggregated argument of a
whose aggregation query is QS.
For example, ID in the sample table is the PRIMARY KEY, so we know it is unique in the table, so the following query conforms to the SQL standard and would run in MySQL and fail in many DBMS currently (At the time of writing Postgresql is the closest DBMS I know of to correctly implementing the standard - Example here):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
Since ID is unique for each row, there can only be one value of Column1 for each ID, one value of Column2 there is no ambiguity about
what to return for each row. As far as I know, Postgresql is the only DBMS that has gone anyway to implementing this.
In order for your query to work you would need to add some columns to the GROUP BY:
GROUP BY wo.workorderid, wo.CREATEDTIME, aau.FIRST_NAME, aac.EMAILID, cd.CATEGORYNAME
However, I think you can remove the issue of duplicates by removing workorder_recipients from your FROM, you don't appear to use this anywhere. Removing this reference should remove the need for GROUP BY
SELECT
[Request ID] = wo.WORKORDERID,
[Created on] = wo.CREATEDTIME,
[Requester] = aau.FIRST_NAME,
[From] = aac.EMAILID,
[To] = STUFF((SELECT ', ' + Recipient_email
FROM workorder_recipients wor2
WHERE wor2.Workorderid = wo.Workorderid
AND wor2.To_cc_bcc='To'
FOR XML PATH('')), 1, 2, ''),
[CC] = STUFF((SELECT ', ' + Recipient_email
FROM workorder_recipients wor2
WHERE wor2.Workorderid = wo.Workorderid
AND wor2.To_cc_bcc='CC'
FOR XML PATH('')), 1, 2, ''),
[Category] = cd.CATEGORYNAME
FROM workorder wo
LEFT JOIN ModeDefinition AS mdd
ON wo.MODEID = mdd.MODEID
LEFT JOIN SDUser AS sdu
ON wo.REQUESTERID = sdu.USERID
LEFT JOIN AaaUser AS aau
ON sdu.USERID = aau.USER_ID
LEFT JOIN SDUser AS crd
ON wo.CREATEDBYID = crd.USERID
LEFT JOIN AaaUser AS cri
ON crd.USERID = cri.USER_ID
LEFT JOIN AaaUserContactInfo AS aauc
ON aau.USER_ID = aauc.USER_ID
LEFT JOIN AaaContactInfo AS aac
ON aauc.CONTACTINFO_ID = aac.CONTACTINFO_ID
LEFT JOIN WorkOrderStates AS wos
ON wo.WORKORDERID = wos.WORKORDERID
LEFT JOIN CategoryDefinition AS cd
ON wos.CATEGORYID = cd.CATEGORYID
WHERE
mdd.MODENAME = 'E-Mail'
AND cd.CATEGORYNAME in ('Agent Operational Technology (EMEA/UK/IE)','Client Technology')
AND wo.IS_CATALOG_TEMPLATE='0'
AND wo.CREATEDTIME >= 1416783600000
AND wo.CREATEDTIME <= 1417388399000
AND wo.ISPARENT='1';
when you use GROUP BY in a query, you need to include every field in the group by which is in the select, except ones where you're aggregating - such as a SUM a MIN or a MAX (Amongst others).
So, to contrive an example, this would be invalid:
SELECT FirstName, LastName, SUM(Score)
FROM HighScores
GROUP BY FirstName
You would also need to include LastName in the GROUP BY to get the sum of a person's scores

SQL Join then combine columns from the result

So I have two db2 tables. One contains work order information like id, requester name, user, description, etc. Second table that has notes, which is keyed to the id of the other table. The notes field is a 255 text field (Yeah don't suggest changing it, I have no control over it). So there could be multiple results, or none, in the note field depending on obviously how many notes there are.
I have a query which fetches the results. The problem is that I am getting multiple results form the join because there are multiple entries.
So my question is how do I concat/merge the results from the notes table into one field for every result? Thanks
Code:
SELECT
p.ABAANB AS WO_NUMBER,
p.ABAJTX AS Description,
i.AIAKTX as Notes
FROM
htedta.WFABCPP p LEFT JOIN HTEDTA.WFAICPP i
ON i.AIAANB = p.ABAANB
WHERE
p.ABABCD = 'ISST' AND p.ABAFD8 = 0
Have you tried LISTAGG
https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/listagg?lang=en
It will allow you to merge all those pesky fields that are causing the additional records... Something like this...
SELECT p.ABAANB AS WO_NUMBER, p.ABAJTX AS Description, LISTAGG(i.AIAKTX, ' ') as Notes
FROM htedta.WFABCPP p
LEFT JOIN HTEDTA.WFAICPP i
ON i.AIAANB = p.ABAANB
WHERE p.ABABCD = 'ISST'
AND p.ABAFD8 = 0
GROUP BY p.ABAAMB, p.ABAJTX
What version of DB2 are you on? If you're using DB2 Linux/Unix/Windows (LUW), then this should work for you:
SELECT p.ABAANB AS WO_NUMBER,
p.ABAJTX AS Description,
,SUBSTR(
xmlserialize(
xmlagg(
xmltext(
concat(',' , TRIM(i.AIAKTX))
)
) AS VARCHAR(4000)
)
,2) AS NOTES
FROM htedta.WFABCPP p
LEFT JOIN HTEDTA.WFAICPP i
ON i.AIAANB = p.ABAANB
WHERE p.ABABCD = 'ISST'
AND p.ABAFD8 = 0
GROUP BY p.ABAANB,
p.ABAJTX
If you are running DB2 at least 9.7, you should be able to use the XMLAGG function similar to this
WITH CombinedNotes( aiaanb, aiaktx) AS (
SELECT aiaanb
, REPLACE( REPLACE(
CAST( XML2CLOB(
XMLAGG( XMLELEMENT(
NAME 'A'
, aiaktx
))
) AS VARCHAR( 3000))
, '<A>',''), '</A>', '')
FROM Htedta.WfaIcpp)
SELECT p.ABAANB AS WO_NUMBER, p.ABAJTX AS Description, i.AIAKTX as Notes
FROM htedta.WFABCPP p
LEFT JOIN CombinedNotes i
ON i.AIAANB = p.ABAANB
WHERE p.ababcd = 'ISST'
AND p.abafd8 = 0;
If you have an earlier version of DB2, or your login for some reason does not permit the XML functions, you can solve the problem with a recursive query. Both techniques are described in the following blog
http://ibmmainframes.com/about44805.html

SQL join statement returned unexpected records

I ran the script below. There is no record in UserDetails.User# that mataches AccountDetails.User#. But the results show Every user# in UserDetails 3 times with each AccountDetails.account# specified in the script.
When there is no match in the SQL join statement, should it not return anything?
little background on the DataType and sample values:
UserDetails.user#
datatype = varchar(520)
values in (1,0)
AccountDetails.user#
datatype = varchar(11)
values in (22054414,28581057,26178648)
The script I ran:
SELECT a.account# , a.userNum , u.userNum, u.Name
FROM AccountDetails a
JOIN UserDetails u ON a.userNum = u.userNum
WHERE a.account# IN (87173000,11900008,79000082)
Sample results:
account#, a.userNum, u.userNum , u.Name
87173000, 22054414, 1, Joe Blow
87173000, 28581057, 1, John Smith
11900008, 22054414, 1, Joe Blow
11900008, 28581057, 1, John Smith
79000082, 22054414, 1, Joe Blow
79000082, 28581057, 1, John Smith
SELECT a.account , a.user , u.Name
FROM AccountDetails a
JOIN UserDetails u ON (a.user = u.user)
WHERE a.account IN (87173000,11900008,79000082)
GROUP BY a.user