SQL JOIN when empty link still show row - sql

I have 2 tables:
patient:
| patientid | name | address |
| 45 | jelle | adres 2 |
| 23 | piet | adres 6 |
| 11 | kees | adres 4 |
agenda:
| agendaid | patientid | datum | time |
| 1 | 45 | 2020-05-12 | 12:05 |
| 2 | 45 | 2020-07-11 | 16:02 |
| 3 | 11 | 2020-02-10 | 10:35 |
I want to get all patients with their upcoming appointment from agenda
I tried to run this query:
SELECT * FROM patient LEFT JOIN agenda ON patient.patientid = agenda.patientid WHERE agenda.datum >= CURDATE()
But this one returns only patients that have an appointment in the future.
I want to receive all patients, even if they don't have an appointment in the future.
The desired result should be this:
| patientid | name | address | agendaid | patientid | datum | time |
| 45 | jelle | adres 2 | 2 | 45 | 2020-07-11 | 16:02 |
| 23 | piet | adres 6 | | | | |
| 11 | kees | adres 4 | 3 | 11 | 2020-02-10 | 10:35 |
Does anyone know how to achieve this?
I created an SQL fiddle for you guys to check out!
http://sqlfiddle.com/#!9/6eecc4/1
As you can see It returns multiple rows for each patient when he has multiple appointments and does not return patients that have no appointments in the future.

You need to move the filtering condition to the ON clause. Otherwise, you turn the outer join into an inner join:
SELECT p.*, a.*
FROM patient p LEFT JOIN
agenda a
ON p.patientid = a.patientid AND a.datum >= CURDATE();

Related

SQL Count depending on certain conditions

I have two tables.
One have userid and email (users table). The other have payments information (payments table) from the userid in users.
users
+--------+------------+
| Userid | Name |
+--------+------------+
| 1 | Alex T |
| 2 | Jeremy T |
| 3 | Frederic A |
+--------+------------+
payments
+--------+-----------+------------+----------+
| Userid | ValuePaid | PaidMonths | Refunded |
+--------+-----------+------------+----------+
| 1 | 1 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 1 | null |
| 2 | 1 | 1 | null |
| 2 | 20 | 12 | 1 |
| 2 | 20 | 12 | null |
| 2 | 20 | 1 | null |
| 3 | 1 | 12 | null |
| 3 | 20 | 1 | 1 |
| 3 | 20 | 1 | null |
+--------+-----------+------------+----------+
I want to count the PaidMonths taking in consideration the following rules:
If ValuePaid < 10 PaidMonths should be = 0.23 (even if in the column the value seen is any other mumber).
If Refund=1 the PaidMonths should be = 0.
Based on this when i join both tables by userid, and sum the PaidMonths based in the previousrules, i expect to see as result:
+--------+------------+------------+
| userid | Name | paidMonths |
+--------+------------+------------+
| 1 | Alex T | 25.23 |
| 2 | Jeremy T | 13.23 |
| 3 | Frederic A | 1.23 |
+--------+------------+------------+
Can you help me to achieve this in the most elegant way? Should a temporary table be used?
The following gives your desired results, using apply with case expression to map your values:
select u.UserID, u.Name, Sum(pm) PaidMonths
from users u join payments p on p.userid=u.userid
cross apply (values(
case
when valuepaid <10 then 0.23
when Refunded=1 then 0
else PaidMonths end
))x(pm)
group by u.UserID, u.Name
See Working Fiddle

Need query for JOIN four tables with some conditions?

I have the following four tables:
1) mls_user
2) mls_category
3) bonus_point
4) mls_entry
In mls_user table values are like below:
*-------------------------*
| id | store_id | name |
*-------------------------*
| 1 | 101 | sandeep |
| 2 | 101 | gagan |
| 3 | 102 | santosh |
| 4 | 103 | manu |
| 5 | 101 | jagveer |
*-------------------------*
In mls_category table values are like below:
*---------------------------------*
| cat_no | store_id | cat_value |
*---------------------------------*
| 20 | 101 | 1 |
| 21 | 101 | 4 |
| 30 | 102 | 1 |
| 31 | 102 | 2 |
| 40 | 103 | 1 |
| 41 | 103 | 1 |
*---------------------------------*
In bonus_point table values are like below:
*-----------------------------------*
| user_id | store_id | bonus_point |
| 1 | 101 | 10 |
| 4 | 101 | 5 |
*-----------------------------------*
In mls_entry table values are like below:
*-------------------------------------------------------*
| user_id | store_id | category | distance | status |
*-------------------------------------------------------*
| 1 | 101 | 20 | 10 | Approved |
| 1 | 101 | 21 | 40 | Approved |
| 1 | 101 | 20 | 10 | Approved |
| 2 | 101 | 20 | 5 | Approved |
| 3 | 102 | 30 | 10 | Approved |
| 3 | 102 | 31 | 80 | Approved |
| 4 | 101 | 20 | 15 | Approved |
*-------------------------------------------------------*
And I want below output:
*--------------------------------------------------*
| user name | Points | bonus Point | Total Point |
*--------------------------------------------------*
| Sandeep | 30 | 10 | 40 |
| Santosh | 30 | 0 | 30 |
| Manu | 15 | 5 | 20 |
| Gagan | 5 | 0 | 5 |
| Jagveer | 0 | 0 | 0 |
*--------------------------------------------------*
I tell the calculation of how the points will come for user Sandeep.
Points = ((10+10)/1 + 40/4)=30
Here 1 and 4 is cat value which comes from mls_category.
I am using below code for a particular user but when i
SELECT sum(t1.totald/c.cat_value) as total_distance
FROM mls_category c
join (
select sum(distance) totald, user_id, category
FROM mls_entry
WHERE user_id=1 AND store_id='101' AND status='approved'
group by user_id, category) t1 on c.cat_no = t1.category
I have created tables in online for checking
DEMO
Computing the points (other than the bonus points) requires a separate join between the mls_entry and mls_category tables. I would do this in a separate subquery, and then join this to the larger query.
Here is one approach:
SELECT
u.name,
COALESCE(t1.points, 0) AS points,
COALESCE(b.bonus_point, 0) AS bonus_points,
COALESCE(t1.points, 0) + COALESCE(b.bonus_point, 0) AS total_points
FROM mls_user u
LEFT JOIN
(
SELECT e.user_id, SUM(e.distance / c.cat_value) AS points
FROM mls_entry e
INNER JOIN mls_category c
ON e.store_id = c.store_id AND e.category = c.cat_no
GROUP BY e.user_id
) t1
ON u.id = t1.user_id
LEFT JOIN bonus_point b
ON u.id = b.user_id
ORDER BY
total_points DESC;
This is the output I am getting from the above query in the demo you setup:
The output does not match exactly, because you have (perhaps) a typo in Santosh's data in your question, or otherwise the expected output in your question has a typo.

Sub query in t sql select statement

I have a query which lists users but it requires a sub query to get whether they are available today. I need the fourth column 'Availability' to iterate through each user and display if they have availability or display a null. I've tried everything I can think of sub queries, cursors etc but no joy. Any pointers welcome!
SELECT inter.authno,
inter.FirstName,
inter.Surname,
COALESCE((
Select at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
full join [database].[dbo].[availability] av
on inter.authno = av.authno
full join [database].[dbo].[availability_days] ad
on av.availID = ad.availID
full join [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where exists(
select authno
from [database].[dbo].[Interviewer]
)
and ad.actualDay = '2015-05-21'
), null ) AS [Availability]
FROM [database].[dbo].[Interviewer] inter
The query gives the below results, but it should only show Available for Harry Kane and the rest should be null.
authno FirstName Surname Availability
-------------------------------------
10 Minch Yoda Available
11 Darth Vadar Available
12 Darth Maul Available
14 Obi Wan Kenobi Available
15 Qui-Gon Jinn Available
16 Darth Sidious Available
17 Boba Fett Available
24 Harry Kane Available
39 mark o'neill Available
I also tried the code suggestion below kindly provided which gives some results I need, but it shows all of the results instead of the availability type for today.
SELECT
inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
left JOIN [database].[dbo].[availability] av
on inter.authno = av.authno
left JOIN [database].[dbo].[availability_days] ad
on av.availID = ad.availID
and ad.actualDay = '2015-07-21'
left JOIN [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
Output:
+----+---------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
+----+---------+-----------+--------------+
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 15 | Qui-Gon | Jinn | Annual Leave |
| 16 | Darth | Sidious | NULL |
| 17 | Boba | Fett | UO |
| 17 | Boba | Fett | Available |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | Available |
| 24 | Harry | Kane | Available |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | NW |
| 24 | Harry | Kane | NW |
| 24 | Harry | Kane | Available |
| 39 | mark | o'neill | US |
+----+---------+-----------+--------------+
I also tried the below which gets me the exact results that I need only that, I need to display all users whether they have a date in the table or not. i.e. If I change the date to last weer Harry Kane disappears.
SELECT
inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
left JOIN [database].[dbo].[availability] av
on inter.authno = av.authno
left JOIN [database].[dbo].[availability_days] ad
on av.availID = ad.availID
left JOIN [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where ad.actualDay = '2015-05-21'or ad.actualDay is null
Output for today:
+--------+-----------+-----------+--------------+
| authno | FirstName | Surname | Availability |
+--------+-----------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 16 | Darth | Sidious | NULL |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
| 24 | Harry | Kane | Available |
+--------+-----------+-----------+--------------+
Output for 2015-05-10
+--------+-----------+-----------+--------------+
| authno | FirstName | Surname | Availability |
+--------+-----------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 16 | Darth | Sidious | NULL |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
+--------+-----------+-----------+--------------+
If I understand correctly, you want a correlated subquery for your version of the query:
SELECT inter.authno,
inter.FirstName,
inter.Surname,
(Select at.typeName as [Availability]
FROM [database].[dbo].[availability] av join
[database].[dbo].[availability_days] ad
on av.availID = ad.availID join
[database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where inter.authno = av.authno and ad.actualDay = '2015-05-21'
) AS [Availability]
FROM [database].[dbo].[Interviewer] inter;
Some notes:
COALESCE(<x>, NULL) doesn't make sense. Just use <X>
With a subquery, you should use IFNULL() rather than COALESCE(), because SQL Server has (what I consider to be) a flawed implementation of COALESCE().
Your subquery needs to be correlated to the outer query.
I have no idea what the EXISTS clause was supposed to do. If the table has any rows, then it would always return TRUE.
There is no reason for full joins in the subquery.
I would expect your version to return the error "subquery returns more than one row".
Without seeing your table structure It's a guess, but an educated one.
Try this:
SELECT inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
LEFT JOIN [database].[dbo].[availability] av on inter.authno = av.authno
LEFT JOIN [database].[dbo].[availability_days] ad on av.availID = ad.availID and ad.actualDay = '2015-05-21'
LEFT JOIN [database].[dbo].[availibiltyType] at on av.typeID = at.typeid
For future sql questions you might have, Please include the relevant tables DDL, some sample data (preferably as DML statements), and the desired output.

Get values from SQL table for each item in table variable and return null if item doesn't exist

I've got 3 tables, a results table, an orders table and a table variable with result codes in.
The table formats are something like as followed:
Results
| Patient_Order_ID | Result_Code | Result_Value | HospId |
|------------------|-------------|--------------|--------|
| 1 | WCC | 10 | 123 |
| 1 | RCC | 8 | 123 |
| 1 | HGB | 13 | 123 |
| 2 | WCC | 11 | 123 |
| 2 | RCC | 9 | 123 |
| 3 | HGB | 10 | 123 |
| 3 | RCC | 10 | 123 |
Orders
| Patient_Order_ID | Date_Collect |
|------------------|--------------|
| 1 | 2014-01-01 |
| 2 | 2014-01-05 |
| 3 | 2014-01-10 |
Codes
| Result_Code |
|-------------|
| WCC |
| RCC |
| HGB |
I want to return the data from the results table for each result code in the result codes table, and return null if a particular code doesn't exist. Something like this:
| Result Code | Result_Value | Date_Collect |
|-------------|--------------|--------------|
| WCC | 10 | 2014-01-01 |
| RCC | 8 | 2014-01-01 |
| HGB | 13 | 2014-01-01 |
| WCC | 10 | 2014-01-05 |
| RCC | 8 | 2014-01-05 |
| HGB | NULL | 2014-01-05 |
| WCC | NULL | 2014-01-10 |
| RCC | 10 | 2014-01-10 |
| HGB | 13 | 2014-01-10 |
If possible, I'd like them to be sorted in the order in which they appear in the codes table.
So far, I've written the following T-SQL:
SELECT
codes.Result_Code
, results.Result_Value
, orders.Date_Collect
FROM
#resultCodes codes
LEFT OUTER JOIN Results results ON codes.Result_Code = results.Result_code
INNER JOIN Orders orders ON results.Patient_Order_ID = orders.Patient_Order_ID
WHERE
results.Hospid = #prmHospid
ORDER BY
orders.Date_Collect DESC
This returns the results almost in the format I want, but doesn't include the null values where needed and doesn't secondary sort on result codes. Any help would be really appreciated.
I'm using SQL Server 2008.
Try a FULL OUTER JOIN between Codes and Orders
SQL Fiddle : http://sqlfiddle.com/#!3/99321/13
SELECT
codes.Result_Code
, results.Result_Value
, orders.Date_Collect
FROM
#resultCodes codes
FULL OUTER JOIN Orders orders ON 1=1
LEFT OUTER JOIN Results results ON codes.Result_Code = results.Result_code AND results.Hospid = 123 AND orders.Patient_Order_ID=results.Patient_Order_ID
ORDER BY
orders.Date_Collect, codes.Result_Code DESC
Note : I have wrote the code to match the desired output and thus the ordering is different from your code

MS Access SQL query from 3 tables

I have 3 tables shown below in MS Access 2010:
Table: devices
id | device_id | Company | Version | Revision |
-----------------------------------------------
1 | dev_a | Almaras | 1.5.1 | 0.2A |
2 | dev_b | Enigma | 1.5.1 | 0.2A |
3 | dev_c | Almaras | 1.5.1 | 0.2C |
*Field: device_id is Primary Key Unique String
*Field ID is just an auto-number column
Table: activities
id | act_id | act_date | act_type | act_note |
------------------------------------------------
1 | dev_a | 07/22/2013 | usb_axc | ok |
2 | dev_a | 07/23/2013 | usb_axe | ok | (LAST ROW for dev_a)
3 | dev_c | 07/22/2013 | usb_axc | ok | (LAST ROW for dev_c)
4 | dev_b | 07/21/2013 | usb_axc | ok | (LAST ROW for dev_b)
*Field: act_id contains device_id; NOT UNIQUE
*Field ID is just an auto-number column
Table: matrix
id | mat_id | tc | ts | bat | cycles |
-----------------------------------------
1 | dev_a | 2811 | 10 | 99 | 200 |
2 | dev_a | 2911 | 10 | 97 | 400 |
3 | dev_a | 3007 | 10 | 94 | 600 |
4 | dev_a | 3210 | 10 | 92 | 800 | (LAST ROW for dev_d)
5 | dev_b | 1100 | 5 | 98 | 100 |
6 | dev_b | 1300 | 8 | 93 | 200 |
7 | dev_b | 1411 | 11 | 90 | 300 | (LAST ROW for dev_b)
8 | dev_c | 4000 | 27 | 77 | 478 | (LAST ROW for dev_c)
*Field: mat_id contains device_id; NOT UNIQUE
*Field ID is just an auto-number column
Is there any way to query tables to get results as shown below (each device from devices and only last row added [see example output table] from each of the other two tables):
Query Results:
device_id | Company | act_date | act_type | bat | cycles |
------------------------------------------------------------
device_a | Almaras | 07/23/2013 | usb_axe | 92 | 800 |
device_b | Enigma | 07/21/2013 | usb_axc | 90 | 300 |
device_c | Almaras | 07/22/2013 | usb_axc | 77 | 478 |
Any ideas? Thank you in advance for reading and helping me out :)
I think is what you want,
SELECT a.device_id, a.Company,
b.act_date, b.act_type,
c.bat, c.cycles
FROM ((((devices AS a
INNER JOIN activities AS b
ON a.device_id = b.act_id)
INNER JOIN matrix AS c
ON a.device_id = c.mat_id)
INNER JOIN
(
SELECT act_id, MAX(act_date) AS max_date
FROM activities
GROUP BY act_id
) AS d ON b.act_id = d.act_id AND b.act_date = d.max_date)
INNER JOIN
(
SELECT mat_id, MAX(tc) AS max_tc
FROM matrix
GROUP BY mat_id
) AS e ON c.mat_id = e.mat_id AND c.tc = e.max_tc)
The subqueries: d and e separately gets the latest row for every act_id.
Try
SELECT devices.device_id, devices.Company, activities.act_data, activities.act_type, matrix.bat, matrix.cycles
FROM devices
LEFT JOIN activities
ON devices.device_id = activities.act_id
LEFT JOIN matrix
ON devices.device_id = matrix.mat_id;
What do you consider the "last" row in Matrix?
You need to do something like
WHERE act_date in (SELECT max(a.act_date) from activities a where a.mat_id=d.device_id GROUP BY a.mat_id)
and something similar for the join to matrix.