CAST varchar to datetime in dynamic SQL - sql-server

I keep getting the following error:
Msg 241, Level 16, State 1, Line 9 Conversion failed when converting
datetime from character string.
Here is the code I am trying to execute:
DECLARE #v_sql varchar(max),
#v_database varchar(25),
#vStartTime DATETIME,
#vEndTime DATETIME
SELECT #v_database = N'[DATABASE_NAME]', #vStartTime = '2012-09-27', #vEndTime = '2012-11-27'
SELECT #v_sql = N'SELECT
(SELECT ID FROM DATASTORE.DBO.PLANT WHERE DESCRIPTION = ''Henderson''),
SEQ,
AID,
NAME,
GRP,
AREA,
PRIO,
CASE ITIME WHEN ''-'' THEN NULL ELSE CAST(SUBSTRING(ITIME,1,8) + '' '' + SUBSTRING(ITIME,9,2) + '':'' + SUBSTRING(ITIME,11,2) + '':'' + SUBSTRING(ITIME,13,2) AS DATETIME) END ITIME,
CASE ATIME WHEN ''-'' THEN NULL ELSE CAST(SUBSTRING(ATIME,1,8) + '' '' + SUBSTRING(ATIME,9,2) + '':'' + SUBSTRING(ATIME,11,2) + '':'' + SUBSTRING(ATIME,13,2) AS DATETIME) END ATIME,
CASE NTIME WHEN ''-'' THEN NULL ELSE CAST(SUBSTRING(NTIME,1,8) + '' '' + SUBSTRING(NTIME,9,2) + '':'' + SUBSTRING(NTIME,11,2) + '':'' + SUBSTRING(NTIME,13,2) AS DATETIME) END NTIME,
DUR,
MSG,
VAR1,
VAR2,
VAR3,
VAR4,
OPR,
USER_COMMENT
FROM ' + #v_database + '.PROD.ALARM
WHERE CAST(substring(ITIME, 1, 4) + ''-'' + substring(ITIME, 5, 2) + ''-'' + substring(ITIME, 7, 2) + '' '' + substring(ITIME,9,2) + '':'' + substring(ITIME,11,2) + '':'' + substring(ITIME,13,2) + substring(ITIME,15,3) AS DATETIME) BETWEEN ' + #vStartTime +' AND ' + #vEndTime + ' ORDER BY ITIME'
EXEC(#v_sql)
Any help would be much appreciated, I am looking into this for a co-worker and it's got us both stumped.
Edit with a bit more digging, we were able to resolve it ourselves, passing parameters to sp_executesql:
declare
#vSql NVARCHAR(MAX),
#vParam NVARCHAR(MAX),
#vDatabase VARCHAR(15)
SET #vParam = '#vStartTime DATETIME, #vEndTime DATETIME'
SELECT #vSql = '
SELECT ''
'+ #vDatabase + ''',
ITEM_CODE,
SOURCE,
DEST,
TRAN_DT,
MILL_NAME,
NULL
FROM ' + #vDatabase + '.PROD.GRD_LOG
WHERE TRAN_DT BETWEEN #vStartTime AND #vEndTime'
EXEC sp_executesql #vSql, #vParam, #vStartTime, #vEndTime
By making the variables NVARCHAR(MAX), and then using sp_executesql instead of just executing the #vSql variable, we were able to resolve our issue.
Thanks to anyone who might have been looking into this.

In your original dynamic SQL, you were trying to add datetime variable to a text string which results in SQL attempting to convert the text string to a datetime value (by the order of precedence of conversion). You need to set the variables to nvarchar as well to avoid conversion in the original dynamic SQL.

Related

Getting ever while SQL script input as a string in the stored procedure

This is the code I used to run, but while running I get the following error:
Msg 156, Level 15, State 1, Line 24
Incorrect syntax near the keyword 'AS'.
Msg 153, Level 15, State 2, Line 34
Invalid usage of the option NEXT in the FETCH statement
Code:
ALTER PROCEDURE [dbo].[GetCustomers_Pager] --'RowNumber','asc',0,10,''
(#sortColumn VARCHAR(50),
#sortOrder VARCHAR(50),
#OffsetValue INT,
#PagingSize INT,
#SearchText VARCHAR(50) )
AS
BEGIN
DECLARE #sqlQuery NVARCHAR(MAX) =
'SELECT
ROW_NUMBER() OVER (ORDER BY document_id ASC) AS RowNumber,
document_id AS ID,
document_title AS Title,
document_url AS Download,
online_filing_link AS FileOnline,
CASE
WHEN LTRIM(document_fee) IS NULL THEN ''
ELSE CASE
WHEN LEN(document_fee) = 0 THEN ''
ELSE ''$'' + CONVERT(VARCHAR, document_fee)
END
END AS FilingFee ,
CASE
WHEN LTRIM(document_fee_enhanced) IS NULL THEN ''
ELSE CASE
WHEN LEN(document_fee_enhanced) = 0 THEN ''
ELSE ''$'' + CONVERT(VARCHAR, document_fee_enhanced)
END
END AS EnhancedFee,
COUNT(document_id) OVER() AS FilterTotalCount
FROM
documents';
SET #sqlQuery = #sqlQuery +
' WHERE ((''' + #SearchText + ''' <> '''' AND (document_title LIKE ''%' + #SearchText + '%'' OR FilingFee LIKE ''%' + #SearchText + '%'')) OR ('''+#SearchText+''' = ''''))';
SET #sqlQuery = #sqlQuery + ' ORDER BY ' + #sortColumn + ' ' + #sortOrder;
SET #sqlQuery = #sqlQuery + ' OFFSET ' + CAST(#OffsetValue AS varchar(100)) + ' ROWS FETCH NEXT ' + CAST(#PagingSize AS varchar(100)) + ' ROWS ONLY';
EXEC (#sqlQuery);
END
The error because the generated SQL statement syntax is invalid. The literals THEN '' should be THEN '''' so the resultant statement has matching quotes. I suggest you add a PRINT statement for the generated SQL during debugging like the below example.
More importantly, use parameters for values that vary by execution instead of building the statement with literals for those values. Make sure the values for #SortColumn and #sortOrder are provided from a trusted source or validate the values against the catalog views in the proc code.
CREATE OR ALTER PROCEDURE dbo.GetCustomers_Pager
#sortColumn VARCHAR(50)
, #sortOrder VARCHAR(50)
, #OffsetValue INT
, #PagingSize INT
, #SearchText VARCHAR(50)
AS
DECLARE #sqlQuery NVARCHAR(MAX) =
N'SELECT
ROW_NUMBER() OVER (ORDER BY document_id ASC) AS RowNumber,
document_id AS ID,
document_title AS Title,
document_url AS Download,
online_filing_link AS FileOnline,
CASE
WHEN LTRIM(document_fee) IS NULL THEN ''''
ELSE CASE
WHEN LEN(document_fee) = 0 THEN ''''
ELSE ''$'' + CONVERT(VARCHAR, document_fee)
END
END AS FilingFee ,
CASE
WHEN LTRIM(document_fee_enhanced) IS NULL THEN ''''
ELSE CASE
WHEN LEN(document_fee_enhanced) = 0 THEN ''''
ELSE ''$'' + CONVERT(VARCHAR, document_fee_enhanced)
END
END AS EnhancedFee,
COUNT(document_id) OVER() AS FilterTotalCount
FROM
documents';
SET #sqlQuery += N' WHERE ((#SearchText <> '''' AND (document_title LIKE ''%'' + #SearchText + ''%'' OR FilingFee LIKE ''%'' + #SearchText + ''%'')) OR (#SearchText = ''''))';
SET #sqlQuery += N' ORDER BY ' + #sortColumn + ' ' + #sortOrder;
SET #sqlQuery += N' OFFSET #OffsetValue ROWS FETCH NEXT #PagingSize ROWS ONLY;';
--use PRINT to debug SQL query text
--PRINT #sqlQuery;
EXEC sp_executesql
#sqlQuery
, N'#OffsetValue INT, #PagingSize INT, #SearchText VARCHAR(50)'
, #OffsetValue = 0
, #PagingSize = 10
, #SearchText = '';
GO
EXECUTE [dbo].[GetCustomers_Pager]
#sortColumn ='RowNumber'
,#sortOrder = 'asc'
,#OffsetValue = 0
,#PagingSize = 10
,#SearchText = '';
GO

Invalid false result expression in CASE WHEN T-SQL

I am writing a query which unpivots the table. The issue is that based on the mapping file I would like to either input values as a constant or variable.
For example - if in mapping file the ExtDate is constant, e.g. ExtDate = '2017-12-31' I want to use this value (2017-12-31). However if ExtDate starts with 'Var' I would like to use variable values - e.g. when ExtDate = VarOpenDate, then I want to fulfill the column with values from column OpenDate. Below exemplary rows from MappingFile:
CREATE TABLE MappingFile (ColNum INT, Variable CHAR(50), IsUsed CHAR(50), ID CHAR(50), ExtDate CHAR(50), DataDate CHAR(50), ValueDate CHAR(50), Flag CHAR(50), Unit CHAR(50))
INSERT INTO MappingFile VALUES (1, 'ClientId', 'YES', 'VarAcctID', '2017-12-31', 'VarSigningDate', 'VarSigningDate', 'X', '')
INSERT INTO MappingFile VALUES (2, 'ProductGroup', 'YES', 'VarAcctID', 'VarOpenDate', 'VarSigningDate', 'VarSigningDate', 'X', '')
INSERT INTO MappingFile VALUES (3, 'ProductType', 'YES', 'VarAcctID', 'VarOpenDate', 'VarSigningDate', 'VarSigningDate', 'X', '')
In order to do this I wrote a code below (this is a simplification, as there are more columns and whole query is in the inserting while loop).
DECLARE #I INT = 2
DECLARE #COL CHAR(50)
DECLARE #ID CHAR(50)
DECLARE #EDT CHAR(50)
DECLARE #DDT CHAR(50)
DECLARE #DTS CHAR(50) = 'dataset_name'
SET #ID = (SELECT ID FROM MappingFile WHERE ColNum = #I)
SET #EDT = (SELECT ExtDate FROM MappingFile WHERE ColNum = #I)
SET #DDT = (SELECT DataDate FROM MappingFile WHERE ColNum = #I)
SET #COL = (SELECT Variable FROM MappingFile WHERE ColNum = #I)
EXEC('
SELECT ''' + #DTS + ''',
CASE WHEN SUBSTRING(''' + #ID + ''', 1, 3) = ''Var'' THEN ' + #ID + ' ELSE ''' + #ID + ''' END,
CASE WHEN SUBSTRING(''' + #EDT + ''', 1, 3) = ''Var'' THEN ' + #EDT + ' ELSE ''' + #EDT + ''' END,
CASE WHEN SUBSTRING(''' + #DDT + ''', 1, 3) = ''Var'' THEN ' + #DDT + ' ELSE ''' + #DDT + ''' END,
''' + #COL + ''',
' + #COL + '
FROM ' + #DTS + '
WHERE ' + #COL + ' IS NOT NULL
')
Unfortunately in case when ExtDate is constant string the query gives me an error. It is caused by the fact that the result expression:
THEN ' + #EDT + '
returns string which is not a name of column. This gives me an error, altough it shouldn't because if
SUBSTRING(''' + #ID + ''', 1, 3) <> ''Var''
then the result of the case is
' ELSE ''' + #DDT + '''
which is not a column name, but a constant string.
You have dynamically built a query that cannot parse.
Consider the test query below:
DECLARE #T TABLE (
Col int
);
INSERT INTO #T VALUES (1);
SELECT CASE
WHEN 1=0 THEN NoSuch ELSE 'Hi' END
FROM #T;
Run this, and it results in
Msg 207, Level 16, State 1, Line 8 Invalid column name 'NoSuch'.
Because even though the CASE doesn't execute the THEN and skips to the ELSE, the parser doesn't know that in advance and checks to make sure NoSuch is a column. If it isn't, it rejects the query before it even tries to run it.
One solution is to check your variables OUTSIDE of the dynamic string.
Thanks to #Tab Alleman I managed to find the solution. The phenomenon regarding parser is exactly true, therefore I had to check the variables outside the EXEC command.
Below find the code which works:
DECLARE #I INT = 2
DECLARE #COL CHAR(50)
DECLARE #ID CHAR(50)
DECLARE #EDT CHAR(50)
DECLARE #DDT CHAR(50)
DECLARE #DTS CHAR(50) = 'dataset_name'
SET #ID = (SELECT CASE WHEN SUBSTRING(ID , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(ID , LEN(ID ) - 3))) WHEN ID = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(ID )), '''') END FROM MappingFile WHERE ColNum = #I)
SET #EDT = (SELECT CASE WHEN SUBSTRING(ExtDate , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(ExtDate , LEN(ExtDate ) - 3))) WHEN ExtDate = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(ExtDate )), '''') END FROM MappingFile WHERE ColNum = #I)
SET #DDT = (SELECT CASE WHEN SUBSTRING(DataDate , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(DataDate , LEN(DataDate ) - 3))) WHEN DataDate = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(DataDate )), '''') END FROM MappingFile WHERE ColNum = #I)
SET #COL = (SELECT Variable FROM MappingFile WHERE ColNum = #I)
EXEC('
SELECT ''' + #DTS + ''',
' + #ID + ',
' + #EDT + ',
' + #DDT + ',
''' + #COL + ''',
' + #COL + '
FROM ' + #DTS + '
WHERE ' + #COL + ' IS NOT NULL
')

Stored Proc does not return resultset with JDBC Connection using MS SQL Driver version 4

For some reason the following stored proc does not return a ResultSet with the MS SQL Driver but it works fine with JTDS. I tried using prepared statements, callable statements, out parameters, etc.. In all cases I can never get a ResultSet object back.
I'm expecting a VARCHAR of the table name just created/updated. This is SQL 2008 server and the stored proc works fine in the query analyser.
These are some of the ways I called it:
"spScheduleSearchCached ?, ?, ?, ?, ?"
"spScheduleSearchCached(?, ?, ?, ?, ?)"
"{? = call spScheduleSearchCached(?, ?, ?, ?, ?) }"
"{ call spScheduleSearchCached(?, ?, ?, ?, ?) }"
using PreparedStatements and CallableStatements.
I tried with callable to return a result directly by calling statement.getString(1) etc...
Any ideas? TIA
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE spScheduleSearchCached (
#ProcedureList varchar(1500),
#StartDate smalldatetime,
#ReservationList varchar(1500) = NULL,
#interval int = 60,
#future int = 30,
#PatientID int = NULL )
AS
DECLARE #tableName varchar(250)
DECLARE #sqlCreateTable varchar(4096)
DECLARE #sqlInsertRecords varchar(4096)
SET #tableName = 'SEARCHRESULT_' + REPLACE (REPLACE (#PROCEDURELIST, ',', '_'), ' ', '') + '_' + convert(varchar, GETDATE(), 112) + '_' + CAST( CAST( RAND() * 99999 as int) as varchar(5))
SET #sqlCreateTable = N'
CREATE TABLE ' + #tableName + '
(
ID int NOT NULL identity(1, 1)
, DefaultResult int NULL DEFAULT 0
, Start1 smalldatetime NULL
, Room1 int NULL
, AMPM1 int NULL
)'
exec(#sqlCreateTable)
/* Determine if this is a phased exam */
IF EXISTS (select 1 from examcode where examcode_no in (select val from fnconverttotable(#ProcedureList)) AND ScheduleWithPhases = 1)
BEGIN
-- Phased Exam, make sure there is only one procdure being passed in
IF (Select count(1) From fnConvertToTable(#ProcedureList)) > 1
BEGIN
-- Do Nothing, too many phased procedures passed in
PRINT 'Only 1 phased procedure at a time'
END
ELSE
BEGIN
SET #sqlInsertRecords = N'
INSERT INTO ' + #tableName + '
(start1, room1, start2, room2, start3, room3, start4, room4, start5, room5)
EXEC spScheduleSearchPhasedExams ' + #ProcedureList + ', ''' + CONVERT(varchar, #StartDate, 102) + ''', ' + CONVERT(varchar, #interval) + ', ' + CONVERT(varchar, #future) + ', ' + ISNULL(CONVERT(varchar, #PatientID), 'NULL')
EXEC (#sqlInsertRecords)
SELECT #tableName as resultKey
END
END
ELSE
BEGIN
SET #sqlInsertRecords = N'
INSERT INTO ' + #tableName + '
(start1, room1, start2, room2, start3, room3, start4, room4, start5, room5)
EXEC spScheduleSearchNormalExams ' + #ProcedureList + ', ''' + CONVERT(varchar, #StartDate, 102) + ''', ' + ISNULL(#ReservationList, 'NULL') + ', ' + CONVERT(varchar, #interval) + ', ' + CONVERT(varchar, #future) + ', ' + ISNULL(CONVERT(varchar, #PatientID), 'NULL')
EXEC (#sqlInsertRecords)
SELECT #tableName as resultKey
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
You may be getting back more than one result set? Try adding the following line:
SET NOCOUNT ON
JTDS does not need this line but I have add to add this for other drivers.
In case anyone else comes across this. The issue is that MSSQL driver returns update count results along with result sets. If your stored proc performs any updates you need to iterate across all result types to find the real resultset.
try (CallableStatement st = con.prepareCall(statement)) {
setParameters(st, params);
st.execute();
while (true) {
boolean queryResult = st.getMoreResults();
if (queryResult) {
try (ResultSet rs = st.getResultSet()) {
// do something here
}
} else {
if (st.getUpdateCount() == -1) {
break;
}
}
}
}

SQL Server : execute dynamic sql help please

EXEC SP_EXECUTESQL
#DynamicSQL
, N'#HostIDs VARCHAR(MAX) OUTPUT'
, #HostIDs OUTPUT;
PRINT #HostIDs;
SELECT #HostIDs AS HostIDs;
SET #UpdateSQL = '
EXECUTE [dbo].[usp_Win7_HostUpdater_NEW]
#HostID = ''' + #HostIDs + ''' ,
#PackageID = ''' + #PackageID + ''' ,
#MigrationFlag = ''' + #MigrationFlagID + ''' ,
#Manufacturer = ' + #Manufacturer + ' ,
#Product = ' + #Product + ' ,
#Version = ' + #Version + ' ,
#Reason = ' + #Reason + ' ,
#Contact = ' + #Contact + '
';
SELECT #UpdateSQL AS UpdateSQL;
PRINT #UpdateSQL;
EXEC( #UpdateSQL )
END
I have a stored procedure on both a SQL Server 2005 and 2008 in which the above code is the last part of
it returns a VARCHAR(MAX) of numbers separated by commas.
Now this returned value is large upwards of 600k characters. If I execute this on a SQL Server 2005 it works like 50% of the time, #HostIDs is populated always and #UpdateSQL gets generated with the correct values and is executed.
On SQL Server 2008, #HostIDs is populated but #UpdateSQL is always NULL
This is weirding me out tremendously
Can anyone maybe shed some light on my odd problem?
Check these out
SET CONCAT_NULL_YIELDS_NULL OFF
select 'abc' + null + 'def'
--- abcdef
SET CONCAT_NULL_YIELDS_NULL ON
select 'abc' + null + 'def'
--- NULL
That's one way to get around the problem, which is to set it off before your string building and back on after. Any NULL in the sequence of string concatenation renders the entire statement NULL, which explains it works like 50% of the time - these are when all of the variables are non-null.
Completely agree with freefaller though, unless the question's an abstraction of a larger puzzle, there's no reason to build a dynamic SQL and EXEC it when a direct execution will work for the particular snippet shown.
If any of the parameters are null, the entire statement will be null. You can work around it by doing something like this (and I don't know what the data types are, but sometimes you need to cast them to varchar from int/bool/etc. types to make the concatenation work):
SET #UpdateSQL = '
EXECUTE [dbo].[usp_Win7_HostUpdater_NEW]
#HostID = ' + ISNULL('''' + #HostIDs + '''', 'null') + ' ,
#PackageID = ' + ISNULL('''' + #PackageID + '''', 'null') + ' ,
#MigrationFlag = ' + ISNULL('''' + #MigrationFlagID + '''', 'null') + ' ,
#Manufacturer = ' + ISNULL(#Manufacturer, 'null') + ' ,
#Product = ' + ISNULL(#Product, 'null') + ' ,
#Version = ' + ISNULL(#Version, 'null') + ' ,
#Reason = ' + ISNULL(#Reason, 'null') + ' ,
#Contact = ' + ISNULL(#Contact, 'null') + '
';
It causes because you are not handling nulls
you can use sp_executesql instead of exec it has some benefits over exec

SQL 2012 - SearchAllTables for datetime Stored procedure

I'm trying to create a stored procedure that will search all user created tables of a database for a search string.
I found this stored procedure that I'd like to start off with and build on.
http://vyaskn.tripod.com/search_all_columns_in_all_tables.htm
The only problem is entering a datetime into the search will always return nothing.
How can I modify this to search for dates as well?
Lines of interest:
CREATE PROC SearchAllTables
#SearchStr nvarchar(100)
AS
....
SET #SearchStr2 = CASE WHEN ISDATE(#SearchStr)=0
THEN QUOTENAME('%' + #SearchStr + '%','''')
ELSE #SearchStr END
....
--Here's where the comparison is made. This comparison works for string and numeric types but not datetime
DECLARE #sql nvarchar(max)
IF ISDATE(#SearchStr) = 0
BEGIN
SET #sql = 'INSERT INTO #Results SELECT ''' + #TableName + '.' + #ColumnName + ''', LEFT(' + #ColumnName + ', 3630)
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + #ColumnName + ' LIKE ' + #SearchStr2
END
ELSE IF ISDATE(#SearchStr) = 1
BEGIN
SET #sql = 'INSERT INTO #Results SELECT ''' + #TableName + '.' + #ColumnName + ''', LEFT(' + #ColumnName + ', 3630)
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE DATEDIFF(day, CONVERT(datetime, '+ #ColumnName + ', 103), ' + #SearchStr+ ') = 0'
END
PRINT #sql
EXEC sp_ExecuteSQL #sql
GO
I get a conversion error. I need this stored procedure to work with all 3 types of data string, numeric, and date.
Conversion failed when converting date and/or time from character string.
Thank you
your date field is a datetime?
you should traet it like this:
declare #SearchStr datetime
set #SearchStr = convert(datetime, '2012-09-10', 103) –- dd/mm/yyyy
and the where part like this:
'WHERE Datediff(day, CONVERT(datetime, '+ #ColumnName + ', 103), ' + #SearchStr+') = 0'
I think this should work
If the search string is a date then you have to do an explicit conversion. Otherwise string will not match with datetime.
Try something like this.
SET #SearchStr2 = CASE WHEN ISDATE(#SearchStr)=1
THEN CONVERT(datetime,#SearchStr,103)
ELSE QUOTENAME('%' + #SearchStr + '%','''') END
Hope this will help you.
ElVieejo's answer was correct but for it to finally work I also needed to SET #ColumnName conditionally (depending IF ISDATE(#SearchStr) ). If #SearchStr is a date, the inner query of SET #ColumnName had to EXCLUDE all types except the 'datetime' under "DATA_TYPE IN ('datetime')".

Resources