Dynamic query and use of variables - sql

The query below works if the 2 dates are hard coded, however, I would like to replace them with the 2 variables #FirstDayM and #LastDayM.
When I do so, it returns the following error
"Conversion failed when converting date and/or time from character string"
DECLARE #sql varchar(max)
DECLARE #FirstDayM DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0)
DECLARE #LastDayM DATE = DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1);
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + 'SELECT COUNT(C1CustID) AS CertsCount, ''' + QuoteName(name)+ ''' as DBname FROM ' + QuoteName(name) + '.dbo.T_C1CustCourse'+
' WHERE C1CertificationDate_N >= '+'''2018-01-01'''+' AND C1CertificationDate_N <= '+'''2018-01-31'''
FROM sys.databases WHERE database_id > 4 AND state = 0;
EXEC (#sql);

Use sp_executesql. Always. It makes it easy to put parameters into queries. Even if the dynamic query does not start with the parameter, you might decide to add one later.
declare #sql nvarchar(max);
declare #firstDayM date;
declare #lastDayM date;
set #firstDayM = ?;
set #lastDayM = ?;
SELECT #sql = Coalesce(#sql + ' UNION ALL ', '') + '
SELECT COUNT(C1CustID) AS CertsCount, ''' + QuoteName(name)+ ''' as
DBname
FROM ' + QuoteName(name) + '.dbo.T_C1CustCourse' + '
WHERE C1CertificationDate_N >= #FirstDayM AND C1CertificationDate_N
<= #LastDayM'
FROM sys.databases
WHERE database_id > 4 AND state = 0;
EXEC sp_executesql #sql, N'#FirstDayM date, #lastDayM date',
#FirstDayM=#FirstDayM, #lastDayM=#lastDayM;

Related

Appending variables inside dynamic sql in sql server

I'm having trouble building a dynamic sql string to run an openquery. When I print the query string it evaluates the variable name as string instead of the actual value. Here's what I have:
Declare #tsql varchar(1000)
Declare #book_review_start as date
Declare #book_review_end as date
set #book_review_start = convert(varchar(10),DATEADD(month, DATEDIFF(month, 0, GETDATE())-2, 0), 120)
set #book_review_end = convert(varchar(10), DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), -2), 120)
Set #tsql = 'select * from openquery(authorsdb, ''select distinct ssn as bsn
from authors.dbo.nytimes where review_date between '' + #book_review_start + 'and' + '#book_review_end' + 'and review = 'annual'
and city_name = ''
and review_hrs = 0
and review_days = 0')'
That throws all kinds of conversion errors and such.
1) You need 1 or more extra ' single quote characters in 1 or more places in your statement.
2) The DATE values need to converted to string / VARCHAR since you are concatenating string to generate the dynamic SQL statement.
The following works.
DECLARE #tsql VARCHAR(1000)
DECLARE #book_review_start AS DATE
DECLARE #book_review_end AS DATE
SET #book_review_start = DATEADD(month, DATEDIFF(month, 0, GETDATE())-2, 0)
set #book_review_end = DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), -2)
Set #tsql = 'SELECT * FROM OPENQUERY(authorsdb, ''SELECT DISTINCT ssn AS bsn FROM authors.dbo.nytimes WHERE review_date BETWEEN'''
+ CONVERT(varchar(20),#book_review_start) + ''' AND ''' + CONVERT(varchar(20),#book_review_end ) + ''''
+ ' AND review = ''annual'' AND city_name = '''' AND review_hrs = 0 AND review_days = 0 )'
PRINT #tsql
The OUTPUT of the PRINT Statement is as follows.
SELECT * FROM OPENQUERY(authorsdb, 'SELECT DISTINCT ssn AS bsn FROM
authors.dbo.nytimes WHERE review_date BETWEEN'2016-05-01' AND
'2016-06-30' AND review = 'annual' AND city_name = '' AND review_hrs =
0 AND review_days = 0 )
If the intent is to eventually do this:
exec sp_executesql #tsql
then, as a minimum you must:
1 Make #tsql an nvarchar variable as opposed to varchar.
2 As suggested by Serg, all single quotes in the sql part become 4 single quotes. For example, this:
and city_name = ''
becomes this:
and city_name = ''''''''
3 You need spaces around words. For example this:
where review_date between '' + #book_review_start + 'and'
must be this:
where review_date between ' + #book_review_start + ' and '
This should get you started. Read the other answers as well.
Your query issues are
- #book_review_start / end declared DATE, should be VARCHAR to take part in concatenation
- to have a quote in a quoted string you need double quote, to have quoted string in a quoted string you need 4 quotes
Try
Declare #tsql varchar(1000)
Declare #book_review_start varchar(10)
Declare #book_review_end varchar(10)
set #book_review_start = convert(varchar(10),DATEADD(month, DATEDIFF(month, 0, GETDATE())-2, 0), 120)
set #book_review_end = convert(varchar(10), DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), -2), 120)
Set #tsql = 'select * from openquery(authorsdb, ''select distinct ssn as bsn
from authors.dbo.nytimes where review_date between ''''' + #book_review_start + ''''' and ''''' + #book_review_end + ''''' and review = ''''annual''''
and city_name = ''''''''
and review_hrs = 0
and review_days = 0'')' ;
select #tsql;

Error message Conversion failed when converting datetime from character string

ALTER PROCEDURE [dbo].[TEST_01]
(
#StartDate DateTime,
#EndDate DateTime
)
AS
BEGIN
SET NOCOUNT ON;
Declare #sql as nvarchar(MAX);
SET #sql = #sql + ';WITH CTE_ItemDetails
MAX(D.Name) as Name,
SUM(ISNULL(DT.col1, 0)) AS col1,
SUM(ISNULL(DT.col2, 0)) AS col2,
SUM(ISNULL(DT.col3, 0)) AS col3,
GROUPING(D.ItemType) AS ItemTypeGrouping
FROM Items D
INNER JOIN Details DT ON DT.ItemId = D.ItemId
INNER JOIN Report R ON R.ReportId = DT.ReportId
where 1=1'
SET #sql = #sql + ' AND (R.ReportDate >= ' + #StartDate + 'AND R.ReportDate <=' + #EndDate +')'
IF #someOtherVariable is not null
SET #sql = #sql + ' AND R.someColumn IN (''' +replace(#someOtherVariableValues,',','')+''+')'
SET #sql = #sql + 'SELECT col1, col2, col3 FROM CTE_ItemDetails'
EXECUTE (#sql)
END
I have a stored procedure that is similar to the T-SQL code above.
(Note that i have removed lots of code that i feel isn't relevant to the error i'm getting)
I'm getting the below error when i execute it.
Conversion failed when converting datetime from character string.
My parameters have values in below format
exec TEST_01 #StartDate=N'4/1/2016 12:00:00 AM',#EndDate=N'4/30/2016 12:00:00 AM'
It looks like the trouble is in the way i'm dynamically setting the SQL statement at line below
SET #sql = #sql + ' AND (R.ReportDate >= ' + #StartDate + 'AND R.ReportDate <=' + #EndDate +')'
What is the best date formatting i can apply to avoid the error.
You should use parameters via sp_executesql.
But your immediate problem is this line:
SET #sql = #sql + ' AND (R.ReportDate >= ' + #StartDate + 'AND R.ReportDate <=' + #EndDate +')'
It should look more like:
SET #sql = #sql + ' AND (R.ReportDate >= ''' + convert(varchar(10), #StartDate, 121) + ''' AND R.ReportDate <= ''' + convert(varchar(10), #EndDate, 121) +''')' ;
Note the inclusion of explicit type casting to a string and the double single quotes so the date literal is not interpreted as 2016 - 04 - 14 (i.e. 2000).
The better method of using parameters looks like:
SET #sql = #sql + ' AND (R.ReportDate >= #StartDate AND R.ReportDate <= #EndDate)' ;
. . .
exec sp_executesql #sql, N'#StartDate date, #EndDate date)', #StartDate = #StartDate, #EndDate = #EndDate;
It is easier to read the SQL statement. The type issues are handled through parameters. And, the query plan is more readily stashed. Unfortunately, parameters only work for constants, not for column or table names, for instance.

patch datetime in varchar column

i have about 12 tables i need to iterate thru and update all datetime fields (which is a varchar type) check if it's a date and if so, add 5hrs to account for utc adjustment or if it's an invalid date just set it to null. Below is a template I came up with. Just wondering if there are some other ways to do this?
update Tables_1
set releasedate = CASE ISDATE(releasedate)
WHEN 1 THEN DATEADD(HH, 5, releasedate)
ELSE NULL
END,
returndate = CASE ISDATE(returndate)
WHEN 1 THEN DATEADD(HH, 5, returndate)
ELSE NULL
END
given: sql server 2008, i know the columns and tables already that have the datetime type stored as varchar.
bonus request to add another check. If it is a date and the time is not specified, set the time to 5:00 AM
If they all end with date you could build it dynamically:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ', ' + name = CASE ISDATE(' + name + ') WHEN 1 THEN
DATEADD(HOUR, 5, ' + name + ' ELSE NULL END'
FROM sys.columns
WHERE name LIKE '%date' AND [object_id] = OBJECT_ID('dbo.Tables_1');
SELECT #sql = 'UPDATE dbo.Tables_1 SET ' + STUFF(#sql, 1, 1, N'');
PRINT #sql;
-- EXEC sp_executesql #sql;
Now if you want to do that for 12 tables, you could just do this in a loop, e.g.
DECLARE #t SYSNAME, #sql NVARCHAR(MAX);
DECLARE c CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT name FROM sys.tables
WHERE name IN ('Tables_1' --, other tables
);
OPEN c;
FETCH NEXT FROM c INTO #t;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #sql = ', ' + name = CASE ISDATE(' + name + ') WHEN 1 THEN
DATEADD(HOUR, 5, ' + name + ' ELSE NULL END'
FROM sys.columns
WHERE name LIKE '%date' AND [object_id] = OBJECT_ID(#t);
SELECT #sql = 'UPDATE ' + #t + ' SET ' + STUFF(#sql, 1, 1, N'');
PRINT #sql;
-- EXEC sp_executesql #sql;
FETCH NEXT FROM c INTO #t;
END
CLOSE c;
DEALLOCATE c;

comparing datetime variables

How do I compare datetime variable passed to the store procedure with datetime variable in the table.
e.g. In my where clause #paramDate value is 2/10/2012
set sql = 'WHERE product.RegisteredDate >= ' + #paramDate
when I exec(#sql)
it fails, Error:
Conversion failed when converting datetime from character string.
Thx
SET #sql = '... WHERE product.RegisteredDate >= '''
+ CONVERT(CHAR(8), #paramDate, 112) + ''';';
If #ParamDate is NULL you can probably do this:
SET #sql = 'SELECT ...';
SET #sql = #sql + COALESCE(' WHERE product.RegisteredDate >= '''
+ CONVERT(CHAR(8), #paramDate, 112) + ''';', '');
Or even:
SET #sql = 'SELECT ...';
IF #paramDate IS NOT NULL
BEGIN
SET #sql = #sql + '... WHERE product.RegisteredDate >= '''
+ CONVERT(CHAR(8), #paramDate, 112) + ''';';
END
Try this:
CONVERT(varchar, #paramDate, 101)
See here for more information:
http://msdn.microsoft.com/en-us/library/ms187928.aspx
Since you're trying to execute a dynamic query, you could use sp_executesql instead of exec so that you can use parameters in your generated query. Here are the details for sp_executesql.
For example:
set #sql = 'WHERE product.RegisteredDate >= #dynamicParm'
EXECUTE sp_executesql #sql, N'#dynamicParm DATETIME', #dynamicParm = #paramDate

Unable to inject smalldatetime into D-SQL statement

when i try to execute this sql statement i am getting the error.. Conversion failed when converting character string to smalldatetime data type.
Does anyone know what i am doing wrong?
declare #modality varchar(50)
declare #datefrom smalldatetime
set #modality = 'xxxxxxx'
set #datefrom = '20090101'
declare #var1 nvarchar(4000)
select #var1 =
'select
sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +'
from dbo.vw_RawData
where vw.date >= ' + #datefrom + ''
exec sp_executesql #var1
You are trying to concatenate the smalldatetime with a varchar.
Change
Solution 1
declare #datefrom smalldatetime
to
declare #datefrom varchar(8)
and
select #var1 = 'select sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +
' from dbo.vw_RawData where vw.date >= ' + #datefrom + ''
to
select #var1 = 'select sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +
' from dbo.vw_RawData where vw.date >= ''' + #datefrom + ''''
Solution 2
change
select #var1 = 'select sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +
' from dbo.vw_RawData where vw.date >= ' + #datefrom + ''
to
select #var1 = 'select sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +
' from dbo.vw_RawData where vw.date >= ''' + convert(varchar(10), #datefrom, 121) + ''''
In the statement select #var1 = 'select sum('+ #modality +') as ' + dbo.fnc_titlecase(#modality) +' from dbo.vw_RawData where vw.date >= ' + #datefrom + '' SQL Server is trying to do date arithmetic by casting all the surrounding strings to a smalldatetime instead of converting #datefrom to a string and performing string concatenation.
Possible fixes.
Change #DateFrom to a sting so that
the concatenation works. Note you
will have to add some quotes so that
the string in #Var1 is properly
formated.
Use convert function to convert #datefrom to a string. Look up the right conversion number in Books online. I don't have time to right now. Don't use cast, it won't give a
Use a paramertized SQL String. Look up sp_executesql in Books Online. (Or wait, StackOverflow always has someone to point out how to avoid dynamic SQL.)
EDIT: Just checked. cast(#DateTime as Varchar(...)) gives a string that I thought might be hard to parse, but it seems to work, might try that instead of convert. Make sure the varchar() is big enough