Store result of dynamic SQL generating XML to XML Variable - sql

So I have found plenty of examples across SO that show how to assign the result of dynamic SQL to variables but I have yet to find one that does it the way that I am trying to... So either I am going about this the wrong way or I just haven't found the proper syntax to perform this correctly.
Basically, I am trying to create an XML record of a row from a table before deleting the row from the table (think of it as an audit of the table) using a Stored Procedure. I was attempting to do this through dynamic SQL so that the Stored Procedure can be called from multiple other Stored Procedures that perform Delete operations on their corresponding tables, but before doing so they pass the necessary information (the #sql variable which will contain the select statement to obtain the record) to this Audit Stored Procedure.
Create Procedure dbo.AuditDelete(
#sql NVARCHAR(200),
#TableName VARCHAR(50),
#UserName NVARCHAR(256)
)
AS
DECLARE #XML XML
BEGIN
--at this point #sql will contain a select statement written by the calling stored procedure
--which is specifically written to return the record that is being deleted, example:
#sql = select * from my.table where tableId = 1 FOR XML AUTO
execute sp_executesql #sql, N'#XML XML OUTPUT', #XML = XML OUTPUT;
SELECT #XML
--I was doing this as a check to ensure that XML has the record
--because the execute command returned the XML record, but #XML is null...
--and I can't figure out why
--code for inserting into the Audit table
So I'm sure that its either some syntax I'm missing or perhaps I'm just going about this the wrong way. Any suggestions?
Note: this is being done in SSMS on SQL Server 2008 R2

You need to assign the result your query to the parameter in the dynamic SQL.
set #sql = N'set #XML = (select * from my.table where tableId = 1 FOR XML AUTO)'
execute sp_executesql #sql, N'#XML XML OUTPUT', #XML OUTPUT

Related

Insert string into SQL query as plain text in stored procedure

I'm trying to write a stored procedure in SQL Server that checks if search input exists in a column.
SELECT *
FROM Product
WHERE #Type LIKE #SearchResult
My problem is that when I fetch #Type from user's input, it comes as a string in SQL therefore syntax is wrong and it returns NULL.
Is there a way to get rid off single quotations or convert it to plain text in SQL?
So if #Type is "ProductName" it would appear in SQL as
SELECT *
FROM Product
WHERE ProductName LIKE #SearchResult (no single quotes around `ProductName`)
You have to use dynamic SQL to replace anything other than a constant in a query:
DECLARE #sql NVARCHAR(MAX);
SET #SQL = 'SELECT * FROM Product WHERE #Type LIKE #SearchResult';
SET #SQL = REPLACE(#SQL, '#Type', #Type);
exec sp_executesql #sql,
N'#SearchResult NVARCHAR(MAX)',
#SearchResult=#SearchResult;
You can still pass the #SearchResult value using a parameter.

sql server - fill results from executed query string in a temp table dynamically

I'm writing a stored procedure. I have a string which contains an sql query. For example:
DECLARE #sql nvarchar(max)
SET #sql = (N'SELECT pkOrderID FROM Orders')
(Just to note: this isn't what the select statement looks like. This is just an example of what I mean) I then want to execute the string and put the result in a temporary table E.g. #tempTable. I know EXEC(#sql) exists but not sure if it will do me any good in this situation. The other twist is that I do not know the names of all the columns in the returned #sql so the temp table #tempTable needs to be created dyanmically based off the return from #sql. Thanks for any help.
I think you could use SELECT INTO to do what you want but it would mean updating your string:
DECLARE #sql nvarchar(max)
SET #sql = (N'SELECT frompkOrderID INTO #tmporders FROM Orders')
then you should be able to run EXEC #sql to create the table
more information about SELECT INTO here : http://msdn.microsoft.com/en-au/library/ms188029.aspx
There is no simple way to do this. The problem with #JanR's solution is that the #tmporders table will be out of scope to the script that calls your stored procedure (ie It will produce an error like "Invalid object name '#rtmporders'"
One alternative is to use a global temp table (eg ##tmporders).
So your SP might look like this:
CREATE PROCEDURE TestSP
AS
BEGIN
SELECT pkOrderID INTO ##tmporders FROM Orders
END
GO
And the calling script might be like:
EXEC TestSP
SELECT * FROM ##temporders

Iterate through XML variable in SQL Server

I have a XML variable in a stored procedure (SQL Server 2008), its sample value is
<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>
I have to take each category and insert into table as a separate record. How to iterate in XML and take individual node value?
If I want to call a stored procedure and send each category as input parameter, how we can do that? The stored procedure is legacy one, which accept only one category at at time. I am trying to do invoke procedure in this way.
loop fetch single category from xml variable.
invoke stored procedure with current category.
move to next category.
loop until list contain value.
Any help will be appreciated.
Something like this?
DECLARE #XmlVariable XML = '<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>'
INSERT INTO dbo.YourTargetTable(CategoryColumn)
SELECT
XTbl.Cats.value('.', 'varchar(50)')
FROM
#XmlVariable.nodes('/parent_node/category') AS XTbl(Cats)
Update: if you must use the old legacy stored procedure and cannot change it (that would be my preferred way of doing this), then you would have to do the row-by-agonizing-row (RBAR) looping yourself, e.g. by using a table variable:
-- declare temporary work table
DECLARE #RbarTable TABLE (CategoryName VARCHAR(50))
-- insert values into temporary work table
INSERT INTO #RbarTable(CategoryName)
SELECT
XTbl.Cats.value('.', 'varchar(50)')
FROM
#XmlVariable.nodes('/parent_node/category') AS XTbl(Cats)
-- declare a single category
DECLARE #CategoryNameToBeInserted VARCHAR(50)
-- get the first category
SELECT TOP 1 #CategoryNameToBeInserted = CategoryName FROM #RbarTable
-- as long as we have data
WHILE #CategoryNameToBeInserted IS NOT NULL
BEGIN
-- execute your stored procedure here.....
EXEC sp_executesql N'dbo.YourStoredProcedure #CategoryName',
N'#CategoryName VARCHAR(50)',
#CategoryName = #CategoryNameToBeInserted
-- delete the category we just inserted from the temporary work table
DELETE FROM #RbarTable WHERE CategoryName = #CategoryNameToBeInserted
-- see if we still have more categories to insert
SET #CategoryNameToBeInserted = NULL
SELECT TOP 1 #CategoryNameToBeInserted = CategoryName FROM #RbarTable ORDER BY CategoryName
END
With XML in SQL Server there's always more than one way to do it. Depending on the size of your XML doc and the number of times you're querying it, you could be best off using sp_xml_preparedocument which parses the document, gives you a handle to reference it, and then you can query it as many times and ways as you want to. Here's how you do that:
declare #xml xml = '
<parent_node>
<category>Low</category>
<category>Medium</category>
<category>High</category>
</parent_node>'
declare #xml_handle int
exec sp_xml_preparedocument #xml_handle output, #xml
select value from openxml(#xml_handle, '/parent_node/category', 2) with (value varchar(100) 'text()') x
exec sp_xml_removedocument #xml_handle

sql server parameter size overloading

i am calling a stored procedure from my ASP.NET application. the stored procedure takes one parameter. the value that i am providing from my WEB Form is too large that it did not fully loading in variable of sql server. the data type of my sql server parameter is nvarchar(max) and the data type in my ASP.NET application is string.
the stored procedure is as below
Create procedure p_getProducts
#nm_emp nvarchar(max)
AS
BEGIN
select * from tblProduct where nm_user in(convert(nvarchar(max),#nm_emp));
END
please tell me which sql server data type i should use to overcome this problem.
Thanks.
For what I could suppose from your code, you should work with dynamic-sql and not using directly the parameter as value for the IN clause. Try with this proc.
Create procedure p_getProducts
#nm_emp nvarchar(max)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N'select * from tblProduct where nm_user in(' +
#nm_emp + N')'
EXEC sp_executeSQL #SQL

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.