Select Statement yields different result when executed via SQL Server Agent - sql

I am observing a strange behavior on our SQL Server 2014, Standard Edition (64-bit) which I cannot explain:
A simple select statement behaves differently when executed manually or via an SQL Job:
The sql-statement is as follows:
[USE DB2]
GO
Select * from DB1.dbo.price p
where
p.sec_id = 10 and
p.dt = CONVERT(date,getdate() - (case when datename(dw,getdate()) = 'Monday' then 3 else 1 end))
The statement pulls out the price record from table dbo.price for a certain security (sec_id = 10) for the previous business day which normally is 1 day prior, however on Mondays it is 3 days prior as there are only price records on business days available (1 price record per security per business day).
This sql-statement is embedded in a stored procedure which itself is executed via an SQL Server Agent Job.
The strange thing happening is:
If the above sql statement is executed "manually", i.e. via a query editor, it yields the correct result, i.e. one price record is returned when executed Monday to Friday.
The same is true when the above sql statement is executed "manually" via a stored procedure.
However, when the stored procedure containing the above statement is executed via an SQL Server Agent Job, the statement only returns a price record on Tuesday to Friday. On Mondays, the statement returns no record. (Even though the stored procedure respectively the sql statement return a record when executed manually).
Since the job is working Tuesdays to Fridays, it should not be any issue of privileges etc. And since the statement is working when executed manually, there shouldn't be any issue with the statement per se neither.
But why would it not work on a Monday when executed via an SQL job?
Would anybody have an idea what the reason could be? I have none unfortunately ...
Thanks a lot for any help.
Cheers

It's due to the default language of the identity that the Agent job runs under.
In your agent job add this to the script :
SET DATEFIRST 7
[or whatever day of week you expect to be deemed first day of week]
(it's connection specific, so won't affect other connections.)
Or you could change the default language of the login used by SQL Agent (or proxy if you are using one):
USE [master]
GO
ALTER LOGIN [LoginName] WITH DEFAULT_LANGUAGE = [SomeLanguage]
GO
Ref: SET DATEFIRST

As Mitch says, it's likely to be because of different language/date settings used by the Agent job.
My preferred fix though is not to fiddle with settings, but instead to pick a "known good" day with the correct property:
datename(dw,getdate()) = datename(dw,'20150720')
It so happens that 20th July 2015 (selection was entirely arbitrary, I just happen to have a 2015 desk calendar in eyesight) was a Monday and I'm using an unambiguous date format as my literal. So, whetever datename(dw,getdate()) happens to return on Mondays should always be what datename(dw,'20150720') produces.

Related

How can a stored proc have multiple execution plans?

I am working with MS SQL Server 2008 R2. I have a stored procedure named rpt_getWeeklyScheduleData. This is the query I used to look up its execution plan in a specific database:
select
*
from
sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
where
OBJECT_NAME(st.objectid, st.dbid) = 'rpt_getWeeklyScheduleData' and
st.dbid = DB_ID()
The above query returns me 9 rows. I was expecting 1 row.
This stored procedure has been modified multiple times so I believe SQL Server has been building a new execution plan for it whenever it was modified and run. Is it correct explanation? If not then how can you explain this?
Also is it possible to see when each plan was created? If yes then how?
UPDATE:
This is the stored proc's signature:
CREATE procedure [dbo].[rpt_getWeeklyScheduleData]
(
#a_paaipk int,
#a_location_code int,
#a_department_code int,
#a_week_start_date varchar(12),
#a_week_end_date varchar(12),
#a_language_code int,
#a_flag int
)
as
begin
...
end
The stored proc is long; has only 2 if conditions both for #a_flag parameter.
if #a_flag = 0
begin
...
end
if #a_flag = 1
begin
...
end
Depending on the nature of the stored procedure (which wasn't provided) this is very possible for any number of reasons (most likely not limited to below):
Does the proc use a lot of if this then this select, else this select/update
Does the proc contain dynamic sql?
Are you executing the SP from both web and SSMS? Then you're likely executing the SP with different connection settings.
Does the stored proc have parameters? Sometimes a difference in parameters can cause one execution plan to be terrible for a specific set, so a different plan is used.
Going to try an analogy which might help... maybe...
Say you have a stored procedure for your weekend shopping.
You typically need to get groceries, sometimes an air filter, and even less often a big pack of something that needs replacing 4 times a year.
The grocery store can handle groceries, and is the closest to your house (5 minutes).
Target can handle the air filter and groceries, but add 25 minutes travel time.
"Big place of everything" has everything you'd possibly need, but is an hours drive away.
So here, depending on your parameters #needsAirFilter and #needsBigPackOfSomething could vastly change your "execution plan" of your stored procedure of "shopping".
If #needsAirFilter and #needsBigPackOfSomething is false, there's no reason to make the 30 minute or hour drive, as everything you need is at the grocery store.
One a month, #needsAirFilter is true, in that case we need to go to Target, as the grocery store's execution plan is insufficient.
4 times a year #needsBigPackOfSomething is true, and we need to make the hour drive to get the big pack of something, while grabbing groceries, and airfilter since we're there.
Sure... we could make the hour drive every time to get groceries, and the other things when needed (imagine single execution plan). But this is in no way the most efficient way to do it. In instances like this, we have different execution plans for what information/goods are actually needed.
No idea if that helps... but I had fun :D
Typically SQL Server will generate a new query plan depending on the values of the parameters being passed in (this can determine what indexes, if any, it will use) and if indexes are added, changed or updated (on the tables/views being used in the proc) so SQL Server may decide that it is more effective to use one or more indexes that it previously ignored. The more involved the SQL in the proc will also kick off more work on SQL Server side as it attempts to optimize the query. If the data changes (suddenly you have many more customers in NJ and there is a query and index for states) it may decide that its going to use that index and the query plan is changed. If any of the tables or views involved in the query change (schema change) will also invalidate an existing plan and result in a new plan being generated.

Dynamically update "Status" column after "X" amount of time

I'm rather new to SQL Server, but I am working on an app where a record is added to a table, and is given a DateTime stamp.
I want to be able to dynamically update the Status column of this row, 1 hour after the row was added.
Is this possible without running some server side script or store procedure every couple minutes? Is there an efficient way to accomplish this?
In Sql Server you can have Time Dependant or Action Dependent code execution.
Time Dependent
Time Dependant Code execution is handled via SQL Server Agent Jobs. You can execute a stored procedure or ad-hoc T-SQL code on a certain time of the day. It can be scheduled to execute on regular basis.
Action Dependent
Action Dependent Code execution is handled via Triggers (After/Instead of Triggers). A piece of code that is executed in response to a DML action INSERT, UPDATE or DELETE.
Solution
In your case you are trying to execute code in response to an action (Insert) after a certain period of time. I dont think there is an efficient way of doing it I would rather do the following....
You can have a Column called Created of Datetime datatype in your table and set a default value of GETDATE().
Now you dont need the status column. All you need is a query/View which will check at runtime if the row was added more than an hour ago and will return it STATUS as required.
Something like.....
CREATE VIEW dbo.vw_Current_Status
AS
SELECT *
, CASE WHEN DATEDIFF(MINUTE, Created, GETDATE()) >= 60
THEN 'OLD'
ELSE 'New' END AS [Status]
FROM TABLE_NAME

How to update table values automatically every day

I have table with the expiry date, if the expiry date is less than today's date I have to update the flag IsExpired = 1. I have tried with scheduling job, but it's not happening.
I have tried the following steps:
I have created a stored procedure to update the column
then I created a schedule that will run (execute the stored procedure) daily at 12:00 AM
You could create a view which populates its IsExpired column by checking the current date and the expiry date. You could then select from this view to know if a row has expired.
Make sure all the required authentication is correct. I mean (SQL Agent UserName ans Password).
Restart the Sql Server Services and Sql Agent as well. Then start you job schedule.

Set datefirst permanently

I'd like to change permanently the value of DATEFIRST (which is used for functions like DATEPART)
If I do this : SET DATEFIRST 1 the value stay during the execution but it return to the default value - 7 here - after the execution
I already had the problem, I know that it's related to the country of the login but I forgot which table & which property I had to change.
I must say this took some research on my part. Take a look at the following query. You will notice the datefirst field. I would imagine there are all kinds of permission implications that go along with changing language settings at this level.
SELECT
*
FROM
sys.syslanguages
I don't have a server I can test this on at the moment but I would imagine through a set statement you could set the datefirst column to whatever you wanted it to be.
TEST TEST TEST as this will have huge implications across more than problem you are trying to solve.
Interesting Resource # 1
Interesting Resource # 2
Interesting Resource # 3
You can alter the default language of a sql login or windows user authenticated on SQL Server using the Microsoft SQL Server Management Studio or executing the below sample t-sql script :
USE [master]
GO
ALTER LOGIN [login] WITH DEFAULT_LANGUAGE = [you_language]
GO
The script was taken from this site
Perhaps a bit late to the party, but here's a way to "hack" it, regardless of the datefirst value, set either to the server, db, login or session and parametrizable (WOW, never used this word before) by the first day you actual want it to be :
declare
/*
Let's say for a regular, boring day of Monday
, regarldess of the datefirst value, I want it to return Monday as 1 (first day of week)
*/
#ForDate datetime = '20170320',
-- you can play with any value here
#SetDateFirstValue int = 5
--this is only for testing
set datefirst #SetDateFirstValue;
select
datepart(dw,#ForDate) as ActualReturnedDayOfWeek
, datepart(dw,dateadd(dd, ##DATEFIRST - 1,#ForDate)) As IWantItThisWay
P.S. : Perhaps the only situations you'll ever need this, is in a function or constraint (computed columns maybe) where SET operations are not allowed. If SET is allowed, SET it as you need it and forget about it.

Setting A Date to End the Use of a Section of a SQL Script

I am using SQL 2005. For one of our customers, we run a script everytime we set-up a new database. The script defines what information remains and what information is deleted from the database....we use a master database to set 'typical' default information. I have been asked to add a delete statement to the script with a 'test' for the delete statement to quit running automatically after 1 January 2011. We don't want any other information if the script affected; just the one statement. Does anyone know how to structure the syntax for this kind of request?
Thank you.
IF GETDATE() < '20110101'
BEGIN
--Deletez
END
You may need to cast, but i don't think so CAST('20110101' AS DATETIME)