Better way to write SQL where clause with date - sql-server

I am running a SQL Statement, shown below. This is ultimately going to be used to populate a reporting table nightly with the days transactions. When I run it without the
Cast(M._CreateDateTime as Date) = #StartDate
It takes 1:40.
With the function back in the where clause it takes over 10 minutes.
Declare #StartDate DAte = GetDate()
If Object_ID ('tempdb.dbo.#WinBack') is not null Drop Table #WinBack
Select
M.HospitalMasterID
,M.TxnSite
,P.ClientID
,M.PatientID
,M.TxnDate as CallDate
,M.TxnCode
,M.EnteredMasterID
,U.UserName
,U.UserDept
Into
#WinBack
From
Avimark_OLTP.dbo.MedicalHistory as M
Inner JoinAvimark_OLTP.dbo.Patient as P on
M.HospitalMasterID = P.HospitalMasterID and
M.PatientID = P.PatientID
Left Outer Join RptSys.dbo.Ref_User as U on
M.EnteredMasterID = U.UserMastID
Where
Cast(M._CreateDateTime as Date) = #StartDate and
M.TxnCode = 'RemCall' and
U.UserName is not null
Does anyone have any suggestions on how to speed this up? I know it is the cast part that is killing it.
Thanks,

Using function for a column makes SQL Server both cast every single row in the table it's comparing and preventing index usage, you should do it like this:
Declare #StartDate Date, #EndDate Date
set #StartDate = GetDate()
set #EndDate = dateadd(day, 1, #StartDate)
select
...
where
M._CreateDateTime >= #StartDate and
M._CreateDateTime < #endDate

Related

Automate an SSIS query for a table name having a month and year timestamp

I have a database in which a table names are associated with month and year numbers ex: datavalues_7_2017 for july, datavalues_8_2017 for august and so on.
I am using a query to retrieve certain values from the table.
SELECT o_key ,MIN(dateadd(hour, datediff(hour, 0, time), 0)) as on_time
,MAX(dateadd(hour, datediff(hour, 0, dateadd(hour, 1, time)), 0)) as off_time
,cast(time as date) as RDNG_DT ,[repeated_hour],[value]
FROM [data_values_8_2017]
WHERE value = 1 and o_key in (X,X,X,X...) and cast(time as date) = cast(getdate() as date)
GROUP BY o_key,cast(time as date),repeated_hour,value
I am using SSIS package and using this query I am loading the result into another table.
Now this table datavalues_X_2017 is bound to change every month creating a new table, and my SSIS query should be pointing to the new table.
Can someone suggest me a way where I can automate this processes.
All you need to do is create a stored procedure as a wrapper and have a date parameter passed to it as shown below. Call this procedure as part of your source.
If you do not want it as stored procedure, use the same logic and put the sql as a variable and in the expressions pass the month and year variable
create procedure dbo.usp_GetData
(
#Date datetime = null
)
as
if #Date is null
set #Date = GETDATE()
declare #Month int
,#Year int
,#sql nvarchar(max)
select #Month = DATEPART(MONTH,#Date)
,#Year = DATEPART(YEAR,#Date)
set #sql ='
select o_key
,MIN(dateadd(hour, datediff(hour, 0, time), 0)) as on_time
,MAX(dateadd(hour, datediff(hour, 0, dateadd(hour, 1, time)), 0)) as off_time
,cast(time as date) as RDNG_DT
,[repeated_hour],[value]
from [data_values_'+CAST(#Month AS VARCHAR(2))+'_'+CAST(#Year AS VARCHAR(4))+']
where value = 1
and o_key in (
X,X,X,X...
)
and cast(time as date) = cast(getdate() as date)
group by o_key
,cast(time as date)
,repeated_hour
,value
';
exec sp_executesql #sql;
go
Get dynamic SQL with Variable Expressions the following way:
Create a String variable DateSuffux which will be filled with your datevalue like "7_2017"
Create a String variable SQLQuery with properties EvaluateAsExpression=true and define the following Expression for it
"SELECT o_key ,...,[repeated_hour],[value] FROM [data_values_" +
#[User::DateSuffix] + "] WHERE ... "
Specify variable SQLQuery as a source for your Data Source or SQL Query task
You are done.
This variable expression substitutes result of the expression when the SQLQuery variable is used. The expression itself injects contents of DateSuffix string variable into a query text.

How to get date from yyyy-mm-dd to yyyy-mm-dd in SQL?

I want to get date from yyyy-mm-dd to yyyy-mm-dd in SQL.
Example: I have two parameter #startdate : 2015-12-28 and #enddate : 2016-01-02, and database in SQLServer, datatype is varchar(10)
DATE_ORDER
28-12-2015
30-12-1996
29-12-2016
30-12-1997
24-12-2015
27-12-1993
03-01-2016
01-01-1992
02-01-2016
etc...
Ok,now I want to get data from #startdate : 2015-12-28 and #enddate : 2016-01-02. I use SELECT * FROM TABLE_X WHERE DATE_ORDER >= #startdate AND DATE_ORDER <= #enddate . But the results are not what I expected. Here are the results I want
28-12-2015
30-12-1996
29-12-2016
30-12-1997
01-01-1992
02-01-2016
I think to solve this problem, I need to do two things :
First, get date range from #startdate to #enddate , in here 28/12/2015, 29/12/2015, 30/12/2015, 31/12/2015, 01/01/2016, 02/01/2016.
The second: get the date in database same in range 28/12, 29/12, 30/12, 31/12, 01/01, 02/01, ignoring the year.
Can you give me some ideas about this ?
Your actual format is "105-italian" find details here.
You can convert your existing VARCHAR(10)-values with this line to real datetime
SELECT CONVERT(DATETIME,YourColumn,105)
Next thing to know is, that you should not use BETWEEN but rather >=StartDate AND < NakedDateOfTheFollowingDay to check date ranges
So to solve your need Get date-range from 2015-12-28 to 2016-01-02 you might do something like this:
DECLARE #Start DATETIME={d'2015-12-28'};
DECLARE #End DATETIME={d'2016-01-02'};
SELECT *
FROM YourTable
WHERE CONVERT(DATETIME,YourDateColumn,105)>=#Start AND CONVERT(DATETIME,YourDateColumn,105)<#End+1
Attention Be aware, that the conversion lets your expression be not sargable. No index will be used.
Better was to store your date as correctly typed data to avoid conversions...
Try this query
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
So, after the 2nd or 3rd edit, it slowly becomes clear, what you want (i hope).
So you REALLY WANT to get the dates with the year beeing ignored.
As someone pointed out already, date-values are stored internally not as string, but as internal datatype date (whatever that is in memory, i don't know).
If you want to compare DATES, you cannot do that with ignorance of any part. If you want to, you have to build a NEW date value of day and month of given row and a hard coded year (2000 or 1 or whatever) for EVERY row.
SELECT * FROM TABLE_X WHERE convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) >= #startdate AND convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) <= #enddate
If your startdate and enddate go OVER sylvester, you have to do 2 queries, on from startdate to 1231, one from 0101 to enddate.

Date Exists between Range of Dates not working

ALTER PROCEDURE [dbo].[spInsert]
(#PlanName Varchar(50)=null
,#StartDate Datetime
,#EndDate Datetime
,#ModifiedBy Varchar(100)=null
,#ReturnValue Int Out)
As
BEGIN
IF NOT EXISTS(SELECT PlanName FROM dbo.tblPlan WHERE PlanName=#PlanName)
BEGIN
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <=
#EndDate)<0)
BEGIN
INSERT INTO dbo.tblPlan VALUES(3,#PlanName,#StartDate,#EndDate,#ModifiedBy,GETDATE(),
(SELECT DATEDIFF(DD,#StartDate,#EndDate)))
SET #ReturnValue=1;
END
ELSE
SET #ReturnValue=-2;
END
ELSE
SET #ReturnValue=-1;
END
I am trying to achieve the below thing.I want to check user supplied startDate and Enddate is in between the existing table startdate and enddate. if any of the date of user supplied date range is in between the tables start date and end date,it should return -2,if the record does not exists it should insert the details..
I Could not achieve this logic.where i went wrong ..please suggest me any solution to this.
EDIT:First Consditon check whether planName is exists or not,If not exists then want to check Start and End Date already existed or Not(Including start and End)
I tried two ways as suggested mentioned in the replies.
eg:If existing start and end range is Start-2013-10-09,End-2013-10-15,If am going to insert another plan then the start and end date of this plan should not fall between 9th-15th october and start and End date should not be 9th or 15 th both.
ONE:IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <=
#EndDate)=0)
Result: It does not insert any data, even it is out of previous date. or with in the
range
SECOND:IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate>=#StartDate AND
EndDate<=#EndDate)=0)
RESULT: It insert the date with out Considering the above condition.
I think you need to change your if from
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <= #EndDate)<0)
to
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate >= #EndDate)=0)
Which should ensure that #StartDate and #EndDate is between StartDate and EndDate and test for =0
COUNT(*) can never be smaller than zero like your code suggests.
It's either a positive integer (which is greater than zero), or null, which will also return false on any arithmetic condition.
try the below :
ALTER PROCEDURE [dbo].[spInsert]
(#PlanName Varchar(50)=null
,#StartDate Datetime
,#EndDate Datetime
,#ModifiedBy Varchar(100)=null
,#ReturnValue Int Out)
As
BEGIN
IF NOT EXISTS(SELECT PlanName FROM dbo.tblPlan WHERE PlanName=#PlanName)
BEGIN
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate >= #StartDate AND EndDate <=
#EndDate)=0)
BEGIN INSERT INTO dbo.tblPlan
VALUES(3,#PlanName,#StartDate,#EndDate,#ModifiedBy,GETDATE(),
(SELECT DATEDIFF(DD,#StartDate,#EndDate)))
SET #ReturnValue=1;
END
ELSE
SET #ReturnValue=-2;
END
ELSE
SET #ReturnValue=-1;
END
As count always return positive value or zero. so your first condition always being false.
*UPDATE:*
you want to say if Sdate is '12-12-2013' and edate is '15-12-2013' then you don't want to considers these dates in check if so then try replace the query with below:
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate > #StartDate AND EndDate <
#EndDate)=0)
If you're wanting to check whether the period defined by #StartDate,#EndDate overlaps with any period defined by the values in the StartDate,EndDate columns for a particular row, then the actual comparison that you want to perform is:
StartDate < #EndDate AND EndDate < #StartDate
(With appropriate adjustments of < to <= depending on whether you want to consider two periods such that one starts at the exact time that the other finishes as overlapping or not)
The logic is - Two periods overlap if both of the following conditions are true:
period 1 starts before period 2 ends
period 2 starts before period 1 ends
Other notes -
A COUNT will never be <0 so that part of the logic is incorrect. If you merely want to determine whether any rows exists, use EXISTS (as you already have once) - don't force a COUNT if you don't actually need to know how many rows match your criteria.
I also find it slightly suspect that your first query considers PlanName but your second doesn't. Are you sure that this is correct?

SQL Server Indexes of DateTime Parts such as YEAR, MONTH

Is it possible to have indexes on DateTime parts such as DATEPART(YEAR, bp.[CreatedOn]) and DATEPART(MONTH, bp.[CreatedOn])? For example, I have the following T-SQL query:
DECLARE #year AS INT = 2012;
DECLARE #month AS INT = 8;
SELECT bp.Title, bp.CreatedOn FROM BlogPosts bp
WHERE (DATEPART(YEAR, bp.[CreatedOn]) = #year) AND (DATEPART(MONTH, bp.[CreatedOn]) = #month)
ORDER BY bp.CreatedOn;
And this is the execution plan I have: https://gist.github.com/3551450
Currently, there are not many records so it is not a big problem in terms of perf but the records will grow over the time.
You'd better construct criterion using datetime field:
declare #startMonth datetime = '20120801'
SELECT bp.Title, bp.CreatedOn
FROM BlogPosts bp
WHERE bp.[CreatedOn] >= #startMonth
AND bp.[CreatedOn] < dateadd (month, 1, #startMonth)
ORDER BY bp.CreatedOn;
This way query executor will be able to use index on CreatedOn.
Yes is it possible to put index on year and month. Here is an example:
create table testt(d datetime)
alter table testt add year as year(d) PERSISTED -- after marc_s advise. Thx
alter table testt add month as month(d) PERSISTED --
create index idx_year on testt(year)
create index idx_month on testt(month)
However I would always use a variation of Nikola's solution, so +1 to Nikola.
This is how you can rewrite month and year into a date:
DECLARE #year AS INT = 2012;
DECLARE #month AS INT = 8;
DECLARE #from DATE = dateadd(month, (#year-1900)*12 + #month - 1, 0)
I suggest you store date parts on different fields. Clearly this is a requirement which exist in your domain as such needs to be part of your model.
What you lose is that you have to construct the datetime for every model you load or you set the value but that would be a few CPU cycles.

how to find between dates in sql server 2008?

I give two different date as a input. i want to find, what are date between two date..
For eg:
1'st Input is : 2011-02-20
2'nd input is : 2011-02-25
Output is:
2011-02-20
2011-02-21
2011-02-22
2011-02-23
2011-02-24
2011-02-25
Is there any function to find the between date...
otherwise how to find ?
You can use a CTE to achieve this.
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2011-02-20'
SET #EndDate = '2011-02-25'
;WITH GetDates AS
(
SELECT 1 as counter, #StartDate AS Date
UNION ALL
SELECT counter + 1, DATEADD( day, counter, #StartDate )
FROM GetDates
WHERE DATEADD( day, counter, #StartDate ) <= #EndDate
)
SELECT Date FROM GetDates
See:
Why should I consider using an auxiliary calendar table?
A calendar table can make it much
easier to develop solutions around any
business model which involves dates.
Last I checked, this encompasses
pretty much any business model you can
think of, to some degree. Constant
problems that end up requiring
verbose, complicated and inefficient
methods include the following
questions: How many business days
between x and y? ...
SELECT Date = #StartDate + number
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 0 AND DATEDIFF(day, #StartDate, #EndDate)
Note: This solution is not applicable when the distance between the two dates is more than 2047 days.

Resources