Get an array in a field after a JOIN - sql

Let's say that I have a table called Appointments, another called Clients and a joining table called ClientsAppointments which relates the two aforementioned ones. This is used in a scenario where a client can have several appointments, and several clients can attend to the same appointment (n-n relation).
I would like to list the Appointments with a field "clients" being an array of all the Clients related to that appointment. What I've tried so far:
SELECT * FROM Appointments a
INNER JOIN ClientsAppointments ca ON ca.IdAppointment = a.IdAppointment
INNER JOIN Clients c ON c.IdClient = ca.IdClient
That doesn't work, of course. It gives me a list of appointments repeated with each of the clients they have. Then in PHP I would process them to achieve this. It seemed more efficient this way rather than making multiple queries because there's usually only one client per appointment.
Table schema (these are not the actual tables, but simplified to illustrate the case):
Appointment(
INT idAppointment,
DATETIME start,
DATETIME end)
Clients(
INT idClient,
VARCHAR name)
ClientsAppointments(
INT idAppointment,
INT idClient)

If you use MySQL as a database try to use GROUP_CONCAT:
SELECT a.IdAppointment,GROUP_CONCAT(CAST(ca.IdClient AS CHAR) SEPARATOR ',') FROM Appointments a
INNER JOIN ClientsAppointments ca ON ca.IdAppointment = a.IdAppointment
group by a.IdAppointment

Try to use LEFT OUTER JOIN
SELECT a.*,c.Name FROM ClientsAppointments ca
LEFT OUTER JOIN Appointments a ON ca.IdAppointment = a.IdAppointment
LEFT OUTER JOIN Clients c ON ca.IdClient = c.IdClient

Related

On an SQL Select, how do i avoid getting 0 results if I want to query for optional data in another table?

I have a table with Customers which includes their contact person in the helpdesk. I have another table that lists all vacancies of the helpdesk employees - if they are currently sick or on vacation etc.
I need to get the helpdesk contact and the start/end time of their vacation IF there is an entry.
I currently have this (simplified):
SELECT *
FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND dbo.Projects.HDContactID = dbo.Vacations.HDContactID
So if there is a vacation listed in the Vacations table, it works fine, but if there is no vacation at all, this will not return anything - what i want is that if there is no vacation, it simply returns the other data, and ignores the missing data (returns NULL, doesn't return anything, not important)
In any case, I need to get the Customers and Project data, even if the query can't find an entry in the Vacations table. How would I do this? I pretty new to SQL and couldn't find a similar question on this site
EDIT: I'm using SQL Server, currently using HeidiSQL
Try below query:
SELECT * FROM dbo.Customers, dbo.Projects
left join dbo.Vacations on dbo.Projects.HDContactID = dbo.Vacations.HDContactID
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
Use left join as mentioned by #Flying Thunder,
Example of the left join:
SELECT country.country_name_eng, city.city_name, customer.customer_name
FROM customer
LEFT JOIN city ON customer.city_id = city.id
LEFT JOIN country ON city.country_id = country.id;
You can find a nice guide for the joins and SQL here:
https://www.sqlshack.com/learn-sql-join-multiple-tables/
You should be using LEFT JOIN. In fact, you should never be using commas in the FROM clause. That is just archaic syntax and closes the powerful world of JOINs from your queries.
I also recommend using table aliases that are abbreviations of table names. The best are abbreviations for the table names:
SELECT *
FROM dbo.Customers c LEFT JOIN
dbo.Projects p
ON c.CustomerID = p.CustomerID LEFT JOIN
dbo.Vacations v
ON p.HDContactID = v.HDContactID
WHERE c.Phone = $Phone;
Have you try this to skip vacation record if not present like this:
SELECT * FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND (dbo.Vacations.HDContactID IS NULL OR dbo.Projects.HDContactID = dbo.Vacations.HDContactID)

Explain what means to join same table twice

I was preparing for exam and I have this exercise that I don't understand
I have table of Clients that have ClientID,
and also I have table of Transactions that have Foreign Key referenced to Clients, SenderID and RecieverID (refering to ClientID)
I need to create view that will show Transactions with Sender name and Reciever Name, and I did it but I don't understand how it works and why
Here is code:
SELECT CS.Name [SenderName], CR.Name [RecieverName]
FROM Transactions T
INNER JOIN Clients CS
ON CS.ClientID = T.SenderID
INNER JOIN Clients CR
ON CR.ClientID = T.RecieverID
Each time you need the name (for sender or recevier ) you need a relation based on the key between the the transaction table and the clients table
you need the name of the sender ( first join with Clients ) and the name for recevier ( second join with Clients )
for avoid confusion between the two (same name) table you need an alias that let you join the specific related tabe .. you use CS and CR as table nale alias
in this way is as you work with two differente table name (or with a logical duplication of the same table)
SELECT CS.Name [SenderName], CR.Name [RecieverName]
FROM Transactions T
INNER JOIN Clients CS ON CS.ClientID = T.SenderID
INNER JOIN Clients CR ON CR.ClientID = T.RecieverID
You can thinks at the table content as a set of data ..so you use two time the same set of data extracting the row mathcing your relation each time.
Each row in the table Transactions contains:
a SenderID which points to a row in the table Clients and
a RecieverID which points to another row in the table Clients.
So you must make one join of Transactions to Clients using SenderID to get the sender's name and another join to Clients using RecieverID to get the reciever's name.

SQL: Showing data from two tables together

I'm just starting in the SQL world, so I have a very noob question:
I have 2 tables:
clients (columns: client_id and name)
accounts (columns: account_id and client_id)
and I need to write a query that shows the accounts of all the clients.
But, the problem is that not all the clients have accounts, if the client doesn't have one: how can I show the client_id, the name and NULL for the account_id column?
This query should work:
SELECT *
FROM accounts
LEFT [OUTER] JOIN clients
ON accounts.client_id = clients.client_id;
if not try this one:
SELECT *
FROM accounts
LEFT [OUTER] JOIN clients
ON accounts.client_id = clients.client_id WHERE clients.client_id IS NOT NULL;
These are plain SQL queries, I mean they are not PL-SQL specific. LEFT [OUTER] JOIN will only returns the columns of accounts table. [OUTER] keyword is optional, it defers from database version to version. ON accounts.client_id = clients.client_id will match client_id columns in both tables. Lastly, WHERE clients.client_id IS NOT NULL part should prevent the rows with NULL values in client_id cells.
Useful link: https://www.techonthenet.com/oracle/joins.php
Try this query it returns the clients name client id and shows null to those client who has no accountid.
select clients.name, accounts.account_id from accounts left join clients on
accounts.clintid=clients.client_id

SQL for many to one to many table

I have three tables in an Access database that I am using in java via ucanaccess.
Patients (PK Pt_ID)
Endoscopy (PK Endo_ID, FK Pt_ID)
Histology (PK Histol_ID, FK Pt_ID)
1 patient can have many endoscopies
1 patient can have many histologies
Endoscopy and histology are not related
I want to retrieve all the Endoscopies and histologies for a single patients in a single SQL query. Although I can write select statements for two tables I don't know how to do this across the three tables. Is it something like this
Select *.Endoscopy,*.Histology from Patients INNER JOIN Endoscopy, Histology ON Patient.Pt_Id=Endoscopy.Pt_ID, Patient.Pt_Id=Histology.Pt_ID
I'm sure that's a mess though...
What kind of SQL DB are you using?
I believe this works on most.
SELECT * FROM Patients, Endoscopy, Histology
WHERE Patient.Pt_Id=Endoscopy.Pt_ID
AND Patient.Pt_Id=Histology.Pt_ID
Also, I belive you have these switched around *.Endoscopy,*.Histology If you need to use that it should be Endoscopy.*, Histology.*
You can use the following query to select both endoscopies and histologies :
SELECT p.Pt_ID
, e.Endo_ID
, h.Histol_ID
FROM Patients p
INNER JOIN Endoscopy e ON p.Pt_Id = e.Pt_ID
INNER JOIN Histology h ON p.Pt_Id = h.Pt_ID
But I'm not sure this is really what you want. You might need to map the tables Patients, Endoscopy and Histology into Java classes ? In this case, you can consider the Java Persistence API (JPA). It helps you to handle these tables in your java code. Here is a JPA Quick Guide.
First idea is to use inner join (with correct syntax) but this is wrong. inner join returns patients who have both procedure. Pure left join returns additionally patients who have none. So this is the solution:
SELECT Patients.Pt_PK, Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
--exclude patients who don't have any
where coalesce(Endoscopy.Endo_ID, Histology.Histol_ID) is not null
If you have multiple Endoscopy records or multiple Histology records for the same Patient then you will receive duplicate/repeated records in your SELECT. I do no think there is a way around that unless you use 2 SELECT statements instead of 1.
SELECT Endoscopy.*, Histology.*
FROM Patients
INNER JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
INNER JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
To select all records on a table in the select its table name/table alias .*
INNER JOIN will only select records where there is a relationship, once one of these tables does not contain a Pt_ID where it is contained in any one of the other tables then no record will be displayed with that Pt_ID
To add additional tables continue to add additional join statements
You used Patients (with S) in one location and Patient (no S) in another, make sure you use the correct naming. I am guessing its Patients but maybe its Patient.
This statement does almost the same as the above but uses LEFT JOIN syntax so that you will always get records for both tables even if one of the two tables does not have a record for a patient.
SELECT Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
WHERE Histology.Histol_ID IS NOT NULL OR Endoscopy.Endo_ID IS NOT NULL
The added WHERE clause ensures that you do not get a record with all NULL values where there is a patient but no records in either of those tables.

New to SQL. Query SQL database using info across three tables

This is using phpMyAdmin.
I need to find the contact information for Subscribers who have pending Orders on November 15th. Their contact information is stored in a table called Subscribers, and the primary key is UID (User ID). The Subscriptions Table has a primary key called SID (Subscriptions ID). The Subscriptions table also stores the UID for each Subscription. However, the Orders table is where the Date is stored, and this table stores the SID but not the UID, so I can't directly JOIN Orders with Subscribers.
I have to JOIN Orders with Subscriptions on SID where the Orders Date is 11-15-10, and then I have to JOIN the resulting table with the Subscribers table on UID.
I'm currently trying this:
SELECT * FROM Subscribers
RIGHT JOIN (Orders a, Subscriptions b, Subscribers c)
ON (a.SID = b.SID AND b.UID = c.UID)
WHERE a.Date = '2010-11-01'
This is causing a massive lag followed by Gateway Timeout.
This is a classic case of knowing what to do, but not knowing how to do it. Any help would be greatly appreciated. Thanks!
You could try this:
SELECT
scrb.*
FROM
Subscribers scrb
WHERE
scrb.UID in (
SELECT DISTINCT
scrp.UID
FROM
Subscriptions scrp
INNER JOIN Orders ordr ON
ordr.SID = scrp.SID
WHERE
ordr.Date = STR_TO_DATE('2010-11-01')
)
Not sure if you're going to have a big performance improvement though... Maybe your tables miss a better indexing strategy...?
In fact, you should try executing just the inner query (SELECT DISTINCT scrp.UID...) first... If it is too slow, I would guess your problem is on the Orders.Date field - a full scan over that table probably has a high performance cost.
Why do you join Subscribers to Subscribers?
SELECT * FROM Subscribers ... JOIN ... Subscribers c)
Given the limited amount we know about your schema, it seems like you'd do better with an INNER JOIN, which will filter records for you, and #seriyPS is right about the redundant Subscribers table - currently, this query as written is performing a CROSS JOIN, joining all Subscribers to every result of Subscriber joined to Subscription joined to Order...
Is there a reason why this won't work?
SELECT a.*
FROM Subscribers a
INNER JOIN Subscriptions b ON a.UID = b.UID
INNER JOIN Orders c ON b.SID = c.SID
WHERE c.Date = '2010-11-01'