SQL Server : execute dynamic sql help please - sql-server

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

Related

SQL dynamic query check for null, or empty string parameter

I am writing a stored procedure that will be executed from an SSRS report. It receives 2 parameters: SourceID, and ConfirmationNumber but it is not working for all the tests that I am running
this returns recs:[dbo].[GetParkingPaymentInformation] 'PARC', ''
this does not return recs:[dbo].[GetParkingtPaymentInformation] 'PARC', NULL
this does not return recs:[dbo].[GetParkingPaymentInformation] '', '002077770'
this does not return recs:[dbo].[GetParkingPaymentInformation] NULL, '002077770'
this does return recs:[dbo].[GetParkingPaymentInformation] 'PARC', '002077770'
I want the sp to work when one or the other parameter are either null, or blank.
This is what I have so far for code:
SET #s_SQL = 'SELECT d.ID, d.TransactionNumber, h.SourceType, ' + #s_ColumnName + ', d.FirstName, d.LastName,' +
'LTRIM(RTRIM(d.FirstName)) + '' '' + LTRIM(RTRIM(d.LastName)) [Name], '+
'd.PaymentAmount, CONVERT(VARCHAR(10), CAST(d.InitiationDate AS DATE), 101) [InitiationDate]' +
', d.Fee, d.TotalAmount, d.PaymentStatus, d.PaymentType, d.CreditCardType, ' +
'CONVERT(VARCHAR(10), CAST(d.PaymentEffectiveDate AS DATE), 101) [PaymentEffectiveDate]' +
', CONVERT(VARCHAR(10), CAST(d.ProcessDate AS DATE), 101) [ProcessDate], CONVERT(VARCHAR(10), CAST(d.CreatedDate AS DATE), 101) [CreatedDate],' +
'd.CashCode, d.TransConfirmID' +
', d.Phone, d.StreetAddress1, d.StreetAddress2, ' +
'LTRIM(RTRIM(d.StreetAddress1)) + '' '' + CASE WHEN LEN(d.StreetAddress2) > 0 THEN LTRIM(RTRIM(d.StreetAddress2)) ELSE '''' END [Address]' +
', d.City, d.[State], d.ZipFive, d.ZipFour, d.Email ' +
'FROM '+ #s_TableHeader + ' h WITH (NOLOCK) ' +
'INNER JOIN ' + #s_TableDetail + ' d WITH (NOLOCK) ' +
'ON h.ID = d.headerID ' +
'WHERE' +
' ((h.sourcetype = ' + '''' + #s_Source_Type + '''' + ') OR ' + '''' + #s_Source_Type + '''' + ' IS NULL OR ' + '''' + #s_Source_Type + '''' + '= '''')' +
' AND ((d.transconfirmid = ' + '''' + #s_Confirmation_Number + '''' + ') OR ' + '''' + #s_Confirmation_Number + '''' + ' IS NULL OR ' + '''' + #s_Confirmation_Number + '''' + '= '''')'
Any help I can get to figure why my checks are not working, it would be great.
*Please note that your example sql code is incomplete and that you name the parameters slightly different from the variables which makes for difficulty understanding the question. *
It is not without reason that the use of dynamic sql is discouraged even in cases where the dangers of sql injection are negligible. One of the primary reasons for this is that dynamic sql is difficult to write, read and debug. That being said I have often found myself using it to solve for problems within poorly designed systems.
I assume that you have investigated alternatives appropriately.
To reduce the complexity of dynamic sql statements I have found that constructing the statement in a modular way to be a good strategy.
In your particular case the use of an 'if' statement (or some variation) may help reduce the complexity of your dynamic where clause and likely help resolve your problem.
Example:
`
set #sql = 'select....... your select statement..... where'
if (#a_Source_Type is not null and len(#a_Source_Type) > 0)
begin
set #sql += '''' + #a_Source_Type + ''' = some_field and '
end
else
begin
set #sql += ' len(isnull(some_field, '''')) = 0 and '
end
set #sql += ' 1 = 1 '
`
The above attempts to move the comparison operators out of the dynamic sql.
I suggest refactoring your query to use a strategy similar to this example as you may find it easier to identify erroneous code.
the final line of the example has proven useful in circumstances where the resulting dynamic sql statement may or may not have where clauses to append
Try changing your WHERE to something like this:
'where' +
' h.sourcetype = ' + isnull(quotename(nullif(#s_Source_Type, ''), ''''), 'h.sourcetype') +
' AND d.transconfirmid = ' + isnull(quotename(nullif(#s_Confirmation_Number, ''), ''''), 'd.transconfirmid')

Error in my stored procedure

I created a stored procedure as follows:
create procedure [dbo].[ModifCodMedecin] #Table nvarchar(10),#Code nvarchar(7) as
DECLARE #rqt as NVARCHAR(4000)
SET #rqt = 'UPDATE' + #Table + 'SET '+ #Code + ' = Med.New_Code_exploitation from ' + #Table +
' inner join Medecin_New Med on ' + #Table + '.' + #Code + ' = Med.[Ancien_Code])'
exec (#rqt)
When I executed the above, an error is produced:
ModifCodMedecin 'Table','code'
Incorrect syntax near '='.
Try this:
CREATE PROCEDURE [dbo].[ModifCodMedecin] #Table NVARCHAR(10)
,#Code NVARCHAR(7)
AS
DECLARE #rqt AS NVARCHAR(4000)
SET #rqt = 'UPDATE ' + #Table
+ ' SET ' + #Code
+ ' = Med.New_Code_exploitation from ' + #Table
+ ' inner join Medecin_New Med on ' + #Table + '.' + #Code
+ ' = Med.[Ancien_Code]'
EXEC (#rqt)
You had a few missing spaces, one after UPDATE and one before SET. As well as the additional parenthesis at the end.

SQL Stored Procedure How to Modify and Return the Result of an Executed Dynamic Query

I have created a stored procedure that returns a single string of concatenated fields. The issue is that some of these fields may be empty strings resulting in a string much like the below:
, Mendip Road, Farnborough, Hampshire, GU14 9LS
or even
, , Farnborough, Hampshire, GU14 9LS
I really want to strip off any leading commas but I'll only know this once the query has been executed. Is there a way of executing the query, pattern-matching the commas and then removing them before finally returning the modified string?
The query itself is as follows:
SET #SQLQuery = 'SELECT TOP 1 REPLACE((ISNULL(POI,'''') + '', '' + ISNULL(Name,'''') + '', '''
+ ' + ISNULL(Settlement,'''') + '', '' + ISNULL(Cou_Unit,'''') + '', '' + ISNULL(Postcode,'''')),'', , '', '', '')'
+ ' AS ClosestAddress FROM [UKStreetsAndPlaces].[dbo].[OS_Locator] ORDER BY '
+ ' (Longitude ' + #LongitudeOperator + ' ' + CAST(ABS(#Longitude) AS VARCHAR(20)) + ')'
+ ' * (Longitude ' + #LongitudeOperator + ' ' + CAST(ABS(#Longitude) AS VARCHAR(20)) + ')'
+ ' + (Latitude - ' + CAST(#Latitude AS VARCHAR(20)) + ') * (Latitude - ' + CAST(#Latitude AS VARCHAR(20)) + ') ASC'
EXECUTE(#SQLQuery)
Concatenate the comma inside the ISNULL expression as follows:
ISNULL(POI + ', ','')
so your query will look like:
SET #SQLQuery = 'SELECT TOP 1 REPLACE((ISNULL(POI + '', '','''') + ISNULL(Name + '', '','''')'
+ ' + ISNULL(Settlement + '', '','''') + ISNULL(Cou_Unit + '', '','''') + ISNULL(Postcode,'''')),'', , '', '', '')'
+ ' AS ClosestAddress FROM [UKStreetsAndPlaces].[dbo].[OS_Locator] ORDER BY '
+ ' (Longitude ' + #LongitudeOperator + ' ' + CAST(ABS(#Longitude) AS VARCHAR(20)) + ')'
+ ' * (Longitude ' + #LongitudeOperator + ' ' + CAST(ABS(#Longitude) AS VARCHAR(20)) + ')'
+ ' + (Latitude - ' + CAST(#Latitude AS VARCHAR(20)) + ') * (Latitude - ' + CAST(#Latitude AS VARCHAR(20)) + ') ASC'
I don't know if you need dynamic SQL for some other reason, but I think something like this should work (with no Dynamic SQL); if you're really sure you need Dynamic SQL for some other reason, then just refactor this idea into your Dynanmic Statement:
DECLARE #ClosestAddress VARCHAR(1000)
SELECT TOP 1
#ClosestAddress = ISNULL(POI + ', ','')
+ ISNULL(Name + ', ','')
+ ISNULL(Settlement + ', ','')
+ ISNULL(Cou_Unit + ', ', '')
+ ISNULL(Postcode,'')
--AS ClosestAddress
FROM [UKStreetsAndPlaces].[dbo].[OS_Locator] ORDER BY (Longitude = 12.2132) * (Longitude = 12.2132) + (Latitude - 12.2132) * (Latitude - 12.2132) ASC
IF (RIGHT(#ClosestAddress, 2) = ', ')
RETURN SUBSTRING(#ClosestAddress, 0, LEN(#ClosestAddress))
ELSE
RETURN #ClosestAddress
Why this should work: Concatenating NULL + ', ' will result in an empty string. Then we check if the string ends with ', ', and if so we return everything but the last two characters.
You could do something like Replace all Comma with space and then do LTRIM and RTRIM and replace all space with comma.
Create table Data(name varchar(10),lastname varchar(10));
insert into Data values('','Doe');
insert into Data values('Jane','Doe');
insert into Data values('Jane','');
SELECT Replace(Rtrim(Ltrim(Replace(ISNULL(name,'') +',' + ISNULL(lastname,'') + ',',',',' '))),' ',',')
from Data
something like : http://sqlfiddle.com/#!3/6a6c6/1

CAST varchar to datetime in dynamic SQL

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.

SQLServer conditional execution within 'EXEC' if a column exists

I'm new to SQLServer scripting (normally being a C++ developer), and would really appreciate a bit of assistance.
I am attempting to perform a "find and replace" update on all tables in a SQLServer database that contain a 'PROJID' column. I am really struggling to find a way to do this that doesn't report to me:
Msg 207, Level 16, State 1, Line 1 Invalid column name 'PROJID'.
The statement I am executing is:
EXEC
(
'IF EXISTS(SELECT * FROM sys.columns WHERE name = N''PROJID'' AND Object_ID = Object_ID(N''' + #TableName + '''))' +
' BEGIN' +
' UPDATE ' + #TableName +
' SET ' + #ColumnName + ' = REPLACE(' + #ColumnName + ',''' + #ReplaceIDStr + ''',''' + #FindIDStr + ''')' +
' WHERE ' + #ColumnName + ' LIKE ''' + #ReplaceIDStr + '''' + ' AND PROJID = ''1000''' +
' END'
)
I have also tried using:
'IF COL_LENGTH(''' + #TableName + ''',''PROJID'') IS NOT NULL' +
instead of the column-exist check above. This also still gives me the "Invalid Column Name" messages.
I would be happy to take the column-exist check outside of the 'Exec' statement, but I'm not sure how to go about doing this either.
You just need to do it in a different scope.
IF EXISTS (SELECT 1 FROM sys.columns ...)
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'UPDATE ...';
EXEC sp_executesql #sql;
END
Output the results of this query to text. Don't forget to change the values of the variables! Take the result of this and run it.
SET NOCOUNT ON
DECLARE #ColumnName VARCHAR(200) = 'ReplaceColumn'
, #ReplaceIdStr VARCHAR(200) = 'ExampleReplaceIdStr'
, #FindIdStr VARCHAR(200) = 'ExampleFindIdStr'
PRINT 'BEGIN TRAN'
PRINT 'SET XACT_ABORT ON'
SELECT
'UPDATE ' + C.TABLE_NAME + CHAR(13)
+ 'SET ' + #ColumnName + ' = REPLACE(' + #ColumnName + ', ''' + #ReplaceIdStr + ''', ''' + #FindIdStr + ''')' + CHAR(13)
+ 'WHERE ' + #ColumnName + ' LIKE ''%' + #ReplaceIdStr + '%'' AND PROJID = ''1000''' + CHAR(13)
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.COLUMN_NAME = 'PROJID'
PRINT 'COMMIT TRAN'
SET NOCOUNT OFF
EDIT: Also, some reasoning: You said you want update all tables where they contain a column called PROJID. Your first query just says that if the table #TableName has a PROJID column, then update #ColumnName on it. But it doesn't guarantee that it has #ColumnName on it. The query I gave doesn't check that either, because I'm assuming that all tables that have PROJID also have #ColumnName. If that isn't the case, let me know and I can update the answer to check that. That you're getting an Invalid Column Name error points to #ColumnName not existing.
Your query would have updated one table (#TableName) at most, whereas the one I gave you will update every table that has PROJID. I hope that's what your going for.
EDIT 2: Here is a version that would run it all at once:
DECLARE #ColumnName VARCHAR(200) = 'Value'
, #ReplaceIdStr VARCHAR(200) = 'ExampleReplaceIdStr'
, #FindIdStr VARCHAR(200) = 'ExampleFindIdStr'
DECLARE #Sql NVARCHAR(MAX)
DECLARE UpdateCursor CURSOR FOR
SELECT
'UPDATE ' + C.TABLE_NAME
+ ' SET ' + #ColumnName + ' = REPLACE(' + #ColumnName + ', ''' + #ReplaceIdStr + ''', ''' + #FindIdStr + ''')'
+ ' WHERE ' + #ColumnName + ' LIKE ''%' + #ReplaceIdStr + '%'' AND PROJID = ''1000'''
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.COLUMN_NAME = 'PROJID'
OPEN UpdateCursor
FETCH NEXT FROM UpdateCursor
INTO #Sql
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC sp_executesql #Sql
FETCH NEXT FROM UpdateCursor
INTO #Sql
END
CLOSE UpdateCursor
DEALLOCATE UpdateCursor

Resources