SQL statement only select items that appear less than twice - sql

I am currently working on a database which stores information which allows users to make reservations at a restaurant.
I am trying create a SQL statement which returns the times which appear less than twice in the reservations table.
At the moment I have this but this only returns the times which do not appear in the reservations table at all.
SELECT *
FROM TIME
WHERE
TIME NOT IN (
SELECT reservation.a_time
FROM
RESERVATION
JOIN TIME ON
reservation.a_time = time.time
WHERE
reservation.a_date = :the_date
)
ORDER BY time;
The above statement returns all times which are not in the reservations table. However how would I return all times which appear in the reservations table including those that appear once but not those that appear twice?
Thanks

select
*
from
TIME t
where
(select
count(r.a_time)
from
RESERVATION r
where
r.a_time = t.time and
r.a_date = :the_date) < 2
or
select
t.time /* and maybe other field, which you need to add to Group By as well */
from
TIME t
left join RESERVATION r on r.a_time = t.time and r.a_date = :the_date
group by
t.time
having
count(t.time) < 2
I prefer the first, because it is cleaner, more clear and can be expanded easier, despite the subselect.

If the only thing you care about is returning reservation.a_times that appear EXACTLY once, then this will do it:
SELECT reservation.a_time
FROM RESERVATION
GROUP BY reservation.a_time
HAVING COUNT(*) = 1;
This will group all RESERVATION entries by time, and only return the groups with exactly one member.

Related

How do i get my MAX SQL Select statement to work within my inner join

I've wrote a small inner join that takes column's from two different database tables; WKS_LOG_VIEW and FDE_XML_VIEW. I match them on the ACID/AID and data is returned within the date and machine selected (this works). I have then tried to add a MAX select query that will only put the latest EventTime row in. As i only want one record per EventTime entry. I don't need many EventTime entries just the latest. My query is returning no entries.
select
EXCDS1.dbo.FDE_XML_VIEW.ACID,
EXCDS1.dbo.FDE_XML_VIEW.SID,
EXCDS1.dbo.FDE_XML_VIEW.STAR,
EXCDS1.dbo.FDE_XML_VIEW.ROUTE,
EXCDS.dbo.WKS_LOG_VIEW.AID,
EXCDS.dbo.WKS_LOG_VIEW.Sector,
EXCDS.dbo.WKS_LOG_VIEW.LocalCoAltIn
EXCDS.dbo.WKS_LOG_VIEW.EventTime
from EXCDS1.dbo.FDE_XML_VIEW
inner join
EXCDS.dbo.WKS_LOG_VIEW
on AID = ACID
where
LastUpdateMachine = 'R02'
and
convert (date, EventTime) = '2021-03-11'
and
EXCDS.dbo.WKS_LOG_VIEW.EventTime = (SELECT MAX(lv.EventTime) from EXCDS.dbo.WKS_LOG_VIEW as lv
where
lv.AID = EXCDS.dbo.WKS_LOG_VIEW.AID)
order by ACID, EventTime;
I'm very new to SQL so apologies if I'm making obvious mistakes.
I think the issue is the additional filtering in the query. The simplest way to solve this uses row_number():
select fw.*
from (select f.ACID, f.SID, f.STAR, f.ROUTE,
wl.AID, wl.Sector, wl.LocalCoAltIn wl.EventTime,
row_number() over (partition by wl.AID order by wl.EventTime) as seqnum
from EXCDS1.dbo.FDE_XML_VIEW f join
EXCDS.dbo.WKS_LOG_VIEW wl
on wl.AID = f.ACID
where LastUpdateMachine = 'R02' and
convert(date, wl.EventTime) = '2021-03-11'
) fw
where seqnum = 1;
order by ACID, EventTime;
Notes:
Use tables aliases. They make the query easier to write and to read.
Qualify all column references. I still have no idea what table LastUpdateMachine comes from.
Your query seems to reference two views not two tables. This doesn't matter for your query. I just want to be sure that you understand the difference between a view and a table.

Getting duplicate rows on subquery

So this has been bothering me for some time because I feel like in MSSQL this query would run just fine but at my new job I am forced to use Oracle. I have a subselect in a query where I want to find all of the people not assigned to a survey. My query is as follows:
Select distinct * From GetAllUsers, getperms
Where id not in (getperms.users) and Survey_ID = '1'
If there are three users in the getperms table I get three rows for each person in the the GETALLUsers table.
I guess I could do some kind of join and that's no problem, it's just really bothering me that this doesn't work when i think that it should.
I feel like in MSSQL this query would run just fine
It would not. In both Oracle and MS-SQL, an IN clause needs to be a static list of items or a subquery that returns one column, so you'd need something like:
Select distinct *
From GetAllUsers
Where id not in (SELECT id FROM getperms.users)
and Survey_ID = '1'
Note that I took getperms out of the FROM since it produces a cross-join, which is why you get every combination of records from both tables.
Your query looks just horrible. First of all you are using a join syntax that has become out of date more than twenty years ago. Then you are using NOT IN where it doesn't make sense (your set contains just one value). Then I can hardly imagine that DISTINCT makes sense in your query. Or one of the two tables contains records that are exact duplicates, which it shouldn't. Is Survey_ID really a string? And as you are working with two tables, you should use qualifiers on your columns to indicate where they reside in, e.g. GetAllUsers.id. I assume that Survey_ID resides in getperms.
Your query properly written looks thus:
Select *
From GetAllUsers gau
Join getperms gp On gp.Survey_ID = '1' And gp.users <> gau.id;
But of course whether you write it this way or the other, it does exactly the same. And there is no difference whether you use it in Oracle or SQL Server. What is does is: get all combinations of GetAllUsers and survey-1 getperms, except for the matches. So every user will be in the results, each combined with almost every getperms record.
You say: "I want to find all of the people not assigned to a survey". That would rather be:
Select *
From GetAllUsers
Where id Not In (Select users From getperms Where Survey_ID = '1');
You can use any of the following 3 methods,if you wanted to exclude the getperms users from GetAllUSers .
SELECT DISTINCT a.*
FROM GetAllUsers a
LEFT JOIN getperms b
ON a.id=b.users
WHERE b.users is null AND Survey_ID = '1'
OR
SELECT DISTINCT *
FROM GetAllUsers a
WHERE NOT EXISTS (SELECT 1
FROM getperms b
WHERE a.id=b.users)
AND Survey_ID = '1'
OR
SELECT DISTINCT *
FROM GetAllUsers
WHERE ID NOT IN (SELECT users
FROM getperms )
AND Survey_ID = '1'

Specifying SELECT, then joining with another table

I just hit a wall with my SQL query fetching data from my MS SQL Server.
To simplify, say i have one table for sales, and one table for customers. They each have a corresponding userId which i can use to join the tables.
I wish to first SELECT from the sales table where say price is equal to 10, and then join it on the userId, in order to get access to the name and address etc. from the customer table.
In which order should i structure the query? Do i need some sort of subquery or what do i do?
I have tried something like this
SELECT *
FROM Sales
WHERE price = 10
INNER JOIN Customers
ON Sales.userId = Customers.userId;
Needless to say this is very simplified and not my database schema, yet it explains my problem simply.
Any suggestions ? I am at a loss here.
A SELECT has a certain order of its components
In the simple form this is:
What do I select: column list
From where: table name and joined tables
Are there filters: WHERE
How to sort: ORDER BY
So: most likely it was enough to change your statement to
SELECT *
FROM Sales
INNER JOIN Customers ON Sales.userId = Customers.userId
WHERE price = 10;
The WHERE clause must follow the joins:
SELECT * FROM Sales
INNER JOIN Customers
ON Sales.userId = Customers.userId
WHERE price = 10
This is simply the way SQL syntax works. You seem to be trying to put the clauses in the order that you think they should be applied, but SQL is a declarative languages, not a procedural one - you are defining what you want to occur, not how it will be done.
You could also write the same thing like this:
SELECT * FROM (
SELECT * FROM Sales WHERE price = 10
) AS filteredSales
INNER JOIN Customers
ON filteredSales.userId = Customers.userId
This may seem like it indicates a different order for the operations to occur, but it is logically identical to the first query, and in either case, the database engine may determine to do the join and filtering operations in either order, as long as the result is identical.
Sounds fine to me, did you run the query and check?
SELECT s.*, c.*
FROM Sales s
INNER JOIN Customers c
ON s.userId = c.userId;
WHERE s.price = 10

Need to make SQL subquery more efficient

I have a table that contains all the pupils.
I need to look through my registered table and find all students and see what their current status is.
If it's reg = y then include this in the search, however student may change from y to n so I need it to be the most recent using start_date to determine the most recent reg status.
The next step is that if n, then don't pass it through. However if latest reg is = y then search the pupil table, using pupilnumber; if that pupil number is in the pupils table then add to count.
Select Count(*)
From Pupils Partition(Pupils_01)
Where Pupilnumber in (Select t1.pupilnumber
From registered t1
Where T1.Start_Date = (Select Max(T2.Start_Date)
From registered T2
Where T2.Pupilnumber = T1.Pupilnumber)
And T1.reg = 'N');
This query works, but it is very slow as there are several records in the pupils table.
Just wondering if there is any way of making it more efficient
Worrying about query performance but not indexing your tables is, well, looking for a kind word here... ummm... daft. That's the whole point of indexes. Any variation on the query is going to be much slower than it needs to be.
I'd guess that using analytic functions would be the most efficient approach since it avoids the need to hit the table twice.
SELECT COUNT(*)
FROM( SELECT pupilnumber,
startDate,
reg,
rank() over (partition by pupilnumber order by startDate desc) rnk
FROM registered )
WHERE rnk = 1
AND reg = 'Y'
You can look execution plan for this query. It will show you high cost operations. If you see table scan in execution plan you should index them. Also you can try "exists" instead of "in".
This query MIGHT be more efficient for you and hope at a minimum you have indexes per "pupilnumber" in the respective tables.
To clarify what I am doing, the first inner query is a join between the registered table and the pupil which pre-qualifies that they DO Exist in the pupil table... You can always re-add the "partition" reference if that helps. From that, it is grabbing both the pupil AND their max date so it is not doing a correlated subquery for every student... get all students and their max date first...
THEN, join that result to the registration table... again by the pupil AND the max date being the same and qualify the final registration status as YES. This should give you the count you need.
select
count(*) as RegisteredPupils
from
( select
t2.pupilnumber,
max( t2.Start_Date ) as MostRecentReg
from
registered t2
join Pupils p
on t2.pupilnumber = p.pupilnumber
group by
t2.pupilnumber ) as MaxPerPupil
JOIN registered t1
on MaxPerPupil.pupilNumber = t1.pupilNumber
AND MaxPerPupil.MostRecentRec = t1.Start_Date
AND t1.Reg = 'Y'
Note: If you have multiple records in the registration table, such as a person taking multiple classes registered on the same date, then you COULD get a false count. If that might be the case, you could change from
COUNT(*)
to
COUNT( DISTINCT T1.PupilNumber )

SQL Scheduling - Select All Rooms Available for Given Date Range

I'm using Microsoft's idea for storing resource and booking information. In short, resources, such as a hotel room, do not have date records and booking records have a BeginDate and EndDate.
I'm trying to retrieve room availability information using MS's queries but something tells me that MS's queries leave much to be desired. Here's the MS article I'm referring to: http://support.microsoft.com/kb/245074
How can I retrieve available rooms for a given date range? Here's my query that returns a simple list of bookings:
SELECT r.RoomID, b.BeginDate, b.EndDate
FROM tblRoom as r INNER JOIN tblBooking b ON r.RoomID = b.AssignedRoomID;
But I'm still baffled as to how I can get a list of available rooms for a given date range?
I am using Microsoft Access but I'd like to keep my queries DBMS agnostic, as much as possible. While this isn't really my question, if you feel that the data model I'm using is unsound, please say so, as I am willing to consider a better way of storing my data.
Edit1:
I failed to mention that I don't like MS's queries for two reasons. First of all, I'm really confused about the 3 different OR operators in the WHERE clause. Are those really necessary? Secondly, I don't like the idea of saving a query and using it as a table although I'm willing to do that if it gets the job done, which in this case I believe it does.
Edit2:
This is the solution I've landed on using the excellent answer given here. This is MS Access SQL dialect (forgive me):
SELECT * FROM tblRoom AS r
WHERE RoomID NOT IN
(SELECT AssignedRoomID as RoomID From tblBooking
WHERE assignedroomid IS NOT NULL AND assignedroomid = r.roomid AND
(BeginDate < #BookingInquiryEndDate AND EndDate > #BookingInquiryBeginDate)
)
You want all the rooms which do not have a booking in that date range, i.e.,
If your sql engine does subqueries...
Select * From Rooms r
where not exists
(Select * From Bookings
Where room = r.room
And startBooking < #endRange
And endBooking > #startRange)
HIK, to understand the need for the room = r.room clause try these two queries
Query One (with room = r.room clause)
Select r.*,
Case Where Exists
(Select * From Bookings
Where room = r.room
And startBooking < #endRange
And endBooking > #startRange)
Then 'Y' Else 'N' End HasBooking
From Rooms r
Query Two(without room = r.room clause)
Select r.*,
Case Where Exists
(Select * From Bookings
Where startBooking < #endRange
And endBooking > #startRange)
Then 'Y' Else 'N' End HasBooking
From Rooms r
Notice the first one returns different values in HasBooking for each row of the output, because the subquery is 'Correleated' with the outer query... it is run over and over agaio, once for each outer query results row.
The second one is the same value for all rows... It is only executed once, because nothing in it is dependant on which row of the outer query it is being generated for.