Dynamic insert sql stored procedure - sql

I have an issue regarding dynamic insert sql stored procedure because i need to insert an unknown amount (dynamically generated),
below is how it's written:
#Parameter as nvarchar(Max),
#SQLQuery as nvarchar(Max)
SET #SQLQuery = 'insert into <table>
(FieldOne,FieldTwo,FieldThree,GeneratedId,dateAdded,addedBy)
values '+#Parameter
EXEC sp_executesql #SQLQuery
When i try to execute this stored procedure using the below values:
#Parameter = (1,0,0,12345678123,"2016-03-22 23:26:25",123)
#SQLQuery = null
I even tried (for silly reasons) to try the below values:
#Parameter = (1,0,0,12345678123,2016-03-22 23:26:25,123)
#SQLQuery = null
I get the below Error from SQL Server
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '23'.
(1 row(s) affected)
But no rows are actually added.
The table i am using is as below:
ID (PK and autoincrement)
FieldOne: tinyint
FieldTwo: tinyint
FieldThree: tinyint
GeneratedId: nvarchar(50)
dateAdded: datetime
addedBy: int
I can't seem to figure out what i am doing wrong,
Thank you all in advance for your time and assistance.

Your date variable should be wrapped in single quotes, not double. Try that and it should work.
BUT.....
If you particularly want to go down this (unusual) road, I'd strongly suggest using the parameterised query support in sp_ExecuteSQL - it'll give you a better performing and usually more secure query.
Have a look https://msdn.microsoft.com/en-us/library/ms188001.aspx for more information on how to use sp_ExecuteSQL for parameterised queries.

If you need the double quotes, be sure that QUOTED_IDENTIFIER is on
ie.
SET QUOTED_IDENTIFIER ON;

Related

parameterize a stored procedure by a simple mathematical expression

I have a bunch of simple expressions, such as:
c=a+b
c=a*b
...
I would like to pass them as parameter to a stored procedure, which is going to perform an update using them.
CREATE TABLE t(
a int,
b int,
c int
);
INSERT INTO t VALUES (1,2,3),(4,5,6);
CREATE PROCEDURE sp #left_member varchar(50), #right_member
AS
BEGIN
UPDATE t
SET #left_member = #right_member
END
EXEC sp 'c', 'a+b'
EXEC sp 'c', 'a*b'
Is there a way of doing something like that ? I would like to possibly avoid dynamic SQL. In my target design, the expressions will be stored in their own table (editable online).
I generally don't recommend doing this, but dynamic SQL is pretty much the solution:
CREATE PROCEDURE usp_exec_dangerous_update (
#left_member nvarchar(50),
#right_member nvarchar(50)
)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'
UPDATE t
SET [left_member] = [right_member]
';
SET #sql = REPLACE(REPLACE(#sql, '[left_member]', #left_member), '[right_member]', #right_member);
EXEC sp_executesql #sql;
END;
Although such code can be useful in a thoughtful, well-designed system, in general it is not needed:
It exposes the system to SQL injection attacks. Running "generic" code is just dangerous.
It does not handle errors, which are easy to occur with this method.

OPENQUERY(SERVERNAME, STOREDPROCEDURE) Syntax error

This is my code
DECLARE #stringvariable nvarchar(200) = 'Hello';
DECLARE #sql nvarchar(2000) = SELECT * INTO ##global FROM OPENQUERY(DB1, ''EXEC GETCASE ''' + #stringvariable + ''''')'
Printing #sql returns a correctly formatted query, however SQL Server doesn't like #stringvariable and returns an error
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'Hello'.
Here is what the outputted query looks like
SELECT * INTO ##global FROM OPENQUERY(DB1, 'EXEC GETCASE 'Hello'')
How can I avoid this error? It seems like because my stored procedure takes a string parameter, it's throwing off the query. I've read that OPENQUERY does not support variables, but I've parameter the variable so it should work?
Appreciate your help!
The stored procedure exists in a database and a schema. You need to supply those. Supposing database db_name and schema schema_name:
DECLARE #stringvariable nvarchar(200) = 'Hello';
SET #stringvariable=REPLACE(#stringvariable,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #sql nvarchar(2000) = 'SELECT * INTO ##global FROM OPENQUERY(DB1, ''SET FMTONLY OFF;EXEC db_name.schema_name.GETCASE ''''' + #stringvariable + ''''''')';
I've also made sure single quotes are properly escaped in the #stringvariable.
It's also likely you need to start the query with SET FMTONLY OFF; so I've added that.
Update: To test this I created following simple procedure on a linked server local_server in database TEST_TT
CREATE PROCEDURE [dbo].[tst]
#i VARCHAR(128)
AS
SELECT #i AS field;
I then ran the following:
DECLARE #var VARCHAR(128)='TT.';
SET #var=REPLACE(#var,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #stmt VARCHAR(4000)='SELECT * INTO ##tt FROM OPENQUERY(local_server,''SET FMTONLY OFF;EXEC TEST_TT.dbo.tst '''''+#var+''''''');';
EXEC (#stmt);
SELECT * FROM ##tt;
DROP TABLE ##tt;
And I received the results. I count 7 (!!) single quotes at the end of the query... yuck! Updated original part with the same number of quotes.

Creating Stored Proc to Insert into a View

I have created some new tables that I need to insert to on a semi-regular basis. Due to normalization I decided to build a view on top of the base tables to make reports more logical for myself and EU's. I got the bright idea to try to use a stored procedure to push inserts into the base tables via a different view. I can run the insert statement in SSMS successfully, but when I try to create it into a stored procedure it will run because it appears to think my insert is a function.
Here is the error:
Msg 215, Level 16, State 1, Procedure jedi.p_ForcePush, Line 12
Parameters supplied for object 'jedi.v_midichlorians' which is not a function. If the parameters are intended as a table hint, a WITH keyword is required.
Here is my script:
CREATE PROCEDURE jedi.p_ForcePush
#Field varchar(25) = NULL,
#Value varchar(250) = NULL
AS
BEGIN
SET NOCOUNT ON;
insert jedi.v_midichlorians (#field) values (#value)
END
GO
I have poured out my petition to the googles but haven't found a good solution. I have tried lots of different combo's in my syntax but nothing doing.
Any help is much appreciated! (ps-SQL 2012)
CREATE PROCEDURE jedi.p_ForcePush
#Field varchar(25) = NULL,
#Value varchar(250) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'INSERT INTO jedi.v_midichlorians (' + QUOTENAME(#field)
+ N') values (#value)'
EXECUTE sp_executesql #sql
, N'#Value varchar(250)'
, #Value
END
GO
When you pass #field param as a parameter sql server treats it as a string not an object name, Using QUOTENAME() wraps the passed column name in [] square brackets telling sql server explicitly that it is an object(Table, column) name.
On a side note If your View has only one underlying table on then use view to insert values other wise use the table name.
If you do have more then one underlying table in your View's definition and you want to insert data using view then you will need to create Instead of triggers.
Best option is to do all insert, update delete operation directly to tables, avoid using views and then triggers for view based on more then one underlying tables.
though you mentioned that insert stmts run fine in ssMS, can you confirm that the same insert stmt you ran in SSMS ? becaus ethere is an error in this stmt.
"insert jedi.v_midichlorians (#field)"
syntex is inccoect and column name should dnot have "#" right?
also is this view based on a single table ?

How to set a variable to the result of a sql query with a variable as a table name in SQL 2005

I'm currently having trouble writing a stored procedure and setting the value of a variable of type int to the results of a select statement with a variable as the tablename. I've looked at old threads and tried multiple methods, but no luck. If I'm not getting an error regarding the tablename, I end up getting an error with a variable conversion issue. I've been working on this for too long and any help would be appreciated. Below is a portion of my code. Thanks
DECLARE #BATCHNUMBER VARCHAR --value set in earlier code
DECLARE #ETABLE VARCHAR(50); --the table name
DECLARE #FIRSTDOCID INT;
SET #ETABLE = 'tablename_' + #BATCHNUMBER; --CREATE FIRST TABLE NAME
SELECT #FIRSTDOCID = MIN(D0CID) FROM #ETABLE
The error I get is: Must declare the table variable "#ETABLE"
You are trying to select from a VARCHAR, not a table. The only way to make this work is by using Dynamic SQL.
DECLARE #SQL NVARCHAR(250);
SET #SQL = 'SELECT #OUTPUT = MIN(D0CID) FROM ' + QuoteName(#ETABLE);
EXEC sp_executeSql #SQL, N'#output INT OUTPUT', #FIRSTDOCID OUTPUT;
SELECT #FIRSTDOCID;
However, I would not suggest using Dynamic SQL as this often leads to SQL injection.
You'll probably have to do something like use exec if you're dynamically building the query:
SET #QUERY = "SELECT" + ...etc.
exec(#QUERY)
Since ETABLE is a varchar, and not, as expected, a 'table variable'.

sp_executesql securing the dynamic search keywords

I am trying to convert the SQL statement to support the sp_executesql to make it safe but I ran into an unsafe area. Hopefully you guys can help me with this. I've created the temp table to make it easier to demonstrate the problem.
The problem is at STEP # 6. I can use the STEP # 5 BUT this is not safe and it can be hacked easily. I don't really want break the keywords and search multiple times because of the system performance.
Error for MS SQL 2008 Msg 4145, Level 15, State 1, Line 4 An expression of non-boolean type specified in a context where a condition is expected, near 'ORDER'.
GO
/****** Object: StoredProcedure [dbo].[ups_MultiWareHouse] Script Date: 06/14/2012 09:12:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
create PROCEDURE ups_TestSearch(
#Keywords nvarchar(4000),
#SortColumns nvarchar(4000)
)
AS
--STEP #1 - Create Temp Table - Begin
CREATE TABLE #TempTable
(
ProductID uniqueidentifier,
ProductName varchar(600),
Price decimal(18,2),
Active bit
)
--STEP #2 - Insert couple records to search
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Mouse','10.12','1')
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Keyboard','20.45','1')
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Monitor','150.87','0')--Disable this product
--STEP #3 - Display the current table data
select 'STEP #3' as STEP, * FROM #TempTable
--STEP #4 - SETTING UP sp_executesql to support parameter substitution
--Set definition
DECLARE #ParmDefinition nvarchar(4000);
SET #ParmDefinition='
#Param1ProductName nvarchar(4000),
#Param2SortColumns nvarchar(4000)
'
DECLARE #SQLString nvarchar(4000);
--STEP #5- CONVERT THE #SQLString TO use #Keywords and #SortColumns
--Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'
SET #SQLString = N'SELECT ''STEP #5'' as STEP, #TempTable.* FROM #TempTable WHERE ('+#Keywords+') ORDER BY '+#SortColumns;--unsafe, open to hackers
EXECUTE sp_executesql #SQLString, #ParmDefinition, #Param1ProductName = #Keywords, #Param2SortColumns=#SortColumns;
--STEP #6- CONVERT THE #SQLString TO use #Keywords and #SortColumns
--Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'
SET #SQLString = N'SELECT ''STEP #6'' as STEP, #TempTable.* FROM #TempTable WHERE (#Param1ProductName) ORDER BY #SortColumns';--Safe but not working
SELECT #SQLString AS SeeStatement
EXECUTE sp_executesql #SQLString, #ParmDefinition, #Param1ProductName = #Keywords, #Param2SortColumns=#SortColumns;
--Drop temp table
DROP TABLE #TempTable
I think the issue is that in Step 5 you aren't using parameter substitution - that is, you are basically building the SQL statement by string concatenation. When you execute it via sp_executesql you could really just do this:
EXECUTE sp_executesql #SqlString
The code in Step 6 is performing parameter substitution. In this case, however, you are limited to using parameters only in locations where they are allowed in "normal" SQL expressions. For example, you can't do this in T-SQL:
DECLARE #Criteria NVARCHAR(500);
SET #Criteria = N' WHERE ProductName = ''Mouse'''
SELECT * FROM #MyTempTable + #Criteria
Depending upon how complex you expect your filter to be, you might be able to write the criteria to a temporary table and perform a join to the temporary table to limit the resulting data that is returned. Off the top of my head I am not sure how best to sort the resulting data unless you did that in the calling code perhaps?
Your error message indicates that the WHERE clause in step 6 is invalid, and so is the ORDER BY clause. This is because you're passing in strings as parameters to sp_executesql and trying to use them as entire clauses. Additionally, the statement references a parameter #SortColumns, but you appear to have named the parameter #Param2SortColumns.
Have a read of what some SQL server MVPs have written:
http://www.sommarskog.se/dynamic_sql.html
More to the point: http://www.sommarskog.se/dyn-search.html
http://www.sqlmag.com/article/tsql3/parameterizing-result-order
I don't see a simple way to alter your procedure to make this work, since you're passing in entire WHERE and ORDER BY clauses as parameters. What you should really do is redesign the proc. Supply each WHERE criterion as an individual parameter to ups_TestSearch. You resupply each WHERE parameter to sp_executesql and structure your initial SQL statement in this fashion:
SET #SQLString = SELECT and JOIN portions of command
SET #SQLString = #SQLString + 'WHERE 1 = 1 '
IF (#WhereParam1 IS NOT NULL)
SET #SQLString = #SQLString + 'AND (SomeTable.SomeColumn = #WhereParam1) '
IF (#WhereParam2 IS NOT NULL)
SET #SQLString = #SQLString + 'AND (SomeTable.SomeColumn = #WhereParam2) '
...
If necessary, you can use the same structure to add joins to the statement.
The ORDER BY structure depends on how complex this might get, and whether you know all possible involved columns. If it's relatively simple, you can write it out as a CASE statement as follows, or break it up as individual parameters, as I recommend for the WHERE clause.
ORDER BY
CASE WHEN CHARINDEX(#SortColumns, 'SortCol1') > 0 THEN SortCol1 ELSE NULL END,
CASE WHEN CHARINDEX(#SortColumns, 'SortCol2') > 0 THEN SortCol2 ELSE NULL END,
...
The simplest thing to do here might be to sort at the application level rather than the DB, but that could be just as infeasible as a complex ORDER BY clause would be to parameterize.