Selecting with filters - sql

I'm developing a booking system for a clinic, so my aim is that i want to display available doctor shift which are not already reserved, the patient check the availability of a certain doctor in specific date and shift.
I have 2 tables
Booking (ID, DocID, shift_id, Date)
Shift (ID, name, DocID)
MySQL selecting query which is not working is:
SELECT Shift.ID, Shift.name, Shift.DocID
FROM Shift INNER JOIN
Booking ON Shift.ID = Booking.shift_id
WHERE (Shift.DocID = #DoctorID) AND (Booking.DocID <> #DoctorID)
AND (Booking.shift_id <> #ShiftID) AND (Booking.Date <> #VisitDate)
so please any ideas???

You could try
SELECT s.* FROM Shift
LEFT JOIN Booking b
ON s.id = b.shift_id
AND s.DocID = b.DocID
WHERE b.id IS NULL
OR b.date <> visit_date

Left join method:
SELECT s.* FROM Shift s
LEFT JOIN Booking b
ON s.id = b.shift_id
AND s.DocID = b.DocID
AND b.date = #visit_date
WHERE s.ID = #ShiftID and s.DocID = #DocID and b.id IS NULL
You could also write variants using NOT EXISTS or NOT IN clauses - the point is that you want shifts where there is no booking for that doctor, for that shift, on that day

Related

How to build the SQL query for given question?

I have 2 SQL problems for which I need SQL query.
Table - Booking
Table - Adventure
Table - Tourist
Table - Location
Query1: Display TourId, TourName and Email of those tourist(s) who have booked all types of adventures. (Hint: Use the concept of Joins).
My Try:
Select DISTINCT T.TourId, T.TourName, T.Email
From Tourist T
INNER JOIN Booking B ON B.TourId = T.TourId
INNER JOIN Location L ON L.LocId = B.Loc
INNER JOIN Adventure A ON A.AdvId = L.AdvId
AND A.AdvType in (Select DISTINCT AdvType From Adventure)
Query2: For each booking, Identify the location whose bookingamount is greater than the average bookingamount of all the bookings done for that location. Display LocId, LocName and Rating for the identified location(s). (Hint: Use the concept of subqueries)
My Try:
Select B.Loc, L.LocName, L.Rating
From Booking B
INNER JOIN Location L ON B.Loc = L.LocId
AND BookingAmount > (Select AVG(B.BookingAmount) from Booking B Group By B.Loc)
Query 1:
select distinct tourid,tourname,email from tourist, booking, location
where 1=1
and tourist.tourid = booking.tourid
and booking.locid = location.locid
and location.advid = adventure.advid
and adventure.advtype = 'A'
Query 2:
select locid,locname,rating
from location
where locid in (select booking.locid from booking, (select
b.bookid,b.loc,avg(b.bookingamount) as avg_ba from booking b group by
b.bookid,b.loc) aa
where booking.bookid = aa.bookid and booking.loc = aa.loc and
booking.bookingamount > aa.avg_ba)
Note: if it is a database design for any production server, I must say it needs to be changed ASAP.
Another Note: Please do not ever use pictures as references. It is very difficult to get information from pictures
Query 1:-
Select DISTINCT T.TourId, T.TourName, T.Email
From Tourist T
INNER JOIN Booking B ON B.TourId = T.TourId
INNER JOIN Location L ON L.LocId = B.Loc
INNER JOIN Adventure A ON A.AdvId = L.AdvId
WHERE A.AdvType='A' AND A.AdvType='G' AND A.AdvType='W';
Query 2:-
Select B.Loc, L.LocName, L.Rating
From Booking B
INNER JOIN Location L ON B.Loc = L.LocId
WHERE B.BookingAmount > (Select AVG(B.BookingAmount) from Booking B Group By B.Loc);

Get all tables where there is no booking on this time or date

So basically I have a tables table. And a bookings table. A table can be assigned to a booking via the table_no column. The booking also has a reservation_time and reservation_date columns. What I'd like my query to do, is to return all tables that aren't linked to a booking on a certain time or date. It's really bugging me.
Here is what my query looks like as of now
select t.id, t.number
FROM tables t JOIN
bookings b
ON b.table_no = t.number JOIN
reservation_time_data r
ON r.id = b.reservation_time
WHERE t.number != b.table_no AND b.reservation_date != '2020-07-22' AND 45 NOT BETWEEN r.start_time AND r.end_time
You seem to want not exists. Based on your sample query, I think this is:
select t.id, t.number
from tables t
where not exists (select 1
from bookings b join
reservation_time_data r
on r.id = b.reservation_time
where b.table_no = t.number and
b.reservation_date = '2020-07-22' and
45 >= r.start_time and
45 <= r.end_time
);
I think you can get it with left join like this
select t.id, t.number FROM tables t Left JOIN bookings b ON b.table_no = t.number
WHERE b.table_no is null AND (b.reservation_date = '2020-07-22' Or b.[your time column here] BETWEEN b.start_time AND b.end_time )

sql subquery join group by

I am trying to get a list of our users from our database along with the number of people from the same cohort as them - which in this case is defined as being from the same medical school at the same time.
medical_school_id is stored in the doctor_record table
graduation_dt is stored in the doctor_record table as well.
I have managed to write this query out using a subquery which does a select statement counting the number of others for each row but this takes forever. My logic is telling me that I ought to run a simple GROUP BY query once first and then somehow JOIN the medical_school_id on to that.
The group by query is as follows
select count(ca.id) , cdr.medical_school_id, cdr.graduation_dt
from account ca
LEFT JOIN doctor cd on ca.id = cd.account_id
LEFT JOIN doctor_record cdr on cd.gmc_number = cdr.gmc_number
GROUP BY cdr.medical_school_id, cdr.graduation_dt
The long select query is
select a.id, a.email , dr.medical_school_id,
(select count(ba.id) from account ba
LEFT JOIN doctor bd on ba.id = bd.account_id
LEFT JOIN doctor_record bdr on bd.gmc_number = bdr.gmc_number
WHERE bdr.medical_school_id = dr.medical_school_id AND bdr.graduation_dt = dr.graduation_dt) AS med_count,
from account a
LEFT JOIN doctor d on a.id = d.account_id
LEFT JOIN doctor_record dr on d.gmc_number = dr.gmc_number
If you could push me in the right direction that would be amazing
I think you just want window functions:
select a.id, a.email, dr.medical_school_id, dr.graduation_dt,
count(*) over (partition by dr.medical_school_id, dr.graduation_dt) as cohort_size
from account a left join
doctor d
on a.id = d.account_id left join
doctor_record dr
on d.gmc_number = dr.gmc_number;
Using your same code for group by:
SELECT * FROM (
(
SELECT acc.[id]
, acc.[email]
FROM
account acc
LEFT JOIN
doctor doc
ON
acc.id = doc.account_id
LEFT JOIN
doctor_record doc_rec
ON
doc.gmc_number = doc_rec.gmc_number
) label
LEFT JOIN
(
SELECT count(acco.id)
, doc_reco.medical_school_id
, doc_reco.graduation_dt
FROM
account acco
LEFT JOIN
doctor doct
ON
acco.id = doct.account_id
LEFT JOIN
doctor_record doc_reco
ON
doct.gmc_number = doc_reco.gmc_number
GROUP BY
doc_reco.medical_school_id,
doc_reco.graduation_dt
) count
ON
count.[medical_school_id]=label.[medical_school_id]
AND
count.[graduation_dt]=label.[graduation_date]
)
how about something like this?
select a.doctor_id
, count(*) - 1
from doctor_record a
left join doctor_record b on a.medical_school_id = b.medical_school_id
and a.graduation_dt = b.graduation_dt
group by a.doctor_id
Subtract 1 from the count so that you're not counting the doctor in the "other folks in same cohort" number
I'm defining "same cohort" as "same medical school & graduation date".
I'm unclear on what GMC number is and how it is related. Is it something to do with cohort?

Show row in a series even if the data is missing from the table

I need a SQL query to return a row for every month in years 2015 and 2016 for every company that pays dues. The resulting dataset will show which months the company didn't pay dues by a null value. The problem is that if they didn't pay dues they won't have an entry in the database so no row will appear for than month. Here is the query:
SELECT
case when n.co_id <>'' then n.co_id else n.ID end ID
,su.CONTINUOUS_SINCE
,n.COMPANY
,a.EFFECTIVE_DATE
, a.AMOUNT
FROM dbo.Name n
LEFT OUTER JOIN dbo.Activity a ON n.ID = a.ID
inner JOIN dbo.Loc_Info l ON n.ID = l.ID
inner JOIN dbo.Segment_Categories s ON l.CURRENT_SEGMENT = s.CODE
inner JOIN dbo.Subscriptions su on su.id=n.id
WHERE   a.PRODUCT_CODE='rental' and n.MEMBER_TYPE in ('rb','rl') and a.EFFECTIVE_DATE Between '2015-07-01' And GetDate() AND a.ACTIVITY_TYPE='dues'
order by case when n.co_id <>'' then n.co_id else n.ID end, EFFECTIVE_DATE asc
If the company has paid every month it works out fine but the point is to find the companies that haven't paid so suppose Company XYZ paid every month in 2015 except June I need a row for June for Company XYZ that has a NULL value or a zero or some other indicator that they missed a payment. As it stands now the row is simply omitted because the data isn't there and it is hard to find a missing row out of thousands or rows.
I realize it is probably a different type of join or something but I am just not getting it to work out.
You can create a dummy table for the months, left join the dbo.Activity to it, that way you'll get all the months, and then join that to dbo.Name
1) Generate all the months from 1 to 12 with a recursive cte.
2) Get all months years and companies combinations with a cross join.
3) left join on this result-set to show missing months.
with months as (select 1 mth
union all
select mth+1 from months where mth<12)
,yearmonthscompanies as (select *
from months m
cross join (select 2015 yr union all select 2016 yr) y
cross join (select distinct id,co_id,company from name) c
)
SELECT
case when ymc.co_id <>'' then ymc.co_id else ymc.ID end ID
,su.CONTINUOUS_SINCE
,ymc.COMPANY
,coalesce(a.effective_date,datefromparts(ymc.yr,ymc.mth,1)) as effective_date
,coalesce(a.AMOUNT,0) amount
FROM yearmonthscompanies ymc
LEFT JOIN dbo.Name n ON n.co_id=ymc.co_id and n.id=ymc.id and n.company=ymc.company
LEFT JOIN dbo.Activity a ON n.ID = a.ID and a.PRODUCT_CODE='rental'
and n.MEMBER_TYPE in ('rb','rl') and a.EFFECTIVE_DATE Between '2015-07-01' and GetDate()
and a.ACTIVITY_TYPE='dues'
and year(a.effective_date) = ymc.yr and month(a.effective_date) = ymc.mth
inner JOIN dbo.Loc_Info l ON n.ID = l.ID
inner JOIN dbo.Segment_Categories s ON l.CURRENT_SEGMENT = s.CODE
inner JOIN dbo.Subscriptions su on su.id=n.id
order by case when ymc.co_id <>'' then ymc.co_id else ymc.ID end
,effective_date

Duplicate Values in SQL

I'm using this query and have used the Select Distinct code to enusre no duplicates are pulled from the database.
However on my QTD colum the number is sometimes X2 the proper amount?
This is probably an error with the server or would my query be incorrect?
SELECT DISTINCT ad.eid, MAX(u1.email) as ops,MAX(u2.email) as rep,
(SUM(ad.cost)) as qtd_spend,
Sum(case when day < current_date AND day >='2015-01-01' then cost else 0 end) as MTD,
AVG(case when day < current_date AND day >= current_date-7 then cost else null end) as weekly_spend
FROM adcube as ad
inner JOIN advertisables as a on ad.eid = a.eid
LEFT JOIN organizations as o on o.id = a.id
LEFT outer JOIN users as u1 on o.ops_organization_id = u1.organization_id
LEFT outer JOIN users as u2 on o.sales_organization_id = u2.organization_id
WHERE day >='2015-01-01' and day < current_date
GROUP BY eid
You must have GROUP BY if you have aggregate functions (such as SUM or MAX).
What is likely the problem is in you JOINs.
I am not familiar with your data structure, but I am assuming that in your advertisables table, it contains (or CAN contain) more than one entry of the same "eid" - is this correct? Or do you have a constraint?
If this is correct, then when you join even if you have only ONE entry in the "adcube" table, once it JOINs with the multiple entries in the "advertisables" table then it pulls up TWO records (or however many match) and then the aggregate results at the select level of the statement then sum BOTH (or more) columsn.
So you should take the duplicates out of hte joining tables or factor that into account.
EDIT:
Ok, well glad to know that is the problem. You will not fix it by INNER JOINING either. You will have to do an inline select statement.
The best way to solve this, from what I understand you are trying to do, is do the following:
SELECT ad.eid
, (
select max(u1.email)
from JOIN advertisables as a
LEFT JOIN organizations as o on o.id = a.id
LEFT outer JOIN users as u1 on o.ops_organization_id = u1.organization_id
LEFT outer JOIN users as u2 on o.sales_organization_id = u2.organization_id
where a.eid = ad.eid
) as ops
, (
select max(u2.email)
from JOIN advertisables as a
LEFT JOIN organizations as o on o.id = a.id
LEFT outer JOIN users as u1 on o.ops_organization_id = u1.organization_id
LEFT outer JOIN users as u2 on o.sales_organization_id = u2.organization_id
where a.eid = ad.eid
) as rep
, (SUM(ad.cost)) as qtd_spend
, Sum(case when day < current_date AND day >='2015-01-01' then cost else 0 end) as MTD
, AVG(case when day < current_date AND day >= current_date-7 then cost else null end) as weekly_spend
FROM adcube as ad
WHERE day >='2015-01-01' and day < current_date
GROUP BY eid