I am a newbie to both SQL and Access and was wondering if you could help please?
I am in the process of creating a hotel bookings database using Access 2010 but I cannot get my query working where I search for a vacant room.
My database has 5 tables as follows (Field Names in brackets):
BOOKINGS (BookRef, CustAcctNo, BookDate, ArrivDate, DurStay, EmpNo, RoomNo)
CUSTOMERS (CustAcctNo, Title, Forename, Surname, Address1, Address2, Address3)
EMPLOYEES (EmpNo, Title, Forename, Surname)
ROOM TYPES (RoomType, Description, Rate/Price)
ROOMS (RoomNo, RoomType)
These tables all have a 'one-to-many' relationship i.e. one customer can have many bookings.
So, my thinking is that the Fields of interest would be the ArrivDate Field (date of arrival) and DurStay (Duration of Stay) Field. In the Rooms table the Room Number is the Field I am calling out.
So, the closest I have got so far is the following:
PARAMETERS [Start Date] DateTime, [End Date] DateTime;
SELECT R.*, [Start Date] AS Expr1, [End Date] AS Expr2, *
FROM ROOMS AS R
LEFT JOIN (SELECT B.RoomNo
FROM Bookings AS B
WHERE ([Start Date] between B.ArrivDate and (B.ArrivDate + [Please Enter]))
AND ([End Date] between B.ArrivDate and (B.ArrivDate + B.DURSTAY))) AS BKD
ON R.RoomNo = BKD.RoomNo
WHERE (((BKD.RoomNo) Is Null));
This just doesn't seem to be working for me at all. I have tried many times with different versions of the above code but seem to be getting nowhere. My thoughts were that I do a search where the field is null between the dates plus the duration of stay but maybe I am going about it the wrong way I am not sure.
Hopefully I have provided enough detail here but please let me know if you need to know more. I really appreciate you all having a look at this at least. Maybe a fresh outlook on it might spot where I am going wrong.
Many thanks in advance for any help you can offer.
Try changing from this:
WHERE ([Start Date] between B.ArrivDate and ...
AND ([End Date] between B.ArrivDate and ...
to this:
WHERE ([Start Date] between B.ArrivDate and ...
OR ([End Date] between B.ArrivDate and ...
I think this fulfills the logic better because it accepts a partial match, that is, an overlap in dates.
PARAMETERS [Start Date] DateTime, [End Date] DateTime;
SELECT R.*, [Start Date] AS Expr1, [End Date] AS Expr2, *
FROM ROOMS AS R
LEFT JOIN (SELECT B.RoomNo
FROM Bookings AS B
WHERE ([Start Date] between B.ArrivDate and (B.ArrivDate + **B.DURSTAY**))
**OR** ([End Date] between B.ArrivDate and (B.ArrivDate + B.DURSTAY))) AS BKD
ON R.RoomNo = BKD.RoomNo
WHERE (((BKD.RoomNo) Is Null));
or
an easier, more elegant query would be something like:
SELECT R.*, #06/05/2014# as [Start Date] , #06/10/2014# as [End Date]
FROM ROOMS AS R
WHERE R.RoomNo not in
(
SELECT DISTINCT RoomNo FROM Bookings B
WHERE (#06/05/2014# between B.Arrival and (B.Arrival + B.Duration))
OR (#06/10/2014# between B.Arrival and (B.Arrival + B.Duration))
);
The date parameters must be passed in the american format (#MM/DD/YYYY#)
Related
I am running SQL Server 2008 R2 (RTM).
I have a SQL query that pulls Dates, Products, Customers and Units:
select
[Transaction Date] as Date,
[SKU] as Product,
[Customer Name] as Customer,
sum(Qty) as Units
from dataset
where [Transaction Date] < '2019-03-01' and [Transaction Date] >= '2016-01-01'
group by [Transaction Date], [SKU], [Customer Name]
order by [Transaction Date]
This pulls hundreds of thousands of records and I wanted to determine if a certain transaction was a new order or reorder based on the following logic:
Reorder: That specific Customer has ordered that specific product in the last 6 months
New Order: That specific Customer hasn’t ordered that specific product in the last 6 months
For that I have this formula in Excel that seems to be working:
=IF(COUNTIFS(A$1:A1,">="&DATE(YEAR(A2),MONTH(A2)-6,DAY(A2)),C$1:C1,C2,B$1:B1,B2),"Reorder","New Order")
The formula works when I paste it individually or in a smaller dataset, but when I try to copy paste it to all 500K+ rows, Excel gives up because it loops for each calculation.
This could probably be done in SQL, but I don’t have the knowledge on how to convert this excel formula to SQL, I just started studying it.
You're doing pretty well with the start of your query there. There are three additional functions you're looking to add to your query.
The first thing you'll need is the easiest. GETDATE() simply returns the current date. You'll need that when you're comparing the current date to the transaction date.
The second function is DATEDIFF, which will give you a unit of time between two dates (months, days, years, quarters, etc). Using DATEDIFF, you can say "is this date within the last 6 months". The format for this is pretty easy. It's DATEDIFF(interval, date1, date2).
The thrid function you're looking for is CASE, which allows you to tell SQL to give you one answer if one condition is met, but a different answer if a different condition is met. For your example, you can say "if the difference in days is < 60, return 'Reorder', if not give me 'New Order'".
Putting it all together:
SELECT CASE
WHEN DATEDIFF(MONTH, [Transaction Date], GETDATE()) <= 6
THEN 'Reorder'
ELSE 'New Order'
END as ORDER_TYPE
,[Transaction Date] AS DATE
,[SKU] AS PRODUCT
,[Customer Name] AS CUSTOMER
,Qty AS UNITS
FROM DATASET
For additonal examples on CASE, take a look at this site: https://www.w3schools.com/sql/sql_ref_case.asp
For additional examples on DATEDIFF, take a look here: See the
following webpage for examples and a chance to try it out:
https://www.w3schools.com/sql/func_sqlserver_datediff.asp
SELECT CASE
WHEN Datediff(day, [transaction date], Getdate()) <= 180 THEN 'reorder'
ELSE 'Neworder'
END,
[transaction date] AS Date,
[sku] AS Product,
[customer name] AS Customer,
qty AS Units
FROM datase
If I understand correctly, you want to peak at the previous date and make a comparison. This suggests lag():
select (case when lag([Transaction Date]) over (partition by SKU, [Customer Name] order by [Transaction Date]) >
dateadd(month, -6, [Transaction Date])
then 'Reorder'
else 'New Order'
end) as Order_Type
[Transaction Date] as Date,
[SKU] as Product,
[Customer Name] as Customer,
sum(Qty) as Units
from dataset d
group by [Transaction Date], [SKU], [Customer Name];
EDIT:
In SQL Server 2008, you can emulate the LAG() using OUTER APPLY:
select (case when dprev.[Transaction Date] >
dateadd(month, -6, d.[Transaction Date])
then 'Reorder'
else 'New Order'
end) as Order_Type
d.[Transaction Date] as Date,
d.[SKU] as Product,
d.[Customer Name] as Customer,
sum(d.Qty) as Units
from dataset d outer apply
(select top (1) dprev.*
from dataset dprev
where dprev.SKU = d.SKU and
dprev.[Customer Name] = d.[Customer Name] and
dprev.[Transaction Date] < d.[Transaction Date]
order by dprev.[Transaction Date] desc
) dprev
group by d.[Transaction Date], d.[SKU], d.[Customer Name];
I have following table with following relevant columns-
Invoices-
Invoice Number | Invoice Date | Invoice Status
Invoice status can have values : PENDING, SENT, COURIER, LR, CANCELLED, DONE
I want to order the records in this table such that,
Invoices older than 2 days but not having status CANCELLED and DONE should appear first
then
Invoices newer then 2 days but not having status CANCELLED and DONE should appear first
then
All invoices having CANCELLED or DONE should be last on the list
How to achieve this in SQL.
I am using SQL server and writing stored procedure is something I am not keen to do it.
Ideally it should be single SQL statement.
What would be the solution for this problem?
You do this using expressions in the order by:
select i.
from invoices i
order by (case when invoicedate < dateadd(day, -2, getdate()) and
status not in ('Cancelled', 'Done')
then 1
when status not in ('Cancelled', 'Done')
then 2
then 3
end);
A stored procedure is not necessary for this (nor in my opinion is it desirable).
Please try the following...
SELECT [Invoice Number],
[Invoice Date],
[Invoice Status]
FROM Invoices
WHERE [Invoice Status] NOT IN ( 'CANCELLED', 'DONE' )
AND DATEADD( DAY, -2, GETDATE() ) > [Invoice Date]
UNION
SELECT [Invoice Number],
[Invoice Date],
[Invoice Status]
FROM Invoices
WHERE [Invoice Status] NOT IN ( 'CANCELLED', 'DONE' )
AND DATEADD( DAY, -2, GETDATE() ) <= [Invoice Date]
UNION
SELECT [Invoice Number],
[Invoice Date],
[Invoice Status]
FROM Invoices
WHERE [Invoice Status] IN ( 'CANCELLED', 'DONE' );
In each of by SELECT statements I have chosen to show all the fields and in the same order. Note that the corresponding fields in the SELECT statements making up a UNION should always be of the same type. In some situations a field of one type may be converted to match another, but this can sometimes lead to troubles and it is strongly recommended that you try to avoid such situations.
In my first statement I compare the date two days before the current date to the [Invoice Date] and if it is greater (i.e the Invoice is older than 2 days) then the row is included (subject to its [Invoice Status]).
For the second statement I choose invoices where the date is no more than two days before the current one.
For the third statement I show all Invoices where the status is either CANCELLED or DONE.
You can specify sorts, groupings, etc. of similar or different patterns within each list, but since you have not specified any such in your Question I have not attempted to implement them.
If you have any questions or comments, then please feel free to post a Comment accordingly.
I had a question up earlier and it was pretty much solved expect one part of it. for some reason its not showing me between dates for my query. Just the dates that i enter in the parameter. i was wondering if anyone can see any problem with the code. Any help is much appreciated.
Relationships
SELECT *
FROM Vehicles
WHERE Vehicles.vehicle_id NOT IN (
SELECT Booking.[vehicle id]
FROM Booking
WHERE (
[Enter Start Date] BETWEEN booking.start_rent_date
AND booking.end_rent_date
)
OR (
[Enter End Date] BETWEEN booking.start_rent_date
AND booking.end_rent_date
)
);
It's supposed that [Enter End Date] is greater than [Enter Start Date].
First, try if this single query works fine:
SELECT Booking.*
FROM Booking
WHERE (((Booking.start_rent_date)>#2016/11/1#)
AND ((Booking.end_rent_date)<#2016/11/20#));
Second, try to join with distinct values:
SELECT *
FROM Vehicles
WHERE Vehicles.vehicle_id NOT IN
(
SELECT distinct Booking.[vehicle id]
FROM Booking
WHERE (((Booking.start_rent_date)>#2016/11/1#)
AND ((Booking.end_rent_date)<#2016/11/20#))
);
I'm using Advantage Database Server by Sybase. I need to elimiate duplicate addbatch's from my report, but having trouble pulling up just distinct records. Any idea what I am missing?
here is what I am using
SELECT DISTINCT
SI.[addbatch] as [Batch#],
SI.[current account #] as [Account],
SI.[status date] as [Status Date],
SI.[SKU] as [SKU],
AC.[email address] as [Email]
FROM salesinventory SI, accounts AC
WHERE AC.[account #]=SI.[current account #] and [Status Date] > '6/1/2015'
I still get duplicate addbatch's though. I'm not sure where I am going wrong! Thanks in advance! Wasn't even sure how to google this question!
The problem is that you need to check uniqueness of a single column and that's not actually what your code is performing. Try this
SELECT *
FROM (SELECT SI.[addbatch] as [Batch#],
SI.[current account #] as [Account],
SI.[status date] as [Status Date],
ETC,
ROW_NUMBER() OVER (PARTITION BY [Batch#]) AS RowNumber
FROM salesinventory SI, accounts AC
WHERE AC.[account #]=SI.[current account #] and [Status Date] > '6/1/2015') as rec
WHERE rec.RowNumber = 1
-- The following code is generic to de-duplicate records, modify to suit your need.
select x.[Well_Name] as nameX
, x.[TestDate] as dateX
from (
SELECT count(*) as dup
,[Well_Name]
,[TestDate]enter code here
FROM [dbo].[WellTests]
group by [TestDate] ,[Well_Name] ) x
where dup > 1
If you want to have unique batch numbers in your result, you have to GROUP BY the batch field only.
Something like this should work:
SELECT
SI.[addbatch] as [Batch#],
MIN(SI.[current account #]) as [Account],
MIN(SI.[status date]) as [Status Date],
MIN(SI.[SKU]) as [SKU],
MIN(AC.[email address]) as [Email]
FROM salesinventory SI, accounts AC
WHERE AC.[account #]=SI.[current account #] and [Status Date] > '6/1/2015'
GROUP BY
SI.[addbatch]
You didn't say how you want to aggregate the other columns, just replace MIN with something that makes more sense for you, like SUM or COUNT, etc.
There is a topic about grouping in the documentation.
PS: SELECT DISTINCT is (basically) just a shorter way to GROUP BY on all columns without any aggregation.
I have a query, where I need the MIN of a DateTime field and then I need the value of a corresponding field in the same row.
Now, I have something like this, however I cannot get Price field without putting it also in an aggregate clause, which is not what I want.
SELECT MIN([Registration Time]), Price FROM MyData WHERE [Product Series] = 'XXXXX'
I need the MIN of the Registration Time field and then I just want the corresponding Price field for that row, however how do I show that?
I do also need my WHERE clause as shown.
I'm sure I've overlooked something really obvious. Using SQL Server 2008
If you want just one record with [Registration Time], Price, it'd be as simple as this:
select top 1 [Registration Time], Price
from MyData
where [Product Series] = 'XXXXX'
order by [Registration Time]
If you want minimum [Registration Time] and corresponding Price for all [Product Series], then there's a few approaches, for example, using row_number() function:
with cte as (
select
[Registration Time], Price,
row_number() over(partition by [Product Series] order by [Registration Time]) as rn
from MyData
)
select
[Registration Time], Price, [Product Series]
where rn = 1