How to convert/cast column data type in concatenated SQL query string - sql-server

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

Related

Pivot table not worked by dynamic parameter

I wrote this query:
declare #startdate nvarchar(10) = '2016/01/01',
#enddate nvarchar(10) = '2019/10/04',
#cols nvarchar(max),
#strsql nvarchar(max);
select
#cols =
(select distinct QUOTENAME(maintenancename)+','
from (select distinct maintenancename
from Table_maintenancetype where coalesce(maintenancename, '') != '') AS t1
for xml path('')
)
set #cols=LEFT(#cols,LEN(#cols)-1)
set #strsql=N'select *
from
(select sum(timedurationok) as wt, maintenancetype,tcode
from Table_maintenancereport
where ((svok=1 and need_netok=0) or (svok=1 and netok=1 and need_netok=1))and tcode<>-1
and dateendrole >='+ #startdate+ 'and dateendrole<='+ #enddate+'
group by maintenancetype,tcode
) d
pivot
(sum(wt)
for maintenancetype in ('+#cols+')
) piv
'
exec sp_executesql #strsql
When execute this no record return but when replace #startdate and #enddate by '2016/01/01','2019/10/04'
The query works fine I don't know what the problem is... and when use between in query I get error for can not convert nvarchar to data int 2016
first, do a print #strsql to show your dynamic query and check.
You will see that your query is dateendrole >= 2016/01/01
the date should be in single quote dateendrole >= 2016/01/01'
your query should be
and dateendrole >= '''+ #startdate+ ''' and dateendrole<= '''+ #enddate+'''
a better way is to use parameter with sp_executesql
declare both the variable as date data type since it is going to hold date value
declare #startdate date = '20160101',
#enddate date = '20191004'
and in your dynamic query, just use the variable
and dateendrole >= #startdate and dateendrole <= #enddate
finally when calling sp_executesql, pass in the variable. And don't forget to print out the verify your query
print #strsql
exec sp_executesql #strsql, N'#startdate date, #enddate date', #startdate, #enddate

SQL Parameterised column names

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.

Dates and Dynamic SQL error message

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;

SELECT dynamic columns GROUP BY dynamic columns

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

Working with datetime with dynamic query in SQL Server

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)

Resources