SP_ExecuteSQL with Parameters doesn't like Contains clause - sql

I've been tasked with modifying a stored procedure so that it goes from looking like this:
DECLARE #ID nvarchar(10)
SET #ID = '0000000001'
DECLARE #SQL nvarchar(200)
SET #SQL = 'SELECT AppN FROM Apps WHERE CONTAINS(ID, ''"*'' + #ID + ''*"'')'
EXECUTE SP_EXECUTESQL #SQL
to using the parameter list for SP_EXECUTESQL and not string concatenation. The issue is that the following doesn't appear to work:
DECLARE #CID nvarchar(10)
SET #CID = '0000000001'
DECLARE #ID2 nvarchar(14)
SET #ID2 = '"*' + #ID + '*"'
DECLARE #SQL nvarchar(200)
SET #SQL = 'SELECT AppN FROM Apps WHERE CONTAINS(ID, ID2)'
DECLARE #ParamDefinition NCHAR(300)
SET #ParamDefinition = '#ID2 nvarchar(10)'
EXECUTE SP_EXECUTESQL #SQL, #ParamDefinition, #ID2
For whatever reason, the first set of statements works fine. The second does not. I get the following error message: Syntax error near '"' in the full-text search condition '"*00000000'.
If I remove 4 characters from #ID the second set of statements also works. Clearly it has something to do with the length of either #ID or the column ID but I can't figure out what.

You define #ID2 as nvarchar(10) in your parameters for the dynamic SQL.
It's actually 14 characters, so you are cutting off the end of it.
This outputs the correct variable for me:
DECLARE #CID nvarchar(10)
SET #CID = '0000000001'
DECLARE #ID2 nvarchar(14)
SET #ID2 = '"*' + #CID + '*"'
DECLARE #SQL nvarchar(200)
SET #SQL = 'SELECT #ID2'
DECLARE #ParamDefinition NCHAR(300)
SET #ParamDefinition = '#ID2 nvarchar(14)'
EXECUTE SP_EXECUTESQL #SQL, #ParamDefinition, #ID2

Related

How to set a SQL variable with either EXEC or sp_executesql

I am attempting to build a query on the fly that will set a variable. How would I do this?
Here is what I have so far
DECLARE #SQL as NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
set #SQL = 'set #maxRowCount = (select count(*) from Test)'
SET #ParmDefinition = N'#maxRowCount int';
EXECUTE sp_executesql #SQL, #ParmDefinition
When I run this I get the following error
Msg 8178, Level 16, State 1, Line 1
The parameterized query '(#maxRowCount int)set #maxRowCount = (select count(*) from docto' expects the parameter '#maxRowCount', which was not supplied.
I am trying to get #maxRowCount to be set to the total row count of the Test table
This works, you need the 'OUTPUT' in the parameter definition and a place where the result goes to as well
DECLARE #SQL as NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #iRowCount INT
SET #SQL = 'set #maxRowCount = (select count(*) from Test)'
SET #ParmDefinition = N'#maxRowCount INT OUTPUT';
EXECUTE sp_executesql #SQL, #ParmDefinition, #maxRowCount = #iRowCount OUTPUT
PRINT 'rowcount is:' + CONVERT(VARCHAR,#iRowCount)

SQL Server | Is my stored procedure is OK?

CREATE PROCEDURE spCountTableRowWHere
#TblName VARCHAR(50),
#TblID VARCHAR(10) = 'Id',
#WhereClause NVARCHAR(500) = '1=1'
AS
BEGIN
DECLARE #Query NVARCHAR(500)
DECLARE #ParamDefinition NVARCHAR(40)
DECLARE #Count INT
SET #Query = 'SELECT #C = COUNT('+#TblID+') FROM '+#TblName+' WHERE '+#WhereClause
SET #ParamDefinition = '#C INT OUTPUT'
EXECUTE SP_EXECUTESQL #Query, #ParamDefinition, #C = #Count OUTPUT
SELECT #Count
END
I am wondering if this kind of procedure is better than a separate procedures for different tables.
The short answer is no.
The long answer is that it is an opening to sql injection attacks.
Think what will happen if someone will put the following string in your where clause argument: 1=1; drop table myTable.

Selecting from a table where the name is passed as a variable

I am trying to write a simple stored proc which takes three arguments 'database name one', 'database name two' and 'table name'. The sql will then perform a row count for the defined table in each database and store it.
Working on it piecemeal I have hit the first problem in that you can't do
select * from #tablename
I know you can use dynamic sql with the exec command but this is not ideal as I can't return values.
The following example looks like it should work but doesn't.
declare #tablename as nvarchar(500)
declare #sqlstring as nvarchar(500)
declare #parmdefinition as nvarchar(500)
declare #numrows as bigint
set #tablename = N'dummy_customer'
set #parmdefinition = N'#tablenameIN nvarchar(500), #numrowsOUT as bigint OUTPUT'
select #sqlstring = 'select #numrowsOUT = count(*) from #tablenameIN'
select #sqlstring
exec sp_executesql #sqlstring, #parmdefinition, #tablenameIN = #tablename, #numrowsOUT = #numrows OUTPUT
select #numrows
The error message given is
Msg 1087, Level 16, State 1, Line 1
Must declare the table variable "#tablenameIN".
Currently using SQL Server 2008 SP2.
Edit:
We're doing this because we are doing a migration and the customer wants a report which shows the row count for each table in the source and destination database. As there are many tables being able to use sp_MSForEachTable to call the stored proc seems ideal.
Edit:
The final solution for future reference is
declare #tablename as nvarchar(500)
declare #sqlstring as nvarchar(500)
declare #parmdefinition as nvarchar(500)
declare #numrows as bigint
set #tablename = N'dummy_customers'
set #parmdefinition = N'#tablename nvarchar(500), #numrowsOUT as bigint OUTPUT'
select #sqlstring = 'select #numrowsOUT = count(*) from ' + quotename(#tablename)
exec sp_executesql #sqlstring, #parmdefinition, #tablename = #tablename, #numrowsOUT = #numrows OUTPUT
select #numrows
You'd have to use dynamic sql, and concatenate the table name into the SQL string to then execute via sp_executsql:
select #sqlstring = 'select #numrowsOUT = count(*) from ' + QUOTENAME(#tablename)
EXECUTE sp_executesql ....

Issues with building SQL strings

Why won't this piece of code work for me? It gives the error Must declare the scalar variable "#y".
DECLARE #y int
DECLARE #dbname VARCHAR(50)
SET #y = -1
SET #dbname = 'SomeDb.dbo.'
SET #sql = 'SELECT #y=1 from ' + #dbname + 'Respondent'
exec(#sql)
The exec function creates a new execution scope. Your #y variable doesn't exist there.
Use sp_executesql instead. Note that you'll still have to do some extra work to pass your variable across the function boundary.
DECLARE #IntVariable int;
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #max_title varchar(30);
SET #IntVariable = 197;
SET #SQLString = N'SELECT #max_titleOUT = max(Title)
FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #level';
SET #ParmDefinition = N'#level tinyint, #max_titleOUT varchar(30) OUTPUT';
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #max_titleOUT=#max_title OUTPUT;
SELECT #max_title;

Dynamic sql statement to update a variable

my sql statement is something like this below
DECLARE #OLD_NAV_VALUE AS INT
DECLARE #FINAL AS INT
SELECT #OLD_NAV_VALUE = [col1] from TBL_BA where DATE = #id_Date
SET #FINAL = #OLD_NAV_VALUE * 50
But the problem i am haveing here is that the column name in the select statement which is given as [col1] is a dynamic value. So i am trying something like this below.
DECLARE #OLD_NAV_VALUE AS INT
DECLARE #FINAL AS INT
EXEC('SELECT #OLD_NAV_VALUE = [' + #DYNAMIC_COL_NAME + '] from TBL_BA where DATE = ' + #id_Date)
SET #FINAL = #OLD_NAV_VALUE * 50
this gives an error that #OLD_NAV_VALUE has to be declared. So i tried declaring #OLD_NAV_VALUE inside the EXEC statement. But if i do this i am not able to use the same outside the EXEC statement.
Please let me know how to do this.
You can also use the sp_executesql statement with an output parameter:
declare #field nvarchar(50);
set #field = N'FieldToSelect';
declare #sql nvarchar(3000);
declare #parmDefinition nvarchar(500);
SET #parmDefinition = N'#returnValueOUT nvarchar(50) OUTPUT';
set #sql = N'SELECT #ReturnValueOUT = ' + #Field + ' FROM [TableName] WHERE [SomeCondition]'
declare #returnValue nvarchar(50);
EXECUTE sp_executesql #sql, #parmDefinition, #returnValueOut = #returnValue OUTPUT;
SELECT #returnValue
First, I'd suggest that you do a Google on "Erland dynamic SQL" and read his white paper on the subject.
Your design is probably not the best if it requires that you use a dynamic column name like this.
The reason that you can't do what you're trying to do is that everything in the EXEC is entirely in its own scope. If you absolutely have to do it this way though then you could use a table (either a normal table, or a global temporary table) to store the value for use outside of the EXEC.
We've used sp_executesql. Here's another example of a parameterized record count:
DECLARE #sql AS nvarchar(MAX)
SET #sql = N'SELECT #RecordCount = COUNT(*) FROM [{#SchemaName}].[{#TableName}]'
SET #sql = REPLACE(#sql, '{#SchemaName}', #SchemaName)
SET #sql = REPLACE(#sql, '{#TableName}', #TableName)
DECLARE #RecordCount AS int
EXEC sp_executesql
#query = #sql,
#params = N'#RecordCount INT OUTPUT',
#RecordCount = #RecordCount OUTPUT
This worked for me.
I declared a temp table and used it to receive the values from the select statement.
Something like below.
declare #i int
declare #v int
create table #t (val int)
insert into #t
exec ('declare #i int set #i = 0 select #i+1')
select * from #t