retrieve available day from date rante - sql

I think I'm pretty close on this query, but can't seem to crack it, and I'm not sure if I've got the most efficient approach.
I am trying to find a day where a user is not booked from a range of dates where they are booked.
Think staff scheduling. I need to find who is available to work on Tuesday, and is working on other days this week.
My query currently looks like this
SELECT employees.uid, name, date
FROM employees
LEFT JOIN storelocation ON employees.uid = storelocation.uid
LEFT JOIN schedule ON emplyees.uid = schedule.uid
WHERE slid =9308
AND date
BETWEEN '2009-11-10'
AND '2009-12-20'
AND NOT
EXISTS (
SELECT uid
FROM schedule
WHERE date = '2009-11-11'
)
If I don't include the 'Not Exists', I get 1500 results
If I use only the Select form the 'Not Exists', I get 200 results, so both of those queries work independently.
However, my query as I've written it returns 0 results.

You might want to try something more like this:
SELECT employees.uid, name, date
FROM users
LEFT JOIN storelocation ON employees.uid = storelocation.uid
LEFT JOIN schedule ON emplyees.uid = schedule.uid
WHERE slid =9308
AND date BETWEEN '2009-11-10' AND '2009-12-20'
AND employees.uid NOT IN (
SELECT uid
FROM schedule
WHERE date = '2009-11-11'
)

The problem is your NOT EXISTS isn't correllated, and you won't be able to do with this without using table aliases:
SELECT e.uid,
e.name,
s.date
FROM EMPLOYEES e
LEFT JOIN STORELOCATION sl ON sl.uid = e.uid
LEFT JOIN schedule s ON s.uid = e.uid
AND s.date BETWEEN '2009-11-10'AND '2009-12-20'
WHERE slid = 9308
AND NOT EXISTS (SELECT NULL
FROM SCHEDULE t
WHERE t.uid = e.uid
AND t.date = '2009-11-11')
The SELECT in an EXISTS clause doesn't do anything - you could use EXISTS( SELECT 1/0 ..., which should cause a "can not divide by zero" error. But it won't... EXISTS only returns true if 1+ instances match the WHERE/etc clause. There are numerous questions on SO asking about if it matters what's in the SELECT clause if you want to read more.
Jim's answer, typo aside, should be faster in MySQL than the alternative I supplied. To read more about why, check this article: NOT IN vs NOT EXISTS vs LEFT JOIN/IS NULL in MySQL.

Related

ORDER BY IS INVALID

I know youve answered similar issues before but I have a specific query I hope you can help.
I have a table of organizations (clients).
I need to find the most recent job per client.
the thing is, the client table isnt directly connected to the job. It goes like this Job - Job Header - Organization.
So I have a query for ALL Organizations (Select * From Organizations), then I have a JOIN to a query which finds the most recent job using the client ORg as the join criteria.
For
example:
Select * From Organization
LEFT JOIN (Select Top 1 JobDate, JobNumber,JobWeight From Jobs LEFT JOIN JobHeader on Job.PK = JobHeader.ParentPK LEFT JOIN Organization on JObHeader.Org = Organization.PK Order by JObDate DESC)
When I ran it, it gives an error saying Order By clause is invalid in views, inline functions.
How else can I find the most recent JObDate in the JobHeader table for each related Organization?
Your syntax looks like SQL Server. You can do what you want using a correlated subquery or lateral join (apply). This looks like
select o.*, j.*
from Organization o outer apply
(select Top 1 JobDate, JobNumber, JobWeight
from Jobs j join
JobHeader jh
on j.PK = jh.ParentPK
where jh.Org = o.PK
order by JObDate DESC
) j;

How do combine a count in a particular select statement?

On our postgresql database we currently have 2 tables called Users and Bookings.
We are currently trying to know on the users that made a booking yesterday how many bookings they did over time.
Here is the query we have for the moment:
SELECT "domain".users.email, COUNT("domain".bookings."id")
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
If we have the date the booking was created (field "domain.bookings.created_at) with filter yesterday we only get the data from yesterday.
Is there a way to see on who bought yesterday how many bookings they did overtime?
Thank you!
Luca
TRY THIS
SELECT "domain".users.email, COUNT(CASE WHEN bookingdate = current_date - 1 THEN 1 END ) AS TOTAL_BOOKING
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
This is what you are looking for:
SELECT u.id, u.email, count(b.id) AS "Total Bookings"
FROM "domain".bookings as b
JOIN "domain".users as U ON u.id = b.user_id
WHERE EXISTS (SELECT 1
FROM "domain".bookings b2
WHERE b2.created_at = current_date - 1
AND b2.user_id = b.user_id)
GROUP BY u.email;
The exists condition will only return rows from the bookings table for users that booked something yesterday.
Unrelated, but: using a keyword like domain that requires double quotes for identifiers is not such a good idea. It would save you some trouble in the long run if you found a different name
Thank you for answering. I was probably not precise enough!
Basically if I use the following query:
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY
"domain".users.email
I get the number of bookings made by a user.
BUT
I want to get the following: the number of bookings made all time by the users that bought yesterday.
I tried this
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
WHERE
"domain".bookings.created_at = 'yesterday'
GROUP BY
"domain".users.email
But I am getting no responses...
Thank you for help!
Luca

SQL single-row subquery returns more than one row?

I'm trying to get ID and USER name from one query but at the same time I'm looking in my WHERE clause if ID exist in other table. I got error:
ORA-01427: single-row subquery returns more than one row
Here is how my query look:
SELECT s.ID, s.LASTFIRST
From USERS s
Left Outer Join CALENDAR c
On s.ID = c.USERID
Where c.SUPERVISOR = '103'
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
And s.ID != (SELECT USER_ID
From RESERVATIONS
Where EVENT_ID = '56')
My query inside of where clause returns two ID's: 158 and 159 so these two should not be returned in my query where I'm looking for s.ID and s.LASTFIRST. What could cause this error?
Use not in instead of !=
!= or = are for single IDs and values, not in and in are for multiple
And s.ID not in (SELECT USER_ID
From RESERVATIONS
Where EVENT_ID = '56')
Edit: not in vs not exists
Not exists is a perfectly viable option as well. In fact, it is better to not exists than not in if there are the possibility of null values in the subquery result set - In Oracle, the existence of a null will cause not in to return no results. As a general rule, I use not in for ID, not null columns, and not exists for everything else. It may be better practice to always use not exists... personal preference I suppose.
Not exists would be written like so:
SELECT s.ID, s.LASTFIRST
From USERS s
Left Outer Join CALENDAR c
On s.ID = c.USERID
Where c.SUPERVISOR = '103'
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
And not exists (SELECT USER_ID
From RESERVATIONS r
Where r.USER_ID = S.ID
And EVENT_ID = '56')
Performance
In Oracle there is no performance difference between using not in, not exists or a left join.
Source : https://explainextended.com/2009/09/17/not-in-vs-not-exists-vs-left-join-is-null-oracle/
Oracle's optimizer is able to see that NOT EXISTS, NOT IN and LEFT JOIN / IS NULL are semantically equivalent as long as the list values are declared as NOT NULL.
It uses same execution plan for all three methods, and they yield same results in same time.
This is a formatted comment that is not related to your question.
This is slow:
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
because you are filtering on a function result.
This is logically equivalent and much faster:
And c.DATEENROLLED >= to_date('4/22/2016','fmmm/fmdd/yyyy')
Edit starts here
Aaron D's answer says to use not in. Here are two faster ways to do the same thing:
left join reservations r on s.id = user_id
and r.event_id = '56'
etc
where r.user_id is null
or
where s.id in
(
select user_id
from reservations
minus
select user_id
from reservations
where event_id = 56
)

SQL: Issue with WHERE Clause

SELECT DISTINCT Interests.Interest_name, Impressions.Count
FROM Interests
WHERE Vertical_name = 'Retail'
INNER JOIN Impressions ON Interests.Interest_Name = Impressions.Interest_Name;
The above query generates the error
Lookup Error - SQL Server Database Error: Incorrect syntax near the keyword 'Inner'.
If I remove the WHERE clause it works perfectly OK. I am not sure if there is an issue with the syntax?
WHERE condition needs to go after joins, try to move it to the end:
SELECT DISTINCT Interests.Interest_name,Impressions.Count FROM Interests
Inner Join Impressions ON Interests.Interest_Name = Impressions.Interest_Name
WHERE Vertical_name = 'Retail';
Well the Syntax is just wrong. You have to move the WHERE after your JOIN
For example:
SELECT DISTINCT Interests.Interest_name,Impressions.Count
FROM Interests
INNER JOIN Impressions
ON Interests.Interest_Name = Impressions.Interest_Name
WHERE Vertical_name = 'Retail';
If you tried to pre-filter your table Interests you can do that this way:
SELECT DISTINCT Interests.Interest_name,Impressions.Count
FROM (
SELECT *
FROM Interests
WHERE Vertical_name = 'Retail'
) as Interests
INNER JOIN Impressions
ON Interests.Interest_Name = Impressions.Interest_Name;
Just a hint after all. I would suggest you, to use aliases for every table. It will improve the reading and will save you if you need to rename a table.
The where goes after the from clause. And join is part of the from clause.
Your query would be easier to write and to read with aliases:
SELECT DISTINCT i.Interest_name, i.Count
FROM Interests i Inner Join
Impressions im
ON i.Interest_Name = im.Interest_Name;
WHERE Vertical_name = 'Retail';
If you don't need the DISTINCT, then you should remove it. It only hurts performance.
In fact, a better way to write this query is to use a correlated subquery:
SELECT DISTINCT i.Interest_name, i.Count
FROM Interests i
WHERE EXISTS (SELECT 1
FROM Impressions im
WHERE i.Interest_Name = im.Interest_Name
)
WHERE i.Vertical_name = 'Retail';
This assumes that Vertical_Name is in Interests. Otherwise, it would go in the subquery.

Correct SQL query statement requiring JOIN and UNION

I was trying to solve this question (No. 29) on http://www.sql-ex.ru/
Under the assumption that the income (inc) and expenses (out) of the
money at each outlet are written not more than once a day, get a
result set with the fields: point, date, income, expense. Use Income_o
and Outcome_o tables.
And came up with this solution
SELECT Income_o.point, Income_o.date, Income_o.inc, Outcome_o.out
FROM Income_o
INNER JOIN Outcome_o ON Income_o.point = Outcome_o.point
The result is obviously wrong (and hence my question here). It assumes that a point will never have more than 1 income and expense, so isn't this query correct? I can see from the same page that the correct query has some NULL column values. I would appreciate an explanation (if not the correct answer). My SQL is not a master one (and that's why I am trying to go through those!! So far done 29 out of 125 and only took help from SO on 3 of them)
The expected result is (From the website):
The result of correct query:
A snapshot of the expected result is here - http://snag.gy/yN43V.jpg
P.S. I know that the hint says UNION and JOIN and trying to get my head around this. If I can get the answer myself, I will post it.
You want a full outer join on point and date:
SELECT
COALESCE(i.point, o.point) AS point,
COALESCE(i.date, o.date) AS date,
i.inc,
o.out
FROM
Income_o AS i
FULL JOIN Outcome_o AS o ON i.point = o.point AND i.date = o.date
;
The COALESCE expressions ensure that NULL is not returned for those columns: if the Income_o side has a NULL (because the table has no match for an Outcome_o row), the value is then taken from the other side.
Alternatively you can go with a union of two outer joins, left and right:
SELECT
i.point,
i.date,
i.inc,
o.out
FROM
Income_o AS i
LEFT JOIN Outcome_o AS o ON i.point = o.point AND i.date = o.date
UNION
SELECT
o.point,
o.date,
i.inc,
o.out
FROM
Income_o AS i
RIGHT JOIN Outcome_o AS o ON i.point = o.point AND i.date = o.date
;
If the tables have matches on the specified condition, both joins will return them, but UNION will eliminate duplicate entries. This second method is essentially an alternative implementation of full outer join, useful for cases where the FULL JOIN syntax is not supported. (MySQL is one product that does not support FULL JOIN.)
You can use Group By with Aggregate Function to achieve the desired result, the sub query will combine the result set but will give results as per date, if there are two (in and out transaction) on same date, these will appear as two rows, to make it one row we can use Group By with aggregate function.
select point, date, max(inc), max(out)
from
(
select point, date, inc, NULL as out
from income_o
union all
select point, date, NULL, out
from outcome_o
)
dt
group by point, date
I think you are looking for LEFT JOIN
SELECT Income_o.point, Income_o.dat, Income_o.inc, Outcome_o.outc
FROM Income_o
LEFT JOIN Outcome_o ON Income_o.point = Outcome_o.point
try this SQL Fiddle example