I am using a dynamic query wherein I want to use the variable which holds the datetime, whenever I execute the query it says cannot convert datetime from string, when I cast that variable to varchar(max), it takes it as string and not datetime, so how should I execute the query..
Below is my SQL query which I am trying to execute.
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN CONVERT(VARCHAR(10),'+#FROMDATE+', 101) and
CONVERT(VARCHAR(10), DATEADD(DD,1,'+#TODATE+'), 101)'
EXEC SP_EXECUTESQL #SQL1
here #fromdate and #todate are the datetime type coming from different temp table. and stored in these variable..
How should I execute this query?
You need to quote your dates..
SET #SQL1 =
'SELECT B.FacId,
B.FacName,
B.BookCode,
B.BookName,
B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE
FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN ''' + CONVERT(VARCHAR(10),#FROMDATE, 101) + ''' and ''' + CONVERT(VARCHAR(10),DATEADD(DD,1,#TODATE), 101) + ''''
You should not concatenate your parameter values like this. The best solution is to use a parameterized query with sp_executesql.
DECLARE #sql nvarchar(4000)
select #sql = N'
SELECT B.FacId
, B.FacName
, B.BookCode
, B.BookName
, B.Quantity
, CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE
FROM ' + quotename(#TABLE) + N' B
WHERE B.TillDate BETWEEN cast(floor(cast(#fromDate as float)) as datetime)
AND cast(floor(cast(#toDate as float)) as datetime)'
EXEC sp_executesql #sql, N'#fromDate datetime, #toDate datetime', #FROMDATE, #TODATE
Things to note about sp_executesql are:
The parameters are NVARCHAR values
The 3rd and 4th parameter keep their original datatype and do not need to be converted to a varchar. This again protects agains SQL Injection and it makes the query more readable as you prevent the quote soup which is so common in Dynamic SQL
Some additional changes were applied to the query:
The table name is wrapped in the QUOTENAME() function which protects against sql injection on the object name
The way the date part of the datetime variables is removed is not very optimal. Doing a convert(,,101) is an expensive operation which can better be done using the casting to float and taking floor of that value.
I'd like to see your variable definitions but I suspect it's because #FROMDATE and #TODATE are datetime and you're using them in a string concatenation statement. Thus you can fix it by:
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity, CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B WHERE B.TillDate BETWEEN CONVERT(VARCHAR(10),'+CAST(#FROMDATE as varchar(15))+', 101) and CONVERT(VARCHAR(10), DATEADD(DD,1,'+CAST(#TODATE as varchar(15))+'), 101)'
However better solutions are:
Don't use dynamic SQL at all, maybe #TABLE doesn't vary that much and you can union them in to a view or something
Pass the parameters directly in to sp_executeSQL and thus preserve their types e.g.
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity, B.TillDate AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN #inFROMDATE and #inTODATE'
EXEC SP_EXECUTESQL #SQL1,'#inFROMDATE datetime, #inTODATE',#inFromDate = #FROMDATE, #inTODATE = #TODate
I think this might work:
DECLARE #tempdate datetime
SET tempdate =DATEADD(DD,1,#TODATE)
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,'''+ cast (B.TillDate as VARCHAR(50))+''' AS TILLDATE FROM '+#TABLE+' B WHERE B.TillDate BETWEEN '''+cast(#FROMDATE as VARCHAR(50))+''' and '''+cast(#tempdate as VARCHAR(50))'''
EXEC SP_EXECUTESQL #SQL1
Try this:
declare #sql1 varchar(max)
declare #table sysname
declare #FROMDATE datetime
declare #TODATE datetime
set #table = 'MyTable'
set #FROMDATE = GETDATE()
set #ToDATE = GETDATE()
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN CONVERT(Datetime,''' + CONVERT(VARCHAR(10),#FROMDATE, 101)
+ ''', 101) and CONVERT(DATETIME,'''+ CONVERT(VARCHAR(10), DATEADD(DD,1,#TODATE), 101) + ''', 101)'
print #sql1
But look at Joel Mansford's answer to avoid the double conversion.
This is late, but may be it help someone
What you need is a quotes around your date,
You already got your answer.
Below is an example of what I usually put in my query
'(CONVERT(DATETIME,CONVERT(varchar,gd.CreatedDate),106) <= CONVERT(DATETIME,'''+CONVERT(varchar, #EndDate ) +''',106))'
note that #EndDate is of type Datetime here
Run this example and adapt it to your code.
(There are not 3 contiguous single quotes)
Declare #FromDATE datetime
;Declare #ToDATE datetime
;set #FromDATE = getdate()
;set #ToDATE = #FromDATE
;Print 'WHERE TillDate BETWEEN ' + char(39) + CONVERT(VARCHAR(10),#FromDATE, 101)
+ char(39) + ' and ' + char(39) + CONVERT(VARCHAR(10),#ToDATE, 101) + char(39)
I needed to pass a date as a variable to a stored procedure I was executing within the dynamic SQL:
Declare #asOfDate date = '2020-01-01'
Declare #sql nvarchar(max)
Set #sql='SELECT * FROM OPENROWSET(''SQLNCLI''
,''Server=localhost;Database=YOURDB;Uid=DBO_DB;Pwd=password''
,''SET FMTONLY OFF;SET NOCOUNT ON;
declare #date date ;
select #date = CONVERT(DATE,CONVERT(varchar(10),'+ CONVERT(VARCHAR(100),#asOfDate , 112)+'));
exec your_proc_here #date
'')'
Print #sql
--Exec(#sql)
Related
I am trying to create a parameterised query for retrieving data back from a table
Essentially I have a table structure of
ID
nvarchar1
ntext
datetime1
datetime2
and I am trying to do a query like so that it selects all the data where the current date is greater than datetime1 and less than datetime2
SELECT
ID, nvarchar1,
ntext,
datetime1,
datetime2
FROM
TABLEName
WHERE
datetime1 >= #CurrentDate
AND datetime2 <= #CurrentDate
I want to make the columns parameters such as
#TableName, #CurrentDate, #StartDate, #EndDate
DECLARE #TableName NVARCHAR(100);
SET #TableName = '[Surveys].[dbo].[Table]'
DECLARE #CurrentDate DateTime;
SET #CurrentDate = GETDATE();
DECLARE #StartDate NVARCHAR(100);
SET #StartDate = 'datetime1'
DECLARE #EndDate NVARCHAR(100);
SET #EndDate = 'datetime2'
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName + 'WHERE' + #EndDate + '>=' + #CurrentDate + 'AND' + #StartDatedatetime1 + '<=' + #CurrentDate
EXEC(#sql)
The data is going to be coming from a SP data source so I have no control of the column names etc. and when I create the SP Lists they automatically assign to a table column of that type so this is why I need to columns to be parameters.
Using the above code which I thought should work returns
Msg 241, Level 16, State 1, Line 14
Conversion failed when converting date and/or time from character string.
What am I doing wrong?
Try the below. As #GordonLinoff stated, you where missing the single quotes (') from around the #CurrentDate variable. Also, you where passing a DATETIME parameter in to the #sql variable which is an NVARCHAR data type. These are not implicitly converted, so the #CurrentDate variable needs to be converted to a NVARCHAR first:
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName
+ ' WHERE ' + #EndDate + ' >= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120)
+ ''' AND ' + #StartDate + ' <= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120) + ''''
EXEC(#sql)
You are pretty close. I would construct the SQL for the table and then use parameters for the current date: This would be something like this:
SET #sql = '
SELECT *
FROM #TableName
WHERE datetime2 >= #CurrentDate AND datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
exec sp_executesql #sql, N'#CurrentDate date', #CurrentDate = #CurrentDate;
Incidentally, the problem with your query is the lack of single quotes around the date constants.
EDIT:
You cannot substitute column or table names using parameters. I would write the code as:
SET #sql = '
SELECT *
FROM #TableName
WHERE #datetime2 >= #CurrentDate AND #datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
SET #sql = REPLACE(#sql, '#datetime1', #DateTime1);
SET #sql = REPLACE(#sql, '#datetime2', '#DateTime2);
. . .
I use REPLACE() for this type of operation because the code is easier to understand and to maintain.
i have the following variables defined in my stored procedure
#StartDate DateTime,
#EndDate DateTime,
I'm setting the sql to be executed dynamically, so when constructing the query where clause i have the below line.
SET #sql = #sql + ' AND (convert(datetime, R.ReportDate, 121) >= ' + #StartDate + 'AND convert(datetime, R.ReportDate, 121) <=' + #EndDate +')'
When i execute the stored procedure, the line above throws the error below
Conversion failed when converting datetime from character string.
If i change the variable datatype to NVARCHAR(MAX), the procedure executes successfully but then returns no rows because the date comparison/matching fails.
ReportDate column is of datatype datetime and has data in this format 2014-06-01 00:00:00.000
As you can see i have tried converting the column when constructing my query but that isn't working.
The problem is not with ReportDate, but when you are trying to concatenate your DateTime parameters with your nvarchar sql statement. The problem can be reproduced fairly simply with:
DECLARE #SQL NVARCHAR(MAX) = 'Some text' + GETDATE();
The wrong way to fix this is to convert the datetime parameter to a string so that it can be concatenated with a string, e.g.
SET #sql = #sql + ' AND r.ReportDate >= CONVERT(DATETIME, '''
+ CONVERT(VARCHAR(10), #StartDate, 112)
+ ''', 112) AND r.ReportDate <= CONVERT(DATETIME, ''' +
+ CONVERT(VARCHAR(10), #EndDate, 112)
+ ''', 112)';
N.B. I am including this for completeness of the answer, and in no way endorse this approach
The correct way to fix this is to use sp_executesql and pass properly typed parameters this way, this will avoid conversion issues. e.g.
SET #sql = #sql + 'AND r.ReportDate >= #StartDateParam AND r.ReportDate <= #EndDateParam';
EXECUTE sp_executesql
#sql,
N'#StartDateParam DATETIME, #EndDateParam DATETIME',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate
Using MS SQL 2012.
I am trying to query a set of tables using dynamic SQL and I am passing through the tablename, a start date in the format of dd/MM/yyyy and an end date in the format of dd/MM/yyyy.
My Code is as follows.
#tableName nvarchar(50),
#startDate date,
#endDate date
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Query varchar(max)
SET #Query = 'Select * from ''' + #tableName + ''' where Convert(date, Docdate, 103) >= ''' + CAST(#startDate AS VARCHAR(50)) + ''' and Convert(date, Docdate, 103) <= ''' + CAST(#endDate AS VARCHAR(50)) + ''
EXEC #Query
END
The Docdate field has a data type of Date and is in the format of yyyy-MM-dd.
I get the following error when I run this stored procedure.
Incorrect syntax near '/'.
What am I missing?
UPDATE
I am testing the query with the following entries for the variables and I still get the same error.
USE [TestDbs]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = Prices,
#startDate = 01/02/2016,
#endDate = 20/02/2016
SELECT 'Return Value' = #return_value
GO
You should use sp_executesql and pass proper date parameters to your query, and avoid the need for casting dates to strings at all:
CREATE PROCEDURE dbo.YourProcedure
#TableName SYSNAME,
#StartDate DATE,
#EndDate DATE
AS
BEGIN
DECLARE #Query NVARCHAR(MAX)
SET #Query = 'SELECT * FROM ' + QUOTENAME(#TableName) +
' WHERE Docdate >= #StartDateParam
AND Docdate <= #EndDateParam';
EXECUTE sp_executesql
#Query,
N'#startDateParam DATE, #endDateParam DATE',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate;
END
I have made a couple of other minor tweaks too:
Change the datatype of #TableName from NVARCHAR(50) to SYSNAME (Synonym for `NVARCHAR(128), the maximum length of an object name)
Change the data type of #Query to NVARCHAR(MAX) since this is the type sp_executesql expects.
Wrapped #TableName with QUOTENAME to ensure any special characters do not cause an error.
If DocDate is already a date, then the explicit convert is not necessary so I have removed this.
ADDENDUM
You may also wish to add some validation to the table name:
-- CHECK TABLE NAME IS A VALID TABLE OF VIEW
IF ISNULL(OBJECT_ID(#TableName, 'U'), OBJECT_ID(#TableName, 'V')) IS NULL
BEGIN
-- HANDLE INVALID NAME
RETURN;
END
EDIT
I have changed the procedure slightly to cater for sending through a schema qualified table name
CREATE PROCEDURE dbo.getResultSet
#TableName SYSNAME,
#StartDate DATE,
#EndDate DATE
AS
BEGIN
DECLARE #Query NVARCHAR(MAX)
SET #Query = 'SELECT * FROM ' +
QUOTENAME(OBJECT_SCHEMA_NAME(OBJECT_ID(#TableName))) + '.' +
QUOTENAME(OBJECT_NAME(OBJECT_ID(#TableName))) +
' WHERE Docdate >= #StartDateParam
AND Docdate <= #EndDateParam';
EXECUTE sp_executesql
#Query,
N'#startDateParam DATE, #endDateParam DATE',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate;
END
Then you would use this to execute it:
SET DATEFORMAT DMY;
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = 'dbo.Prices',
#startDate = '01/02/2016',
#endDate = '20/02/2016'
SELECT 'Return Value' = #return_value;
Or better still use a culture insensitive date format (yyyyMMdd) for your literals:
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = 'dbo.Prices',
#startDate = '20160201',
#endDate = '20160220'
SELECT 'Return Value' = #return_value;
I have a procedure to fetch data as per the criteria passed. I works fine with #month, #year, #quater.
ALTER PROCEDURE dbo.SPReportTimeSpan
(
#Week int = null,
#Month int = null,
#Year int = null,
#Quater int = null
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(4000)
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
SELECT #sql='SELECT
CRMDR.Id as Id,.
CRMDR.Request_For_Id as Request_For_Id
From [CRM].[dbo].[CRM_Doctor_Request] AS CRMDR
WHERE CRMDR.Is_Deleted=0 '
If (#Month) IS NOT NULL
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) like (#Month) '
If (#Year) IS NOT NULL
SELECT #sql=#sql + ' AND YEAR(CRMDR.Date_Created) like (#Year) '
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
If (#Quater) IS NOT NULL
IF (#Quater = 1)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (4,5,6) '
IF (#Quater = 2)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (7,8,9) '
IF (#Quater = 3)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (10,11,12) '
IF (#Quater = 4)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (1,2,3) '
SELECT #sql=#sql + ' ORDER BY CRMDR.Id DESC '
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int',
#Month, #Year, #Quater
RETURN
But for #week it gives error.
Running [dbo].[SPReportTimeSpan] ( #Week = 3, #Month = <NULL>, #Year = <NULL>, #Quater = <NULL> ).
Must declare the scalar variable "#startdate".
Column1 Column2
------------------------------ ------------------------------
2013-02-10 00:00:00.0000000 2013-02-17 00:00:00.0000000
No rows affected.
(1 row(s) returned)
#RETURN_VALUE = 0
Finished running [dbo].[SPReportTimeSpan].
I am not getting why its not working with week criteria. Please help if anyone have idea.
#startdate and #enddate are only accessible within the scope of your IF statement:
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
Move the declaration outside of the IF statement if you wish to reference these variables elsewhere:
DECLARE #startdate Date, #enddate Date
If (#Week) IS NOT NULL
BEGIN
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
FYI: The area where you make reference to these variables outside of the IF statement is here:
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
I got solution of my problem. To solve the problem, I need to explicitly cast datetime values to a character type so the query string can be concatenated as expected.
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN ''' + convert(VARCHAR,#startdate) + ''' AND ''' +convert(VARCHAR,#enddate) + ''''
or this (More Optimized)
SELECT #sql=#sql + ' AND (CRMDR.Date_Created BETWEEN #startdate AND #enddate)'
which needs to add something to sp_executesql
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int, #Week int, #startdate datetime, #enddate datetime ',
#Month, #Year, #Quater, #Week, #startdate, #enddate
Your variables are out of scope.
I need to accomplish the following in the stored procedure:
Pass parameterized column names.
Select the parameterized column names and provide total group by selected columns.
Code:
CREATE PROCEDURE sproc (
#column1 NVARCHAR(MAX),
#column2 NVARCHAR(MAX),
#startdate DATE,
#enddate DATE ) AS
BEGIN
DECLARE #sqlquery NVARCHAR(MAX) = 'SELECT #column1, #column2, SUM(amountcolumn)
FROM tablename
WHERE column3 = ''#value3'',
datecolumn BETWEEN ''#startdate'' AND ''#enddate''
GROUP BY #column1, #column2';
DECLARE #params NVARCHAR(MAX) = '#column1 VARCHAR(MAX),
#column2 VARCHAR(MAX),
#startdate DATE,
#enddate DATE';
EXEC sp_sqlexec #sqlquery, #params,
#column1 = #column1,
#column2 = #column2,
#startdate = #startdate,
#enddate = #enddate;
END
GO
Assuming #value3 is a string and is another parameter to the stored procedure, that datecolumn is in fact date, and ignoring the fact that I have no idea how you can have a schema where the grouping fields can be random like this (which you ignored in other recent questions here):
DECLARE #sql nvarchar(max) = N'SELECT '
+ QUOTENAME(#column1) + ', '
+ QUOTENAME(#column2) + ', SUM(amountcolumn)
FROM dbo.tablename
WHERE column3 = #value3
AND datecolumn BETWEEN #startdate AND #enddate
GROUP BY ' + QUOTENAME(#column1)
+ ', ' + QUOTENAME(#column2) + ';';
EXEC sys.sp_executesql #sql,
N'#value3 varchar(255), #startdate date, #enddate date',
#value3, #startdate, #enddate;
-- strongly recommend against sp_sqlexec
-- it is undocumented and unsupported
This also assumes you don't care about order (you probably do and will want to add ORDER BY as well as GROUP BY).
For more info on dynamic SQL and even further ways to protect yourself from user input:
sqlblog.org/dynamic-sql