My question is in my title, I have defined a stored procedure with the following
CREATE PROCEDURE [dbo].[sp_doStuff]
#path CHAR(256), #databaseName sysname, #backupType CHAR(1)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sqlCommand NVARCHAR(1000)
DECLARE #dateTime NVARCHAR(20)
SELECT #dateTime = REPLACE(CONVERT(VARCHAR, GETDATE(),111),'/','') +
REPLACE(CONVERT(VARCHAR, GETDATE(),108),':','')
IF #backupType = 'F'
SET #sqlCommand = 'BACKUP DATABASE ' + #databaseName +
' TO DISK = ' + #path + #databaseName + '_Full_' + #dateTime + '.BAK'''
IF #backupType = 'D'
SET #sqlCommand = 'BACKUP DATABASE ' + #databaseName +
' TO DISK = ' + #path + #databaseName + '_Diff_' + #dateTime + '.BAK'' WITH DIFFERENTIAL'
IF #backupType = 'L'
SET #sqlCommand = 'BACKUP LOG ' + #databaseName +
' TO DISK = ' + #path + #databaseName + '_Log_' + #dateTime + '.TRN'''
EXECUTE sp_executesql #sqlCommand
END
When I want to run the following command:
[dbo].[sp_doStuff] 'D:\FolderName\', 'MyDatabase', 'F';
It gives me this error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'D:'.
Msg 105, Level 15, State 1, Line 1
Unclosed quotation mark after the character string ''.
Does anyone have a clue why this gives errors? I can make it work by hard coding the path into the procedure, but I want to be able to pass it in a a parameter.
Try
' TO DISK = ''' + #path + #databaseName
etc...
It is not the issue of parameter. you are making wrong execute statement that uses single quote and you have not escaped that one..
you can escape single quote using two single quotes (NOT double quote). If a character string enclosed in single quotation marks contains an embedded quotation mark, represent the embedded single quotation mark with two single quotation marks.
e.g.
USE AdventureWorks
GO
SELECT *
FROM Person.Address
WHERE City = 'Villeneuve-d''Ascq'
GO
Ref:
Escape Character in SQL
SQL SERVER – How to Escape Single Quotes – Fix: Error: 105 Unclosed quotation mark after the character string ‘
Related
I created the below stored procedure in sql server that requires 3 parameters: Date, URL, & Table Name:
ALTER PROCEDURE [stg].[usp_Delete_Data]
(#DateLookBack date,
#siteUrl nvarchar(100),
#tableName SYSNAME)
AS
BEGIN
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DELETE FROM ' + CONCAT('[stg].[',#tableName,']') +
'WHERE date = ' + FORMAT(#DateLookBack, 'yyyyMMdd') +
'AND siteUrl = ' + #siteUrl
EXEC sp_executesql #Sql
END
When I pass in a url, like 'https://stackoverflow.com', I get an error message:
Incorrect syntax near 'https:'
How do I format the url string so that it can pass into the query successfully?
I'd strongly advise against this method. Having so many tables of the same structure that it requires a single procedure where the table name is dynamic is a code smell in itself.
If you must use dynamic sql though, at least use parameters as much as possible and only inject your table name, i.e.
SET #sql = CONCAT(N'DELETE FROM [stg].' QUOTENAME(#tableName),
' WHERE Date = #Date AND SiteUrl = #SiteUrl;');
EXECUTE sp_executesql #sql, N'#Date date, #SiteUrl nvarchar(100)', #date, #SiteUrl;
To find such issue, all you need is to PRINT the query before you use it! You could examine the query which is executed, if you printed it first.
Replace the commend Exec sp_executesql #Sql with the command PRINT #Sql and examine the query you get.
In your case, after you do it, then when you execute the procedure using the following command, then I can see all the issues.
EXECUTE dbo.[usp_Delete_Data]
#DateLookBack = '2022-02-27' ,#siteUrl = 'https://stackoverflow.com' , #tableName = 'c'
GO
The printed text which we get is: DELETE FROM [stg].[c]WHERE date = 20220227and siteUrl = https://stackoverflow.com
Now we can go over the errors (yes there are multiple errors here) one by one
(1) Notice that the 'WHERE date = ' missing a space before the "where" which might combine the word "where" with the table name that comes before it. You need to add space like ' WHERE date = '
same with the part after the and siteUrl - missing space before the and
(2) Notice this part: siteUrl = https://stackoverflow.com. in the query you are building you do not have quotation marks around the text of the URL => this lead to the error message.
instead of 'and siteUrl = ' + #siteUrl it should be: 'and siteUrl = ''' + #siteUrl + ''''
(3) same issue you have with the date - you do not have quotation marks around the text of the date
instead of ' WHERE date = ' + format(#DateLookBack,'yyyyMMdd') it should be ' WHERE date = ''' + format(#DateLookBack,'yyyyMMdd') + ''''
So, after adding these fixes, you get the following SP (I use PRING instead of execute but you can change this back)
CREATE OR ALTER PROCEDURE [usp_Delete_Data] (
#DateLookBack date,#siteUrl nvarchar(100), #tableName SYSNAME
) AS BEGIN
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DELETE FROM ' + CONCAT('[stg].[',#tableName,']')
--+ ' WHERE date = ' + format(#DateLookBack,'yyyyMMdd')
+ ' WHERE date = ''' + format(#DateLookBack,'yyyyMMdd') + ''''
+ ' and siteUrl = ''' + #siteUrl + ''''
--+ 'and siteUrl = ' + #siteUrl
PRINT #Sql
--Exec sp_executesql #Sql
END
and now if I execute the same query
EXECUTE dbo.[usp_Delete_Data]
#DateLookBack = '2022-02-27' ,#siteUrl = 'https://stackoverflow.com' , #tableName = 'c'
GO
It will print something that looks like:
DELETE FROM [stg].[c] WHERE date = '20220227'and siteUrl = 'https://stackoverflow.com'
BUT! NOW WE CAN GO TO THE MOST PROBLEMATIC ISSUE! Your procedure is open to SQL Injection! You should NOT use such code.
You should use parameters whenever you can when you use sp_executesql and not combine text text. Read the documentation of sp_executesql on how to use parameters as input: https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
I am dynamically building a SQL statement based on operations from a couple of different tables. The salient part of the SQL is below.
DECLARE #SQL NVARCHAR(MAX) = NULL
...
SELECT #sql = 'TRIM(CAST(' + STRING_AGG(EXPORT_COL, ' AS VARCHAR)) + '','' + TRIM(CAST(') FROM #TEMP_TABLE
SET #sql = 'SELECT''(''+'+#sql+' AS VARCHAR))+'')'''+'FROM '+'[mydatabase].[dbo].['+#TABLENAME+']'
SET #sql = REPLACE(#sql,'''','''''')
When I call the code using sp_executesql
EXEC sp_executesql #sql
I get this error
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ''
If I query #sql or print the value to the messages window in SSMS I get:
SELECT''(''+TRIM(CAST(COL1 AS VARCHAR)) + '','' + TRIM(CAST(COL2 AS VARCHAR))+'')''FROM [mydatabase].[dbo].[DATA_TABLE]
which is the output I would expect.
Copying the text and calling sp_executesql using a quoted version of the output string, the query succeeds with no error.
EXEC sp_executesql N'SELECT''(''+TRIM(CAST(COL1 AS VARCHAR)) + '','' + TRIM(CAST(COL2 AS VARCHAR))+'')''FROM [mydatabase].[dbo].[DATA_TABLE]'
I have already checked for the presence of non-printable characters as indicated in this post
I have also implemented a function that "should" strip out any non printable characters per this post. Yet the problem persists.
SQL Server 2017 Express (v14.0.1000.169) on Windows Server 2019 standard.
You need to be really careful about when and what parts need single quotes vs which parts need doubled quotes.
If you are writing the string to assign it to a variable, it needs the doubled quotes. If, however, the string already has the quote inside, it doesn't need to be doubled again.
Here's a simplified example showing the issues/approach
CREATE TABLE #Test (TestVal varchar(100));
INSERT INTO #Test (TestVal) VALUES ('abcde');
Now, when running the process with doubled quotes (similar to yours), here are the results
DECLARE #SQL2 nvarchar(max) = 'SELECT ''''('''' + TestVal + '''')'''' FROM #Test;'
PRINT #SQL2;
/* -- Result
SELECT ''('' + TestVal + '')'' FROM #Test;
*/
EXEC sp_executesql #SQL2;
/* -- Result
Msg 102, Level 15, State 1, Line 12
Incorrect syntax near ''.
*/
EXEC sp_executesql N'SELECT ''('' + TestVal + '')'' FROM #Test;';
/* -- Result
(abcde)
*/
Note that in the bottom command, the doubled quotes were needed so that the string would contain single quotes - and therefore works. However, when already in the string, it made the command fail.
Now, if we make the variable just have single quotes, it works
DECLARE #SQL3 nvarchar(max) = 'SELECT ''('' + TestVal + '')'' FROM #Test;'
PRINT #SQL3;
/* -- Result
SELECT '(' + TestVal + ')' FROM #Test;
*/
EXEC sp_executesql #SQL3;
/* -- Result
(abcde)
*/
DECLARE #SQLSTRING VARCHAR(1500);
DECLARE #TABLENAME1 VARCHAR(30)='NOV19_COMBINE'
---------------TABLE CREATION WITH FILE NAME--------------------------
SET #SQLSTRING = 'SELECT
CONVERT(VARCHAR('+ cast((select max(len(EMAIL)) from '+#TABLENAME1+' ) as VARCHAR(50))+'), EMAIL ) AS EMAIL,
IDENTITY (INT,1,1) AS RECORDID
INTO FOI_'+#TABLENAME1+'_CONV
FROM '+#TABLENAME1+' A'
PRINT #SQLSTRING
Error:
Msg 102, Level 15, State 1, Line 8
Incorrect syntax near '+#TABLENAME1+'.
You have an issue here:
CONVERT(VARCHAR('+ cast((select max(len(EMAIL)) from ' + #TABLENAME1 + ' ) as VARCHAR(50))+')
where you are trying to select from a table defined in #TABLENAME1. That also needs to be part of your dynamic SQL.
However you have another issue with your convert(varchar( code in that you cannot use a variable as as the length to varchar(). I suggest using varchar(max) because that only uses the storage required.
I have also made your dynamic SQL safe from injection with the use of QUOTENAME which I recommend you use in future.
Fixed version:
DECLARE #SQLSTRING VARCHAR(1500);
DECLARE #TABLENAME1 VARCHAR(30) = 'NOV19_COMBINE'
---------------TABLE CREATION WITH FILE NAME--------------------------
SET #SQLSTRING = 'SELECT CONVERT(VARCHAR(max), EMAIL) AS EMAIL, IDENTITY (INT,1,1) AS RECORDID INTO '
+ QUOTENAME('FOI_' + #TABLENAME1 + '_CONV') + ' FROM '
+ QUOTENAME(#TABLENAME1) + ' A'
PRINT #SQLSTRING
There is no reason I can think of to do it this way, but as an academic exercise, if one really needed the exact length of the EMAIL column then one would use the following query:
declare #SQLSTRING nvarchar(max), #TABLENAME1 VARCHAR(30) = 'NOV19_COMBINE', #EMAILLENGTH int
SET #SQLSTRING = 'SELECT #Length = max(len(EMAIL)) from ' + QUOTENAME(#TABLENAME1)
EXECUTE sp_executesql #SQLSTRING, N'#Length int OUTPUT', #Length = #EMAILLENGTH OUTPUT
SET #SQLSTRING = 'SELECT CONVERT(VARCHAR(' + convert(varchar(4),#EMAILLENGTH) + '), EMAIL) AS EMAIL'
+ ', IDENTITY (INT,1,1) AS RECORDID'
+ ' INTO ' + QUOTENAME('FOI_' + #TABLENAME1 + '_CONV')
+ ' FROM ' + QUOTENAME(#TABLENAME1) + ' A'
PRINT #SQLSTRING
This requires 2 sections of dynamic SQL, the first to find the length of the EMAIL column, which is then used to built the dynamic SQL for the actual query.
I simple want to update the price with for example here 3, but this with a stored procedure.
I already tried it with normal syntax:
update tblPrijs
set PrijsVerkoop = PrijsVerkoop + 1
where PrijsId = '6';
and this works fine.
But my stored procedure always returns :
updatetblPrijssetPrijsVerkoop=PrijsVerkoop+3.00wherePrijsId=11
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '='.
and I don't know what I did wrong..
The stored procedure:
alter PROCEDURE updatePrice
-- Add the parameters for the stored procedure here
#table nvarchar(50),
#field nvarchar(50),
#increase nvarchar(50),
#id nvarchar(50),
#value nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #sql as nvarchar(400);
-- Insert statements for procedure here
set #sql = 'update' + #table + 'set' + #field + '=' + #field + '+' + #increase +
'where' + #id + '=' + #value
print #sql /* drukt het resultaat van concat af */
exec(#sql)
END
GO
This is what I write in my query:
updatePrice'tblPrijs', 'PrijsVerkoop','3.00', 'PrijsId','11'
sorry for the dutch names of fields and tables...
I think it is something small and obvious that is wrong but I just can't see it..
This is in SQL Server 2012
Thank you in advance!
You need to add some spaces in there.
This:
set #sql = 'update' + #table + 'set' +#field + '=' ...
if you add the following parameters:
#table = SomeTable
#field = SomeField
will result in this sql:
updateSomeTablesetSomeField=
Surely that's not what you intended.
I'm assuming by the print #sql statement that you wanted to print your sql statement before executing it. What did it show?
Here's something to try:
set #sql = 'update ' + #table + ' set ' + #field + ' = ' + #field + ' + ' + #increase + ' where ' + #id + ' = ' + #value
I added spaces inside each string, at the start and at the end. Some of those spaces are not strictly needed, but they won't change the outcome either.
I have googled and searched TechNet and SO, so I'm sorry in advance if this happens to be a duplicate.
I am trying to set up a restore script for moving DBs from one server to another. It can be done manually but there are 45 databases and I'm trying to save myself some time, given that I have to do this twice.
Here's a code example (the path is based on naming conventions I've implemented elsewhere):
DECLARE #restore varchar(max)
DECLARE #i int = 0
DECLARE #centername varchar(max)
DECLARE #today DATETIME = SYSDATETIME()
DECLARE #todaystring varchar(10) = CONVERT(VARCHAR(10), #today, 121)
DECLARE #ourpath nvarchar(max) = 'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\'
WHILE #i < 15
BEGIN
SET #i = #i + 1
set #centername =
case
when #i = 1 then 'bosco'
--etc.
else '' end
set #restore =
'RESTORE DATABASE [aspnetdb-' + #centername + ']
FROM DISK = N''' + #ourpath + 'restore\aspnetdb-' + #centername + '-' + #todaystring + '.BAK''
WITH MOVE ''ASPNETDB_SOMEHASH_DAT''
TO ''' + #ourpath + 'aspnetdb-' + #centername + '.mdf'',
MOVE ''ASPNETDB_TMP_log'' TO ''' + #ourpath + 'aspnetdb-' + #centername + '_log.ldf'',
REPLACE, STATS = 10'
END
Output:
Msg 102, Level 15, State 1, Line 9
Incorrect syntax near 'C:'.
Msg 132, Level 15, State 1, Line 10
The label 'C' has already been declared. Label names must be unique within a query batch or stored procedure.
I don't understand. I didn't declare C, I tried to use it as an NVARCHAR and declared it as such in the stored statement (N'text'). But apparently TSQL thinks that's what I'm doing. My question is, why? Don't write SQL for me, just help me understand the syntax error.
Thanks!