SQL Server - best approach for searching between two dates - sql-server-2005

Hello and thanks in advance for taking a look at this.
I have discovered an issue in 45 stored procedures that someone else wrote, she commented that the performance has slipped considerably. I took a look and spotted the problem in about 5 minutes, ran a test and went from 60 seconds down to 4 seconds for one of the 45. An index was not being used and a table scan was occurring on a table with 10 million + records. This is using SQL Server 2005.
The table is an audit log and is queried by a stored proc to pull the updt_tmstmp when a record has a specific value. I changed the below block of code to use "NOT IN" vs 8 "product_code <> 'XX' " statements, changed the first datediff to use the indexed column updt_tmstmp and also added the check that the AUDIT_LOG.updt_tmstmp > #dtStartDate to achieve the peformance increase. I just feel this could be implemented differently (more elegantly). I would appreciate any thoughts or ideas on improvements.
WHERE
PRODUCT.product_code NOT IN ('D01', 'D02', 'D03', 'D04', 'D05', 'D06', 'D07', 'D99') AND
AUDIT_LOG.updt_tmstmp >= #dtStartDate AND
--Compares that the date entered is between the two date parameters
(DATEDIFF(dd,GETDATE(),AUDIT_LOG.updt_tmstmp)
BETWEEN DATEDIFF(dd,GETDATE(),#dtStartDate)
AND DATEDIFF(dd,GETDATE(),#dtEndDate))
AND AUDIT_LOG.event_id = (SELECT MIN(AUDIT_LOG.event_id)
FROM L_EVENT_LOG
WHERE AUDIT_LOG.transaction_id = PRODUCT.transaction_id AND AUDIT_LOG.queue = 'AP')

The comparison against audit_log.updt_tmstmp looks a bit strange.
AUDIT_LOG.updt_tmstmp >= #dtStartDate AND
--Compares that the date entered is between the two date parameters
(DATEDIFF(dd,GETDATE(),AUDIT_LOG.updt_tmstmp)
BETWEEN DATEDIFF(dd,GETDATE(),#dtStartDate)
AND DATEDIFF(dd,GETDATE(),#dtEndDate))
I guess this would do the same.
audit_log.updt_tmstmp >= #dtStartDate and
audit_log.updt_tmstmp < #dtEndDate
I have no idea what to do with the correlated sub query. It uses fields from the outer query in the where clause and does not use any fields from l_event_log. You should probably move the where clause to the main query instead.
Perhaps something like this.
where product.product_code not in ( 'D01', 'D02', 'D03', 'D04',
'D05', 'D06', 'D07', 'D99' ) and
audit_log.updt_tmstmp >= #dtStartDate and
audit_log.updt_tmstmp < #dtEndDate and
audit_log.transaction_id = product.transaction_id and
audit_log.queue = 'AP' and
l_event_log.event_id = (select min(audit_log.event_id)
from l_event_log)

I'd chip in with GETDATE() being called three times. Not sure if that gets optimised away, but worth putting that in a variable to start with to see if that helps.

Related

How to create time slots of the day in SQL server

I apologize in advance if you think the question has already been answered, I searched on Stack, but similar answers were much too complex for me.
I have this request in SQL :
SELECT dbo.tbTransaction.dateHeure, dbo.tbPoste.nomPoste
FROM dbo.tbTransaction
INNER JOIN dbo.tbPoste ON dbo.tbTransaction.ID_poste = dbo.tbPoste.ID_POSTE
WHERE dbo.tbPoste.id_site LIKE 17
AND dbo.tbPoste.nomPoste LIKE 'POSTE1'
AND dateHeure >= DATEADD(dd, DATEDIFF(dd,0,GETDATE()), 0)
ORDER BY dateHeure DESC
It gives me all the production of the day, the problem is : it's not precise enough because there is a team shift in the afternoon and another team shift in the night.
If you see all the production of the day, you cannot see which team produced what.
I would like to create 3 different queries that allow me to see only the production between certain hours (for example, production between 6am and noon). Do you have any advice on how to do this?
I think you will have to hardcode your time slots. For example you know that morning shift is going to work from 06:00 to 12:00.
As you know the hours, you can use HOUR function on your datetime column with appropriate >, <, = and AND operators to get the required results.
From what I have understood, it should be something like this:
HOUR(datetime column) >= 6 AND HOUR (datetime column) < 12
Okay I managed to find the query I wanted :
SELECT dbo.tbTransaction.dateHeure, dbo.tbPoste.nomPoste FROM dbo.tbTransaction INNER JOIN dbo.tbPoste ON dbo.tbTransaction.ID_poste = dbo.tbPoste.ID_POSTE WHERE dbo.tbPoste.id_site LIKE 17
AND dbo.tbPoste.nomPoste LIKE 'POSTE1'
AND dateHeure >= DATEADD(dd, DATEDIFF(dd,0,GETDATE()), 0)
AND DATEPART(HOUR, dateHeure) > 6
AND DATEPART(HOUR, dateHeure) < 13
ORDER BY dateHeure DESC

Giving the wrong records when used datetime parameter in MS Access Query

I am working MS-Access 2007 DB .
I am trying to write the query for the Datetime, I want to get records between 14 December and 16 December so I write the bellow query.
SELECT * FROM Expense WHERE CreatedDate > #14-Dec-15# and CreatedDate < #16-Dec-15#
( I have to use the two dates for the query.)
But It returning the records having CreatedDate is 14 December...
Whats wrong with the query ?
As #vkp mentions in the comments, there is a time part to a date as well. If it is not defined it defaults to midnight (00:00:00). As 14-dec-2015 6:46:56 is after 14-dec-2015 00:00:00 it is included in the result set. You can use >= 15-dec-15 to get around this, as it will also include records from 15-dec-2015. Same goes for the end date.
It seems you want only records from Dec 15th regardless of the time of day stored in CreatedDate. If so, this query should give you what you want with excellent performance assuming an index on CreatedDate ...
SELECT *
FROM Expense
WHERE CreatedDate >= #2015-12-15# and CreatedDate < #2015-12-16#;
Beware of applying functions to your target field in the WHERE criterion ... such as CDATE(INT(CreatedDate)). Although logically correct, it would force a full table scan. That might not be a problem if your Expense table contains only a few rows. But for a huge table, you really should try to avoid a full table scan.
You must inlcude the time in your thinking:
EDIT: I wrote this with the misunderstanding, that you wanted to
include data rows from 14th to 16th of Dec (three full days).
If you'd write <#17-Dec-15# it would be the full 16th. Or you'd have to write <=#16-Dec-15 23:59:59#.
A DateTime on the 16th of December with a TimePart of let's say 12:30 is bigger than #16-Dec-15#...
Just some backgorund: In Ms-Access a DateTime is stored as a day's number and a fraction part for the time. 0.5 is midday, 0.25 is 6 in the morning...
Comparing DateTime values means to compare Double-values in reality.
Just add one day to your end date and exclude this:
SELECT * FROM Expense WHERE CreatedDate >= #2015/12/14# AND CreatedDate < #2015/12/17#
Thanks A Lot guys for your help...
I finally ended with the solution given by Darren Bartrup-Cook and Gustav ....
My previous query was....
SELECT * FROM Expense WHERE CreatedDate > #14-Dec-15# and CreatedDate < #16-Dec-15#
And the New working query is...
SELECT * FROM Expense WHERE CDATE(INT(CreatedDate)) > #14-Dec-15# and CDATE(INT(CreatedDate)) < #16-Dec-15#

SQL Server DELETE and SELECT Behave Differently with Same WHERE Clause

I have a table which is populated by a daily scheduled job that deletes the last 7 days of data and then repopulates with the 7 most recent days worth of data from another source (mainframe).
Recently, users reported a number of duplicates going back to the beginning of October 2011. ...in the magnitude of hundreds of thousand of rows.
I noticed strange behavior with the delete that runs for each job:
DELETE FROM fm104d
WHERE location = '18'
AND (CONVERT(datetime,CASE WHEN ISDATE(pull_date)=0 THEN '19000101'
ELSE pull_date END)) > DATEADD(day, -7, getdate())
The above returns "(0 row(s) affected)".
When I run the above after replacing the DELETE with a SELECT *, I get 32,000+ rows in return.
Why would the SELECT and DELETE behave differently?
UPDATE
Here is the Actual Execution Plan:
http://pastie.org/2869202
You won't believe this. I didn't in fact as it makes almost no logical sense, but in the end, the solution that worked...was to add an index.
Credit for this goes to my local DBA "Did think about adding an index? I just did to test and sure enough it works".
Here's the index as added:
CREATE  INDEX ixDBO_fir104d__SOURCE_LOCATION__Include
ON [dbo].[fir104d] ([SOURCE_LOCATION])
INCLUDE ([Transaction_Date],[PULL_DATE])
GO
I let the job run as scheduled and, sure enough, all is as it was.
My guess is that there is something in the explain plan to say it wasn't using an index / wrong index, but my developer mind can't make much sense of that level of detail.
Thanks to everybody for the time and effort you've all spent.
UPDATE
Received news from a different dev that the data in this table additionally corrupted to the point where it took "several hours of DBA involvement to resolve" along with the dev having to perform some other data fixes (read:data file reloads).
At the end of the day, while adding the index was probably a good thing considering the way the scheduled job runs, apparently, there was even more to the story!
Try this :
DELETE FROM fm104d where fm104d.id in
(
select id from fm104d
WHERE location = '18'
AND (CONVERT(datetime,CASE WHEN ISDATE(pull_date)=0 THEN '19000101'
ELSE pull_date END)) > DATEADD(day, -7, getdate())
)aaa
and give response if it deletes
p.s. : this is not the solution but will lead to decision.
One possible explanation might be that there are two tables, each in a different schema. Perhaps if you have select rights on both schema's but delete rights on only one, SQL Server might choose a different table for delete.
To verify this, prefix your table with the schema name (the default schema is dbo)
FROM schema1.fm104d
(Not tested, just a thought, no access to a SQL Server installation atm.)
For your select, add ISDATE(pull_date) to the select list to determine what part of the case statement these are affecting. Look at the pull_date as well and see if there's a pattern to the format of the string common among these offenders that refuse to be deleted.
This might have some relation to the determinism of Convert and IsDate:
"ISDATE is deterministic only if you use it with the CONVERT function, if the CONVERT style parameter is specified, and style is not equal to 0, 100, 9, or 109."
See the couple of examples here where convert is nested inside isdate:
http://www.sqlmonster.com/Uwe/Forum.aspx/sql-server-programming/181/CAST-CONVERT-nondeterministic
So try adjusting your where clause and see if that helps. Also note that "The return value of ISDATE may be affected by LANGUAGE and DATEFORMAT settings." So maybe something on your server has changed in these regards. Why it'd affect the delete but not the select is still strange.
How about trying this, see if you can evaluate your pull_date column first and then delete the records.
DELETE FROM fm104d
WHERE Location = 18
AND Pull_date IN
(
SELECT CONVERT (DATETIME,
CASE
WHEN ISDATE(pull_Date) = 0
THEN '19000101'
ELSE pull_date
END) AS pull_date
FROM fm104d
WHERE pull_date > DATEADD(DAY, -7, GETDATE())
)
It looks to me like you never want to delete when pull_date is not a date.
Try eliminating the explicit string replacements... perhaps there is a parsing different between the SELECT and DELETE
DELETE
FROM
fm104d
WHERE
[location] = '18' --NOTE if this is an int, then just try with 18, no dits
AND (
CASE ISDATE([pull_date])
WHEN 1 THEN
CAST([pull_date] AS DATETIME)
ELSE
NULL
END > DATEADD(DAY, -7, GETDATE())
)
EDIT: Note that this doesn't exactly match your SQL because, in yours, if you time-travel back to January First, 1900 it will delete your row regardless.... I presumed this was not actually your intention.

how to get data whose expired within 45 days..?

HI all,
i have one sql table and field for that table is
id
name
expireydate
Now i want only those record which one is expired within 45 days or 30 days.
how can i do with sql query .?
I have not much more exp with sql .
Thanks in advance,
If you are using mysql then try DATEDIFF.
for 45 days
select * from `table` where DATEDIFF(now(),expireydate)<=45;
for 30 days
select * from `table` where DATEDIFF(now(),expireydate)<=30;
In oracle - will do the trick instead of datediff and SYSDATE instead of now().[not sure]
In sql server DateDiff is quite different you have to provide unit in which difference to be taken out from 2 dates.
DATEDIFF(datepart,startdate,enddate)
to get current date try one of this: CURRENT_TIMESTAMP or GETDATE() or {fn NOW()}
You can use a simple SELECT * FROM yourtable WHERE expireydate < "some formula calculating today+30 or 45 days".
Simple comparison will work there, the tricky part is to write this last bit concerning the date you want to compare to. It'll depend of your environment and how you stored the "expireydate" in the database.
Try Below:-
SELECT * FROM MYTABLE WHERE (expireydate in days) < ((CURRENTDATE in days)+ 45)
Do not execute directly! Depending of your database, way of obtaining a date in days will be different. Go look at your database manual or please precise what is your database.

SQL figuring out days remaining based on days interval (using SQL Server 2005)?

I have a table that represents a user's subscription to our system. I have two tables. One represents the subscription for an account and the other represents the type of subscription. The subscription table relates to the subscription type table with a subscriptionTypeID field in the subscription types table. I need to figure out the days remaining for that account. Here is my example:
The user signed up on January 1, 2010 and their term is 30 days. Based on today's date (January 25, 2010) they would have 6 days remaining.
I need help designing the SQL. Here is what I have so far in my stored procedure:
#SubscriptionTypesID int
Declare #term int
Declare remainder int
Set #term = (SELECT subscriptionTerm
FROM dbo.SubscriptionTypes
where dbo.SubscriptionTypes.SubscriptionTypesID = #SubscriptionTypesID)
Now i need to figure out what to do with the remainder query, or if I can somehow have one SQL statement so I don't have to get the term separately.
Update:
Here is what I got now with all your help, but I still want a more elgant way to pump the term field value into the query:
Select (DateDiff(day,getDate(),DATEADD (day , 30, '01/01/2010' ))) days
Update 2.0 ;)
Sometimes the answer is right in front of me and I can't even see it. I tend to over think problems and make them more complicated than they need to be. Thanks to everyone who helped! HEre is the code:
SELECT (DateDiff(day,getDate(),DATEADD (day , subscriptionTerm, dbo.Subscriptions.subscriptionDate ))) days
FROM
dbo.Subscriptions
INNER JOIN dbo.SubscriptionTypes ON (dbo.Subscriptions.subscriptionTypeID = dbo.SubscriptionTypes.SubscriptionTypesID)
WHERE
dbo.Subscriptions.userID = 129
You can use Datadiff functionality from sql:
http://msdn.microsoft.com/en-us/library/ms189794.aspx
You could also solve it using:
DATEADD (datepart , number, date )
You can find more in the same MSDN page, just use the searchbox and type "Dateadd"
Good luck with your remaining time thingie ;)
Err, why don't you just compare the current date to the subscription date +30 days? I believe that would look like
if(GETDATE() > DATEADD(day,30,#SubscriptionDate))
BEGIN
END
In the comments below you asked how to get teh number of days. SQL has a method for that...
SELECT DATEDIFF(datepart,start,end)
To get the value and use it in different places you can use the following syntax:
DECLARE #Difference int
SELECT #Difference = DATEDIFF(day,#SubscriptionDate,GETDATE())
HEre it is...Thanks all for the insight:
SELECT (DateDiff(day,getDate(),DATEADD (day , subscriptionTerm, dbo.Subscriptions.subscriptionDate ))) days
FROM
dbo.Subscriptions
INNER JOIN dbo.SubscriptionTypes ON (dbo.Subscriptions.subscriptionTypeID = dbo.SubscriptionTypes.SubscriptionTypesID)
WHERE
dbo.Subscriptions.userID = 129