Left join not producing correct result - sql

I want to return all people regardless of whether they have a sale on any given day. My initial response to solve this would be
SELECT
P.[Name], P.[id],
F.[DATE], F.[Figure], F.[id]
FROM
Person P
LEFT JOIN
Figure F ON P.id = F.id
An example of the result for this is
Name id DATE Figure
--------------------------------------------
Tom 1 2017-09-27 15
Fred 2 2017-09-27 20
Jane 3 2017-09-25 0
Ben 4 2017-09-25 0
Now as soon as I limit the date to today's date, I get nothing.
SELECT
P.[Name], P.[id],
F.[DATE], F.[Figure], F.[id]
FROM
Person P
LEFT JOIN
Figure F ON P.id = F.id
WHERE
F.Date = GETDATE()
I understand this is probably because there is no one with today's date attached to them but how do I still display all of the people even if they had nothing for today? I thought that a left join would accomplish this but it doesn't.
My desired results would be :
Name id DATE Figure
--------------------------------------------
Tom 1 2017-10-02 0
Fred 2 2017-10-02 0
Jane 3 2017-10-02 0
Ben 4 2017-10-02 0
There are no keys in the tables so I feel as though that might be why the left join isn't working as I expected but has anyone got any ideas how I can get the desired result?

First, the condition needs to go in the ON clause rather than the WHERE clause, because it is on the second table.
Second, getdate() -- despite its name -- has a time component. So, convert it to a date:
FROM Person P LEFT JOIN
Figure F
ON P.id = F.id AND F.Date = CAST(getdate() as DATE)

Put your query in a cte and then pull from it to achieve your desired results:
;with cte_example
as
(SELECT
P.[Name],
P.[id],
F.[DATE], F.[Figure], F.[id]
FROM
Person P
LEFT JOIN Figure F
ON P.id = F.id)
select Name
,id
,cast(getdate() as date) as date
,case when date = convert(date, getdate()) then Figure else 0 end as Figure
from cte_example
I made a rextester example so you could see

Related

How to retrieve most recent past date from current date and most future date from current date in sql

I am working on a small application that records appointment visits of patients. I have a both a Patient table and Appointment table.
In Appointment table, I have the following fields: AppointmentId, AppointmentDate, PatientId
In Patient table, I have PatientId, PatientName
In the Appointment table I have records as follows:
1 | 2020-08-08 | 2
2 | 2020-10-11 | 2
3 | 2020-12-15 | 2
4 | 2020-12-24 | 2
What I want to retrieve is that for all patients, I fetch the datasets of the PatientId. I want to retrieve the last appointment date, i.e 2020-10-11 and next appointment date, i.e 2020-12-15 if I run the query today.
I have tried the following but no getting the desired results. Can anyone please help me with this SQL query?
SELECT
patient.PatientName,
MAX(appointment.AppointmentDate) AS NextVisit,
MIN(appointment.AppointmentDate) AS LastVisit
FROM
Patient patient
INNER JOIN
Appointment appointment ON patient.PatientId = appointment.PatientId
You can use left joins in sql as follows:
Select p.patientid, patientname,
Max(ap.appointmentdate) as previous_appointment,
Min(an.appointmentdate) as next _appointment
from patient p
Left join appointment ap on p.patientid = ap.patientid and ap.appointmentdate < sysdate
Left join appointment an on p.patientid = an.patientid and an.appointmentdate > sysdate
Group by p.patientid, p.patientname
You can also use the conditional aggregation as follows:
Select p.patientid, patientname,
Max(case when ap.appointmentdate < sysdate then ap.appointmentdate end) as previous_appointment,
Min(case when ap.appointmentdate > sysdate then ap.appointmentdate end) as next _appointment
from patient p
Left join appointment ap on p.patientid = ap.patientid
Group by p.patientid, p.patientname
Please use >= or <= instead of < and > in condition according to your requirement.

Return first date and previous dates

There is a custid with 3 dates .
I want to return first date and then its previous date .
data should look like:
custid first previous
11 2019-06-10 2019-06-15
11 2019-06-10 2019-07-10
I have done it but I'm not able to put it in 2nd row.
SELECT A.PersonID ,min(a.date) as first,b.date as previous,c.date as previous from Persons as a
INNER JOIN (select PersonID ,date from Persons) b
on a.PersonID
=b.PersonID AND
a.date<
b.date
INNER JOIN (select PersonID ,date from Persons) c
on b.PersonID=
c.PersonID
AND b.date<
c.date
Output which I'm getting:
personid first previous previous
11 2019-06-10 2019-06-15 2019-07-10
I want it to look like:
custid first previous
11 2019-06-10 2019-06-15
11 2019-06-10 2019-07-10
You don't need to add the 3rd inner join table. Since your using min() with other columns, this should be accompanied by Group By.
select
a.PersonID as custid,
min(a.date) as first,
b.date as previous
from
Persons as a
inner join Persons b on a.PersonID = b.PersonID and b.date > a.date
group by a.PersonID, b.date

Finding records that are only before a given date (SQL)

I've tried multiple queries but none of them work.
It's probably really simple.
Here's an example table :
ordernr debnaam debnr orddat
1 Coca-Cola 123 2019-02-07
12 Altec 456 2019-02-07
123 Coca-Cola 123 2016-01-01
1234 Brady 789 2015-03-18
So the point is to find the clients (debnaam) that haven't ordered since the last 2 years.
In my example the only record should be Brady.
I've tried following query :
SELECT a.ordernr, a.debnaam, a.debnr, a.orddat
FROM orkrg as a
WHERE NOT EXISTS(SELECT b.debnr
FROM orkrg as b
WHERE a.ordernr = b.ordernr
AND b.orddat > CONVERT(date, dateadd(year,-2,getdate())))
Or with a Left outer join :
SELECT *
FROM (
SELECT orkrg.ordernr, orkrg.debnaam, orkrg.debnr, orkrg.orddat
FROM orkrg
WHERE orkrg.orddat < CONVERT(date, dateadd(year,-2,getdate()))
) AS a
LEFT OUTER JOIN
(
SELECT orkrg.ordernr, orkrg.debnaam, orkrg.debnr, orkrg.orddat
FROM orkrg
WHERE orkrg.orddat > CONVERT(date, dateadd(year,-2,getdate()))
) as b
ON a.ordernr = b.ordernr
But I always get following result :
ordernr debnaam debnr orddat
123 Coca-Cola 123 2016-01-01
1234 Brady 789 2015-03-18
Could someone please help me?
Thanks!
select a.*
from orkrg as a
where a.orddat < dateadd(year,-2,getdate()) -- this is kinda not needed
and not exists (select 1 -- NOT EXISTS is a safer option than NOT IN, where a null result can cause issues
from orkrg as b
where a.debnaam = b.debnaam and
b.orddat > dateadd(year,-2,getdate()))
I use not exists over not in as default, see here for why
You need to use DATEDIFF() to filter out the older dates:
SELECT a.ordernr, a.debnaam, a.debnr, a.orddat
FROM orkrg as a
WHERE DATEDIFF(year, a.orddat, GETDATE()) > 2
AND A.debnr NOT IN (SELECT b.debnr FROM orkrg as b WHERE
DATEDIFF(year, b.orddat, GETDATE()) <= 2)
select *
from orders as o
where o.debnr not in (select debnr
from orders as u
where orddat > CONVERT(date, dateadd(year,-2,getdate())))

SQL Server joining two tables, order by and display one record

I am having trouble with a SQL Server statement. The perfect scenario is the order and another table (jobs) by date created then display the contact information in descending order. Currently I can get the script to show all records, however if the user has more than one job then they are displayed more than once.
SELECT
c.*,
p.date_created
FROM
[db].[dbo].[Contact] AS c
JOIN
[db].[dbo].[job] AS p ON p.contact_id = c.contact_id
UNION
SELECT
*,
0 as date_created
FROM
[db].[dbo].[Contact]
ORDER BY
p.date_created DESC
The output
contact_id| date_created | contact_name
1 | 8/29/2016 1:07:18 PM | sam
1 | 8/26/2016 1:04:01 PM | sam
14 | 8/24/2016 5:07:22 PM | steve
The final output should just show the newest date created and for one user. Help is much appreciated.
The column in union select must match for number and type so convert 0 in a proper date
SELECT
c.contact_id
,max(p.date_created)
,c. contact_name
FROM [db].[dbo].[Contact] AS c
JOIN [db].[dbo].[job] AS p
ON p.contact_id = c.contact_id
GROUP BY c.contact_id,c. contact_name
union
select
c.contact_id
, convert(datetime, '01/01/1070', 101) as date_created
, c. contact_name
from [db].[dbo].[Contact]
ORDER BY p.date_created desc`
The result you need anyway should be obtainable with only
SELECT
c.contact_id
,max(p.date_created) as max_date_created
,c. contact_name
FROM [db].[dbo].[Contact] AS c
LEFT JOIN [db].[dbo].[job] AS p
ON p.contact_id = c.contact_id
GROUP BY c.contact_id,c. contact_name
ORDER BY c.contact_id,c. contact_name, max_date_created

SQL count categorized by different amounts

As the title suggests. I'm trying to write a query that will give me a count of all people who haven't attended something, however I then need to group them by how many times they haven't attended. Sort of like this.
|---------------------|-----------------------------------|
| No. Of People | No. of times not attended |
|---------------------|-----------------------------------|
| 12 | 1 |
|---------------------|-----------------------------------|
| 34 | 2 |
|---------------------|-----------------------------------|
In this sort of format, with the 1 meaning 'didnt attend once' and the 2 meaning 'didn't attend twice' etc etc.
This is what I have for now..
SELECT COUNT(p.PersonID)AS 'No. of People'
,COUNT(e.attended) AS 'Attended'
,et.EpisodeTypeName
FROM Person p
JOIN Episode e ON e.PersonID = p.PersonID
JOIN EpisodeType et ON et.EpisodeTypeID = e.EpisodeTypeID
WHERE e.Attended = 'No'
AND e.EpisodeDate >= '2015-04-01' AND e.EpisodeDate <= '2016-03-31'
GROUP BY e.Attended, et.EpisodeTypeName;
Any help with this would be great!
If I understand correctly, this is a histogram-of-histograms query. Also, I don't see that EpisodeType is needed, at least based on the results and query in the question.
So, a query with two levels of aggregation:
SELECT NotAttended, COUNT(*) as NumPeople
FROM (SELECT p.PersonID, COUNT(*) as NotAttended
FROM Person p JOIN
Episode e
ON e.PersonID = p.PersonID
WHERE e.Attended = 'No' AND
e.EpisodeDate >= '2015-04-01' AND e.EpisodeDate <= '2016-03-31'
GROUP BY p.PersonID
) p
GROUP BY NotAttended;