SQL, is a date within range of two dates - c#

I have an SQL Server Database, managed through SQL Server MS, interfacing with a c# application and i am struggling with a certain query.
The database is for a campsite booking system, which consists of the following tables relevant to the query.
BOOKING(BookingID, StaffID, CustomerID, PitchID, StartDate, EndDate)
PITCH(PitchID, TypeOfPitch, Capacity)
One pitch can occur in many bookings.
I am looking to create a query which will check the availability of a pitch on a certain date, which is input from a dateTimePicker. The query will return the available pitches and display them in a datagridview. Here is what i have so far.
SELECT * FROM dbo.PITCH, dbo.Booking
WHERE #Date
NOT BETWEEN dbo.BOOKING.[Start Date] AND dbo.BOOKING.[End Date]
This SQL code is not working, it is returning a pitch for every booking in the table.
All the c# around the SQL is working, i'm just not great at SQL queries and need some help!
Thanks in advance

SELECT * FROM dbo.PITCH
WHERE PitchID NOT IN
(
-- sub-query to take reserved pitches
select PitchID from dbo.Booking
where #Date BETWEEN [Start Date] AND [End Date]
)

The following should do what you want. The sub query pulls the pitches that are booked during the given date. So you want the pitches not in that query.
Select *
From dbo.PITCH
WHERE PitchID Not In (
select PitchID
from dbo.Booking
where #Date between [Start Date] And [End Date])

DECLARE #DATE DATETIME
SELECT
,b.BookingID
,b.StaffID
,b.CustomerID
,b.PitchID
,b.StartDate
,b.EndDate
,p.capacity
from Dbo.Pitch p
INNER JOIN Booking b ON b.PitchID = p.PitchID
WHERE #Date BETWEEN b.StartDate AND b.EndDate
ORDER BY #DATE DESC
Since you want the show the date picked from the PICKER at the top you need ORDER BY CLAUSE as well .
Get in the habit of writing like this as it will help you easily solve as well as analyse the problem .
HOPE IT HELPS

Related

Return zero for monthly totals between two dates even when no data exist for those months

I have the following query which returns totals from a transactions tables grouped by month and year:
SELECT
DATEPART(month, T.Date) AS Month,
DATEPART(year, T.Date) AS Year,
ISNULL(SUM(Amount) ,0) AS Total
FROM
Transactions AS T
GROUP BY
DATEPART(month, T.Date), DATEPART(year, T.Date)
This query will only return monthly totals for those months which have transactions for that month and year in the transactions table.
How can I modify the query above so that it will return 0 for all months and years between two dates?
I suspect I need to cross reference the query above with a table that needs to be built dynamically containing all the months and years but I'm not sure how to go about this.
You actually don't need to have an additional table, if all you want is months and years. You can simulate one, with a recursive CTE (Common Table Expression).
DECLARE #startDate datetime = '1/1/2020';
DECLARE #endDate datetime = '1/1/2023';
-- This is a recursive Common Table Expression. They allow you to generate
-- sets of records from seemingly nothing.
-- A recursive CTE involves two SELECT statements joined with a UNION ALL statement.
-- The first statement is the "anchor". It is usually the start of the sequence
-- you're trying to generate. In our case, we say "select our start date
-- as the first record in the sequence we're generating".
-- The second SELECT statement refers recursively to the first one. (I
-- think it may be called "the generator clause", but I'm not sure.) We
-- refer to the record or records selected in the first statement, but
-- add or modify the date to produce the second and subsequent records.
-- So, in our case, we're saying, "Select record(s) from the CTE, and
-- this time, derive our value ("ReferenceDate") by adding a month to
-- the value we're reading."
-- This will continue indefinitely, if you query it. That's why we need
-- a test (the WHERE clause) to limit the records we return, or you'll
-- quickly reach the year 10,000!
WITH Calendar AS (
SELECT #startDate as ReferenceDate
UNION ALL
SELECT DateAdd(month, 1, c.ReferenceDate)
FROM Calendar c
WHERE c.ReferenceDate < #endDate
),
-- The Calendar CTE is a list of the first date of every month. But
-- for our report we need the year and the month, separately. So,
-- let's produce a second CTE (akin to a derived table) that returns
-- the year and month for every date in Calendar.
YearMonths AS (
SELECT datepart(year, ReferenceDate) as [Year]
, datepart(month, ReferenceDate) as [Month]
FROM Calendar
),
-- This CTE is your summary data. We're going to want to join it
-- to YearMonths, and we need it already summarized.
-- (We could have done this as a derived table in the final select,
-- but IMO that would have complicated it without giving any advantage.
-- CTEs perform like subqueries or derived tables.)
SummaryTransactions AS (
select
DATEPART(month, T.Date) as Month,
DATEPART(year, T.Date) as Year,
ISNULL(SUM(Amount) ,0) As Total
from Transactions AS T
GROUP BY
DATEPART(month, T.Date),
DATEPART(year, T.Date)
)
-- The final query!
SELECT ym.Year
, ym.Month
, ISNULL(st.Total, 0) as Total
FROM YearMonths ym
LEFT JOIN
SummaryTransactions st
ON ym.Year = st.Year
AND ym.Month = st.Month
That was from memory, so I can't guarantee it is free from typos, but it ought to work.
The Calendar CTE acts as the "table in memory" you talk about: it will generate a set of records for the first of each month between the start and end dates, without actual records being created anywhere. It's a handy trick!
Note that you'll need to set a date range for your Calendar CTE: you need a fixed start date, for the first record in your CTE, and if you don't set an end date it will continue indefinitely!

How to select records for a given date from SQL Server system-versioned table

I am using SQL Server 2019.
I am using a system-versioned table like that:
CREATE TABLE Employee
(
EmployeeID int PRIMARY KEY NOT NULL,
FirstName varchar(100) NOT NULL,
LastName varchar(100) NOT NULL,
-- Some other columns
StartTime datetime2 (7) GENERATED ALWAYS AS ROW START NOT NULL,
EndTime datetime2(7) GENERATED ALWAYS AS ROW START NOT NULL,
PERIOD FOR SYSTEM_TIME (StartTime,EndTime)
)
WITH (SYSTEM_VERSIONING = ON(HISTORY_TABLE = dbo.EmployeeHistory));
The names of the table and the columns are changed, but this is how the table is created.
My question is pretty simple, but I can not find satisfying answer.
This table is available for the end users and one of the options is to provide DateTime SearchDate and I want to return all records in the Employee table which are made this Date.
However, even though the user may send not just Date but also Time the response should contain all records made the given Date in SearchDate and ignore the time.
Basically the queries which I found relate to system-versioned table are pretty simple:
SELECT *
FROM dbo.Employee
FOR SYSTEM_TIME BETWEEN #SearchDate AND ???
WHERE dbo.Employee.EmployeeID=2
After my research what I found is that I can take advantage of the system-versioned table and search after given DateTime or search in given DateTime range for which I found several different syntax which seems like at the end they provide the same result. Sadly, I wasn't able to find something built in which will search the entire date.
So my question is - is there way to take advantage of the fact that I am using system-versioned tabled and filter all records for a given Date no matter the Time component.
If not, I think that a decent workaround would be to use this clause FOR SYSTEM_TIME BETWEEN. As input I get DECLARE #SearchDate DATETIME2(7) so I would accept as an answer also a query which will the this #SearchDate and transform it in a way where I can extract the correct format of a DateTime which will indicate the very start of the #SearchDate date and the very end of the #SearchDate date so I can use those value in the BETWEEN syntax?
You can just use DATEADD and CAST to get the start and end of the day
DECLARE #startDate datetime2(7) = CAST(#SearchDate AS date);
DECLARE #endDate datetime2(7) = DATEADD(day, 1, #startDate);
SELECT *
FROM dbo.Employee
FOR SYSTEM_TIME BETWEEN #startDate AND #endDate
WHERE dbo.Employee.EmployeeID = 2;
Note that for BETWEEN, the start point is exclusive and the end point is inclusive.

How to get data from database in time range if it's not contained before the time range?

I have a table that contains some data like:
Product ID
Operation
Date
Result (OK or NOK).
I'd like to set a time range. In that time range I'd like to get the first Result of all Product ID by Date.
After that I'd like to check the whole table (out of the time range too), if there is an earlier test Result of those PID-Operation-Result "pack" what I got from the selected time range. How should I do that?
you can use something like this query
SELECT * FROM YOUR_TABLE WHERE Date Between '01/01/2016' and '10/06/2016'
You want to select one record for each PID, the oldest. But if it's not in the time range you want all results, even if there are multiple for one ID? Is that correct?
Presuming you're using SQL-Server you could use a ranking function:
WITH CTE AS
(
SELECT [Product ID],
[Operation],
[Date],
[Result],
RN = ROW_NUMBER() OVER (PARTITION BY [Product ID] ORDER BY [Date])
FROM dbo.TableName
)
SELECT [Product ID], [Operation], [Date], [Result]
FROM CTE
WHERE RN = 1 OR [Date] NOT BETWEEN #BeginRange AND #EndRange

Why is exec sp_executesql much slower than inline sql?

I have test this query in management studio and this execute very fast(less than a second)
declare #p_Message_0 varchar(3) = 'whh'
declare #p_CreatedTime_0 datetime = '2015-06-01'
SELECT count(1) FROM (SELECT * FROM [Logs](nolock) WHERE CONTAINS([Message], #p_Message_0) AND [CreatedTime]<#p_CreatedTime_0) t
SELECT t2.* FROM (SELECT t.*,ROW_NUMBER() OVER (ORDER BY Id DESC) as rownum FROM (SELECT * FROM [Logs](nolock) t WHERE CONTAINS([Message], #p_Message_0) AND [CreatedTime]<#p_CreatedTime_0) t) t2 WHERE rownum>0 AND rownum<=20
execution plan like this:
then I move it into C# ado.net, it run as this
exec sp_executesql N'SELECT count(1) FROM (SELECT * FROM [Logs](nolock) WHERE CONTAINS([Message], #p_Message_0) AND [CreatedTime]<#p_CreatedTime_0) t
SELECT t2.* FROM (SELECT t.*,ROW_NUMBER() OVER (ORDER BY Id desc) as rownum FROM (SELECT * FROM [Logs](nolock) t WHERE CONTAINS([Message], #p_Message_0) AND [CreatedTime]<#p_CreatedTime_0) t) t2 WHERE rownum>0 AND rownum<=20',N'#p_Message_0 varchar(3),#p_CreatedTime_0 datetime',#p_Message_0='whh',#p_CreatedTime_0='2015-06-01'
this one run really slow(about 30s). execution plan like:
I don't know what make these two plan different. Sql server is 2008 R2 with SP2, and I have tried parameter hint and OPTION (RECOMPILE), both not work for me.
Try updating statistics. The first one uses a variable with today's date. Variables aren't sniffed so you will get a guessed distribution. The second one uses a parameter. This can be sniffed.
If the stats haven't been updated today SQL Server will think no rows exist for that date so will give a plan on that basis. Such as a nested loops plan that is estimated to execute the TVF once but actually execute it many times.
AKA the ascending date problem.

Retrieve only todays data from SQL Server database

I need to retrieve only today's data from a SQL Server database table.
I am developing an application in C#, VS 2010 and there is need that to retrieve only today's data.
Not getting the exact query how should it be. Also need to retrieve that data order by Desc.
How can we do this?
Yoou have to take the Date_time Column in Your TABLE That will keep the information of time when your row entered in table.
SELECT Date_Time
FROM TableThantYouWantAcees
WHERE Date_Time = CONVERT(date, Getdate())
ORDER BY Date_Time DESC
GETDATE() is an SQLServer function which will return the today date, like 2013-11-20 14:05:54.943. Cast it in DATE to only keep the date part, ie 2013-11-20.
Try this
SELECT * FROM <table_name>
WHERE <date_field> >= CONVERT(<date_field>,
DATEDIFF(DAY, 0, GETDATE()))ORDER BY <date_field> DESC
Extract the date part from your date_column with convert
select * from your_table
where CONVERT (DATE, date_column) = CONVERT (DATE, GETDATE())
order by date_column desc
If you are concerned about only having the date and not the time, you could so something like this:
SELECT *
FROM tblPhoneAsign
WHERE receivedTime > CAST(FLOOR(CAST(GETDATE() AS FLOAT))AS DATETIME)
That will give you only the date portion of today's date. Doing the adding of zero hours to the GETDATE method is a very complex and costly way of doing things.

Categories