I have a Visual studio based stored procedure that generates a report for a monthly audit process. In the database being queried, all data for each month lives in its own individual table (Contacts_month_1, Contacts_month_2, etc.)
The SQL used in this report generation has some minor logic included, to allow it to work dynamically, rather than use hard coded dates. The problem arose at the start of January 2017, when I started receiving not just the results for the prior month, but additionally the prior year as well. To be specific, the audit report for December 2016 included data for both 12/2016 and 12/2015. Initially I thought it was a fluke of some kind based on the turn of the year, and we have not had this automated process during the turn as of yet. Unfortunately when I came in to the office today, inside the output file for January 2017, I also received the results for January 2016.
I attempted to include a year check to the process, however I am still getting the same result output. Any ideas would be appreciated.
Declare #GetMonth TinyInt
,#SessionTable varchar(50)
,#ContactTable varchar(50)
,#TableVersion varchar(2)
Declare #GetYear SmallInt
,#SessionTable_year varchar(50)
,#ContactTable_year varchar(50)
,#TableVersion_year varchar(4)
Set #GetMonth=MONTH(cast(GetDate() as Datetime))-1
Set #GetYear=YEAR(cast(GetDate() as Datetime))
If (#getmonth=0) Set #GetMonth=12 + (#GetYear-1)
Set #TableVersion=CAST(#getMonth as varchar(2))
Set #SessionTable='[CentralDWH].[dbo].[Sessions_month_' +#tableversion +']'
Set #ContactTable ='[CentralDWH].[dbo].[Contacts_month_' +#tableversion +']'
-- Select #GetMonth,#GetYear (DEBUGGING STATEMENT)
-- Select #SessionTable,#ContactTable (DEBUGGING STATEMENT)
Exec('SELECT [PBX_id] as AgentID
,[p22_value] as Skill
,''Athens'' as Location
,Convert(varchar(20),c.[local_start_time],120) as local_start_time
,convert(varchar(20),c.[local_end_time],120) as local_end_time
,U.[USER_NAME]
,call_id
FROM '+#SessionTable +' S
Inner join dbo.Users U on S.user_key=U.user_key
inner Join '+ #ContactTable+' C on S.contact_key=C.contact_key
Where is_screen > 0
And Unit_Num between 398003 and 398005
and P22_value is not null
and c.[local_start_time] > ' + #GetYear
+ ' order by local_start_time')
As I understand, the #GetMonth variable is used for returning the previous month
Set #GetMonth = MONTH(CAST(GetDate() AS Datetime)) - 1
After a quick look after you procedure my first issue was this line of code:
IF (#getmonth = 0)
SET #GetMonth = 12 + (#GetYear - 1)
I don't understand why are you setting the #GetMonth variable to 12 + current year -1 and I assume this is the cause to the problem.
Did you want to get the 12th month of the previous year when the current month is 1 (January)? If yes then you can easily change the If block to
If #GetMonth = 0
Begin
Set #GetMonth = 12
Set #GetYear = #GetYear - 1
End
Other issues:
It's recommended to keep the consistency of the names of the variables #GetMonth, #getmonth, this will cause an error if the database collation is case sensitive.
#GetMonth is declared as TinyInt and this will cause an arithmetic overflow if you try to store the year
I recommend testing the dynamic SQL statement that you are composing here with some hard coded values to check the results returned, you can use January and 2016 to check if the actual issue in your procedure or it's in your query.
Hope it helps
Thanks for your help, I figured out the root of the problem, and it was because i was not casting the GetYear as a varchar when trying to run the T-SQL statement. This in turn caused the variable to be completely ignored. I also cleaned up the query a little bit after realizing i was goofing up pretty hard.
Below is the cleaned up functional query, so that it may help someone in the future:
Declare #GetMonth SmallInt,
#SessionTable varchar(50),
#ContactTable varchar(50),
#TableVersion varchar(2),
#GetYear SmallInt,
#YearCheck varchar(4)
Set #GetMonth=MONTH(cast(GetDate() as Datetime))-1
Set #GetYear=YEAR(cast(GetDate() as Datetime))-1
If (#GetMonth=0)
Begin
Set #GetMonth =12
Set #GetYear =#GetYear - 1
End
Set #TableVersion=CAST(#GetMonth as varchar(2))
Set #SessionTable='[CentralDWH].[dbo].[Sessions_month_' +#tableversion +']'
Set #ContactTable ='[CentralDWH].[dbo].[Contacts_month_' +#tableversion +']'
Set #YearCheck=CAST(#GetYear as varchar(4))
--Select #GetMonth,#GetYear,#YearCheck (DEBUGGING STATEMENT)
-- Select #SessionTable,#ContactTable (DEBUGGING STATEMENT)
Exec('SELECT
[PBX_id] as AgentID,
[p22_value] as Skill,
''Athens'' as Location,
Convert(varchar(20),c.[local_start_time],120) as local_start_time,
convert(varchar(20),c.[local_end_time],120) as local_end_time,
U.[USER_NAME],
call_id
FROM '+#SessionTable +' S
Inner join dbo.Users U on S.user_key=U.user_key
inner Join '+ #ContactTable+' C on S.contact_key=C.contact_key
Where is_screen>0
And Unit_Num between 398003 and 398005
And P22_value is not null
And year(c.[local_start_time]) > '+#YearCheck+'
order by local_start_time')
Once I cleaned all this up and remembered to cast the year properly, everything fell into place.
Related
We take daily backups of all DB's. There isn't enough space on the server so we copy it to a different server and also a different disk based on what day it is. i.e
Day 1 goes to \\SERVER2\E:\SqlBackups\Day1
Day 2 goes to \\SERVER2\E:\SqlBackups\Day2
Day 3 goes to \\SERVER2\E:\SqlBackups\Day3
Day 4 goes to \\SERVER2\H:\SqlBackups\Day4
Day 5 goes to \\SERVER2\H:\SqlBackups\Day5
etc
I want to use dynamic SQL and CMD but its not working, it either can't find the path or says the credentials are wrong even though the account has access to the shared drive
I've now decided to try getting the CMD to call a .bat file to do the copy but i'm not really familiar with how to do this.
I can obviously create a simple copy statement, but i do not know how to include the parameters etc
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Location nvarchar(200)
DECLARE #Day int
SET #Day = DATEPART(dw, getdate())
IF #Day in (1,2,3) SET #Location =
'\\Server2\BackUps\SqlBackups\'
IF #Day in (4,5,6,7) SET #Location =
'\\Server\BackUps2\SqlBackups2\'
SET #Location = #Location + 'Day'+ cast(#Day as nvarchar(1)) + '\'
SET #Location = #Location + 'DB1.bak'
SET #SQL = 'master..xp_cmdshell ''copy /Y
F:\SqlBackups\LatestToCopyToServer2\DB1.bak ' + #location +
''''
EXEC #SQL
The System cannot find the path specified.
if i just do a select the string looks good to me ...
master..xp_cmdshell 'copy /Y F:\SqlBackups\LatestToCopyToServer2\DB1.bak
\Server2\BackUps\SqlBackups\Day5\DB1.bak'
To clarify i need assistance with the batch file and what to include :)
First, I have read about similar posts and have read the comments that this isn't an ideal solution and I get it but the boss (ie client) wants it this way. The parameters are as follows (for various reasons too bizarre to go into but trust me):
1. SQL Server Mgmt Studio 2016
2. NO parameters or pass throughs or temp tables. All has to be within contained code.
So here we go:
I need to create column headings that reflect dates:
1. Current date
2. Most recent quarter end prior to current date
3. Most recent quarter end prior to #2
4. Most recent quarter end prior to #3
5. Most recent quarter end prior to #4
6. Most recent quarter end prior to #5
So if using today's date, my column names would be as follows
12/18/2016 9/30/2016 6/30/2016 3/31/2016 12/31/2016 9/30/2015
I can easily do it in SAS but can't in SQL given the requirements stated above.
Help please with same code.
Thank you
Paula
Seems like a long way to go for something which really belongs in the presentation layer. That said, consider the following:
Let's assume you maintain a naming convention for your calculated fields, for example [CurrentDay], [QtrMinus1], [QtrMinus2], [QtrMinus3], [QtrMinus4],[QtrMinus5]. Then we can wrap your complicated query in some dynamic SQL.
Just as an illustration, let's assume your current query results looks like this
After the "wrap", the results will then look like so:
The code - Since you did NOT exclude Dynamic SQL.
Declare #S varchar(max)='
Select [CustName]
,['+convert(varchar(10),GetDate(),101)+'] = [CurrentDay]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-1,GetDate())),DatePart(QQ,DateAdd(QQ,-1,GetDate()))*3,1)),101)+'] = [QtrMinus1]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-2,GetDate())),DatePart(QQ,DateAdd(QQ,-2,GetDate()))*3,1)),101)+'] = [QtrMinus2]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-3,GetDate())),DatePart(QQ,DateAdd(QQ,-3,GetDate()))*3,1)),101)+'] = [QtrMinus3]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-4,GetDate())),DatePart(QQ,DateAdd(QQ,-4,GetDate()))*3,1)),101)+'] = [QtrMinus4]
,['+Convert(varchar(10),EOMonth(DateFromParts(Year(DateAdd(QQ,-5,GetDate())),DatePart(QQ,DateAdd(QQ,-5,GetDate()))*3,1)),101)+'] = [QtrMinus5]
From (
-- Your Complicated Query --
Select * from YourTable
) A
'
Exec(#S)
If it helps the visualization, the generated SQL is as follows:
Select [CustName]
,[12/18/2016] = [CurrentDay]
,[09/30/2016] = [QtrMinus1]
,[06/30/2016] = [QtrMinus2]
,[03/31/2016] = [QtrMinus3]
,[12/31/2015] = [QtrMinus4]
,[09/30/2015] = [QtrMinus5]
From (
-- Your Complicated Query --
Select * from YourTable
) A
Here is one way using dynamic query
DECLARE #prior_quarters INT = 4,
#int INT =1,
#col_list VARCHAR(max)=Quotename(CONVERT(VARCHAR(20), Getdate(), 101))
WHILE #int <= #prior_quarters
BEGIN
SELECT #col_list += Concat(',', Quotename(CONVERT(VARCHAR(20), Eomonth(Getdate(), ( ( ( ( Month(Getdate()) - 1 ) % 3 ) + 1 ) * -1 ) * #int), 101)))
SET #int+=1
END
--SELECT #col_list -- for debugging
EXEC ('select '+#col_list+' from yourtable')
When I run this SP, I get:
Msg 241, Level 16, State 1, Procedure PED_SP_PED_Updates, Line 22
Conversion failed when converting date and/or time from character string.
Here is the execution:
exec dbo.ped_sp_ped_updates
#CURRENTHICN='111111111A',
#DATERECEIVED = '20140904',
#FIELDTOBECHANGED='FIRST_NAME_MEMBER',
#CURRENTFIELDVALUE = 'MARY',
#NEWFIELDVALUE = 'MARYTEST',
#REQUESTEDBY = 'IPISORS',
#ID=156
I am not sure why, I'm casting the varchar back to a date for the comparison.
Please note, I have no problem being told a better way to do it, but it would be (I think) more helpful to my learning if I could, at least 'also', get a direct answer as to why my current proc isn't working. In addition to any helpful ideas as to why it should be done different, better, etc, etc. etc.
ALTER PROCEDURE [dbo].[PED_SP_PED_Updates]
#CurrentHicn VARCHAR(500),
#DateReceived VARCHAR(20),
#FieldToBeChanged VARCHAR(500),
#CurrentFieldValue VARCHAR(500),
#NewFieldValue VARCHAR (500),
#RequestedBy VARCHAR(10),
#ID int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #CurrentDBNote VARCHAR(MAX)
DECLARE #NewNote VARCHAR(MAX)
DECLARE #CountofHicn INT
SET #NEWNOTE = 'Isaac Pisors | ' + GetDate() + ' | '
+ 'Changing field: ' + #FieldToBeChanged + ' from ' + #CurrentFieldValue + ' to ' + #NewFieldValue
+ ', per ' + #RequestedBy + ' request. Also changing any related DOCS/FAXES records to correspond'
SET #CurrentDBNote=
(SELECT NOTES_GENERAL FROM PED_APPLICATIONS WHERE HICN_MEDICARE_NUMBER=#CurrentHicn AND (Cast(ISNULL(DATE_RECEIVED,'1900-01-01') as DATE)=CAST(#DateReceived AS DATE)))
--NOW ADD THE TWO:
SET #NewNote = #CurrentDBNote + CHAR(13) + #CurrentDBNote
--SEE IF THERE IS STILL A MATCHING RECORD
SET #CountofHicn=
(SELECT COUNT(*) FROM PED_APPLICATIONS WHERE HICN_MEDICARE_NUMBER=#CurrentHicn AND (CAST(ISNULL(DATE_RECEIVED,'1900-01-01') AS DATE)=CAST(#DateReceived AS DATE)))
IF #CountofHicn=0 --THERE IS NO LONGER A MATCHING RECORD - INSERT THAT NOTE AND CALL IT A DAY
BEGIN
UPDATE PED_PEDUPDATES SET COMPLETEDON=GetDate(), COMPLETEDBY='SSIS',
EXCEPTIONNOTE='Could not locate any records where HICN is ' + #CurrentHicn + ' and Date Received is ' + CAST(#DateReceived AS VARCHAR)
WHERE [ID]=#ID
END
ELSE --GO AHEAD AND DO THE UPDATE
BEGIN
UPDATE PED_APPLICATIONS SET #FieldToBeChanged = #NewFieldValue
WHERE HICN_MEDICARE_NUMBER=#CurrentHicn AND (CAST(ISNULL(DATE_RECEIVED,'1900-01-01') AS DATE)=CAST(#DateReceived AS DATE))
END
IF #FieldToBeChanged='HICN_MEDICARE_NUMBER' --THEN WE HAVE TO UPDATE DOCS TABLE, TOO
BEGIN
UPDATE PED_DOCS SET HICN_MEDICARE_NUMBER=#NewFieldValue
WHERE
(HICN_MEDICARE_NUMBER=#CurrentFieldValue AND (CAST(ISNULL(DATE_RECEIVED,'1900-01-01') AS DATE)=#DateReceived)) or
(HICN_MEDICARE_NUMBER=#CurrentFieldValue AND DATE_RECEIVED IS NULL)
END
IF #FieldToBeChanged='HICN_MEDICARE_NUMBER' --THEN OUR WHERE CLAUSE-HICN IS THE *NEW* HICN
BEGIN
UPDATE PED_APPLICATIONS SET NOTES_GENERAL=#NewNote
WHERE HICN_MEDICARE_NUMBER=#NewFieldValue AND (CAST(ISNULL(DATE_RECEIVED,'1900-01-01') AS DATE)=CAST(#DateReceived AS DATE))
END
ELSE --ELSE OUR WHERE CLAUSE-HICN IS THE *OLD* HICN
BEGIN
UPDATE PED_APPLICATIONS SET NOTES_GENERAL=#NewNote
WHERE HICN_MEDICARE_NUMBER=#CurrentHicn AND (CAST(ISNULL(DATE_RECEIVED,'1900-01-01') AS DATE)=CAST(#DateReceived AS DATE))
END
--FINALLY, UPDATE RECORD AS COMPLETE:
UPDATE PED_PEDUPDATES SET COMPLETEDON=GetDate(),COMPLETEDBY='SSIS' WHERE [ID]=#ID
END
GO
Short Term Fix
Instead of CAST(#DateReceived AS DATE), use CONVERT(date, #DateReceived, 112)
The value 112 is the style code for the yyyymmdd formated varchar you're using. See the cast and convert documentation for more details.
Also, you should verify that all values in the DATE_RECEIVED column of your table are in the correct format. Even one value that is not convertible will cause this error.
Proper Fix
#DateReceived should be passed in to the procedure as a date instead of a varchar.
The DATE_RECEIVED field in your table should be declared as a date instead of a varchar.
In general, avoid treating dates or times as strings in a database when there are native types for that purpose.
I have the following piece of code within my stored procedure, I know it doesn't work and should not work but I wanted to illustrate my intentions:
declare #ErrorMessages varchar;
set #ErrorMessages = 'An existing deposit on this property ends after the intended start date for the new deposit. ' +
'Existing End Date: ' + #PreviousDepositEndDate + '. Intended Start Date: ' + #TenancyAgreementStartDate
raiserror 50002 #ErrorMessages
Can anyone tell me what I should be doing? Or any links on creating this type of string.
EDIT: Forgot to say that the #Dates are both of datetime, the error message is that it cannot be converted from datetime to string
Try this:
declare #ErrorMessages varchar(255);
Using just #ErrorMessages varchar; gives you a varchar(1).
set #ErrorMessages =
'An existing deposit on this property ends after the intended start date for the new deposit. ' +
'Existing End Date: ' +
#PreviousDepositEndDate + '. Intended Start Date: ' + #TenancyAgreementStartDate
raiserror(#ErrorMessages, 16, 1)
If you wan't to specify the error number, you must first use sp_addmessage and define the error message, which you can reference in raiserror. You may have to insert some casts, depending on what types #PreviousDepositEndDate and #TenancyAgreementStartDate are.
Here's a slightly different version which some people like because it emulates C printf style:
-- Test data
declare #PreviousDepositEndDate varchar(30) = cast(getdate() - 1 as varchar(30))
, #TenancyAgreementStartDate varchar(30) = cast(getdate() as varchar(30))
-- Throw
raiserror (N'An existing deposit on this property ends after the intended start date for the new deposit. Existing End Date: %s. Intended Start Date: %s',
16, -- Severity,
1, -- State,
#PreviousDepositEndDate, -- First argument.
#TenancyAgreementStartDate) -- Second argument.
More info can be found in this MSDN link: http://msdn.microsoft.com/en-us/library/ms178592.aspx
I am trying to create a Stored Procedure that will be used for a Report and I want the 2 date parameters to have a DEFAULT value of today's date and 1 month prior.
Is the below the proper way to do this? I was reading elsewhere that I should use COALESCE...(SEE HERE)This is a touchy production system so I wanted to double check before I went forward.
CREATE PROCEDURE spCaseNoteReport
-- Add the parameters for the stored procedure here
#ContactStartDate DateTime = null,
#ContactEndDate DateTime = null
AS
IF #ContactStartDate is null
SET #ContactStartDate = dateadd(m,-1, CONVERT(date, GETDATE()))
IF #ContactEndDate is null
SET #ContactEndDate = CONVERT(date, GETDATE())
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT (id.LastName + ', ' + id.FirstName) AS 'MemberName'
,c.ContactDate
,Li.ItemDescription AS 'Location'
,c.PresentAtContact
,c.ContactDetails
,c.InsertUser
,c.TimeSpentUnits
FROM dbo.tblCaseNotes c
inner join dbo.tblIdentificationMap id
on c.PersonID = id.PersonID
left outer join dbo.tblCaseNoteContactLocation L
on c.Casenoteid = L.Casenoteid
inner join dbo.tblCaseNotesMaintItem Li
on L.ContactLocationID = Li.ItemID
WHERE c.ContactDate BETWEEN #ContactStartDate AND #ContactEndDate
ORDER BY MemberName, c.ContactDate, c.InsertUser
END
continued
So when I actually tried to run the CREATE PROCEDURE for this I get the following errors -->
Msg 243, Level 16, State 1, Procedure spCaseNoteReport, Line 12
Type date is not a defined system type.
Msg 243, Level 16, State 1, Procedure spCaseNoteReport, Line 14
Type date is not a defined system type.
Nothing wrong with this approach. I use it myself.
Parameter defaults can only be constants or udfs so the alternative is to use udfs which honestly doesn't really help.
Edit: best way to remove a time component from datetime
DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)
See this excellent SO Q+A "Most efficient way in SQL Server to get date from date+time?" (not mine!)