Syntax Prob: Using Variables in Stored Procedures - sql-server

Can anyone tell how correct the following code below. Iam tryin to create a stored procedure that returns the rowcount of a table whose name is passed to it.
CREATE PROCEDURE spROWCOUNTER
(
#tablename nvarchar(20)
#rowCountVal int OUTPUT
)
AS
DECLARE #strQuery nvarchar(300)
SET #strQuery = 'SELECT #rowCountVal=COUNT(*) FROM '+#tablename
EXEC(#strQuery)
RETURN #rowCountVal
ERROR MESSAGE :
Incorrect syntax near '#rowCountVal'
Must declare scalar variable '#tablename'
Must declare scalar variable '#rowCountVal'
whereas the code below works fine
ALTER PROCEDURE spROWCOUNTER
(
#rowCountVal int OUTPUT
)
AS
SELECT #rowCountVal=COUNT(*) FROM DEFECT_LOG
RETURN #rowCountVal

CREATE PROCEDURE spROWCOUNTER
#tablename nvarchar(20),
#rowCountVal int OUTPUT
AS
SELECT #rowCountVal = ISNULL(SUM(spart.rows), 0)
FROM sys.partitions spart
WHERE spart.object_id = object_id(#tablename) AND spart.index_id < 2
RETURN #rowCountVal

The syntax problem is easy to solve. There is a missing comma (",") between your parameters. Insert the comma and the stored procedure compiles:
( #tablename nvarchar(20), #rowCountVal int OUTPUT )
Then, there is the major problem: you can't access the #rowCountVal parameter inside the EXEC statement. To solve this problem, you could use the built-in stored procedure sp_executesql.
Read this good article written by the SQL Server MVP Erland Sommarskog.
By the way: you don't have to "return" a variable. Return values are normally used for returning some status values. If you pass an output parameter, it will be automatically returned.

I would use sp_executesql instead of exec. Then you can pass in #rowCountVal as an output variable into the dynamic sql.
create PROCEDURE spROWCOUNTER
(
#tablename nvarchar(20),
#rowCountVal int OUTPUT
)
AS
DECLARE #strQuery nvarchar(300)
SET #strQuery = 'SELECT #rowCountVal = COUNT(*) FROM '+#tablename
exec sp_executesql #strQuery, N'#tablename nvarchar(20), #rowCountVal int OUTPUT', #tablename = #tablename, #rowCountVal = #rowCountVal output
RETURN #rowCountVal

if you want the row count as a function you can also check Speeding up the Performance of Table Counts in SQL Server 2005
Provided in the article function is apparently faster than calling count(*) for very big tables.

In an execute statement, you can use a temporary table to share data:
CREATE PROCEDURE spROWCOUNTER
#tablename nvarchar(20),
#rowCountVal int OUTPUT
AS
CREATE TABLE #Result( Rows INT )
EXEC( 'INSERT INTO #Result( Rows ) SELECT COUNT(*) FROM ' + #tablename )
SELECT #rowCountVal = Rows FROM #Result
RETURN #rowCountVal

yes, i missed the comma. But even after that,the value doesn't get stored in #rowCountVal.
SET #strQuery = 'SELECT #rowCountVal=COUNT(*) *FROM '+ #tablename
EXEC(#strQuery)
the query doesnt return nor displays any value.
By the way, I thought of calling this proc from other stored-procedures to get rowcounts.
Will the following statement work :
set #rCount = exec spROWCOUNTER('DEFECT_LOG')

Related

How can write function inside execute key in sql

Create Function fnRMatrixColorGet1(
#RMID varchar(20)
)
returns varchar(100)
as
begin
EXEC (N'SELECT ' + 'C'+#RMID + ' FROM vwemployeeget where empid='+#RMID)
return
end
As Gordon wrote in the comments, user defined functions in SQL Server can't execute dynamic SQL.
From Create User-defined Functions:
User-defined functions cannot make use of dynamic SQL or temp tables. Table variables are allowed.
However, you can create a stored procedure to do that:
CREATE PROCEDURE stpRMatrixColorGet1
(
#RMID varchar(20)
#MatrixColor varchar(100) OUTPUT
)
AS
DECLARE #Sql nvarchar(4000),
#Column sysname = N'C' + #RMID;
-- White list column name since it can't be parameterized
IF EXISTS
(
SELECT 1
FROM Information_Schema.Columns
WHERE Table_Name = 'vwemployeeget'
AND Column_Name = #Column
)
BEGIN
SET #SQL = N'SELECT #MatrixColor = QUOTENAME('+ #Column +') FROM vwemployeeget where empid = #RMID'
-- Safely execute dynamic SQL using sp_ExecuteSql
EXEC sp_ExecuteSql
#Sql,
N'#RMID varchar(20), #MatrixColor varchar(100) OUTPUT',
#RMID,
#MatrixColor OUTPUT
END

Converting Stored Procedure to Inline Function

I need to perform update but with stored procedure I can't use It.So I m no choice then converting to function.
This is my Stored procedure.
alter Proc spIsUnique
#columnname nvarchar(max),
#tablename nvarchar(max)
As
Begin
EXEC ('select IIf (count(*)>1,''False'',''True'') as [IsUnique-check]
from '+#tablename+'
group by '+#columnname)
End
I try to convert it to inline function
create Function fn_IsUnique(
#columnname nvarchar(max),
#tablename nvarchar(max)
) returns table
As
return
select IIf (count(*)>1,'False','True') as [IsUnique-check]
from #tablename
group by #columnname
It throws me this error
Must declare the table variable "#tablename
Help me convert scored procedure to inline function.
Edited:-
If it is not possible to convert into inline function.
Any alternative way How can I use result of this stored procedure into update statement.
try This
CREATE FUNCTION dbo.FnRetCnt
(
#tablename nvarchar(max)
)
RETURNS BIT
AS
BEGIN
DECLARE #Ret BIT
DECLARE #t TABLE
(
Cnt INT
)
INSERT INTO #t
SELECT
SUM(st.row_count)
FROM
sys.dm_db_partition_stats st
WHERE
object_id = OBJECT_ID(#tablename)
IF EXISTS(SELECT 1 FROM #t WHERE Cnt>0)
SET #Ret = 0
ELSE
SET #Ret = 1
RETURN #Ret
END
go
SELECT
dbo.FnRetCnt('AddressType')

SET FMTONLY OFF effects on stored procedures with dynamic query

I use Telerik Report designer R1 2017, and use a stored procedure with dynamic query as data source. It appears the schema cannot be obtained from the dynamic query. I Googled and found out that it's not possible without adding the following lines in the beginning of the stored procedure
IF 1 = 0 BEGIN
SET FMTONLY OFF
END
But I have really slow execution in this way! I'm not very familiar with store procedure execution process. I want to know What negative effects can it (SET FMTONLY OFF) have?
If you are using SQL Server 2012+ you could use WITH RESULT SETS to define resultset:
CREATE OR ALTER PROCEDURE dbo.mysp_test
AS
BEGIN
DECLARE #sql NVARCHAR(MAX) = 'SELECT id, b FROM dbo.tab ' + 'WHERE 1=1';
EXEC sp_executesql #sql
END
GO
SELECT *
FROM sys.dm_exec_describe_first_result_set (
'EXEC dbo.mysp_test'
,NULL
,NULL
);
The metadata could not be determined because statement 'EXEC sp_executesql #sql' in procedure 'mysp_test' contains dynamic SQL.
Using WITH RESULT SETS:
CREATE OR ALTER PROCEDURE dbo.mysp_test
AS
BEGIN
DECLARE #sql NVARCHAR(MAX) = 'SELECT id, b FROM dbo.tab ' + 'WHERE 1=1';
EXEC sp_executesql #sql
WITH RESULT SETS(
(id INT NOT NULL,
b CHAR(1)
)
);
END
GO
SELECT *
FROM sys.dm_exec_describe_first_result_set (
'EXEC dbo.mysp_test'
,NULL
,NULL
);
DBFiddle Demo

passing a TSQL function database name

Is there any way to pass a tsql function a database name so it can perform selects on that database
ALTER function [dbo].[getemailjcp]
(
#DB_Name varchar(100)
)
Returns varchar(4000)
AS
BEGIN
DECLARE #out varchar (4000);
DECLARE #in varchar (1000);
Set #out =
(select substring
((select ';' + e.email from
(SELECT DISTINCT ISNULL(U.nvarchar4, 'NA') as email
FROM [#DB_Name].dbo.Lists ...
In order to create dynamic SQL statement you should store procedure. For example:
DECLARE #DynamicSQLStatement NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #FirstID BIGINT
SET #ParmDefinition = N'#FirstID BIGINT OUTPUT'
SET #DynamicSQLStatement=N' SELECT #FirstID=MAX(ID) FROM ['+#DatabaseName+'].[dbo].[SourceTable]'
EXECUTE sp_executesql #DynamicSQLStatement,#ParmDefinition,#FirstID=#FirstID OUTPUT
SELECT #FirstID
In this example:
#DatabaseName is the passed as parameter to your procedure.
#FirstID is output parameter - this value might be return from your procedure.
Here you can find more information about "sp_executesql":
http://msdn.microsoft.com/en-us/library/ms188001.aspx
a better solution might be to replace "#DatabaseName" (a char variable)with:
"DB_NAME(<#DBId>)".
You can then continue to pass in the db names, but 1st converting them to IDs in the fcn. Also kinda resolves the contradiction of building a SQL stmt w/ a char variable in it, if using "sp_executesql" to help guard against SQL injection ("DB_NAME()" isn't a variable/can't be substituted).
So you'd have this:
DECLARE #DynamicSQLStatement NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #FirstID BIGINT
DECLARE #DBId INT
SET #DBId = DB_ID(#Db_Name) --raise the proper security/robustness** concern if this doesn't resolve.
SET #ParmDefinition = N'#FirstID BIGINT OUTPUT'
SET #DynamicSQLStatement=N' SELECT #FirstID=MAX(ID) FROM ['+DB_NAME(#DBId)+'].[dbo].[SourceTable]'

How to write Batch SQL query in procedure

if i want to write a procedure like below, is there some other way that,
to avoid using concatenate SQL statement, i am just afraid, if the input is too long, exceed the limit of max varchar, the code will have big problem.
Thanks
CREATE PROCEDURE UPDATE_ALL_STATUS
#IDs varchar(MAX) = null,
#status int = null
AS
BEGIN
IF #IDs is null
BEGIN
RETURN
END
DECLARE #SQL VARCHAR(MAX)
SET #SQL = 'UPDATE mytable SET status = ' + #status + ' WHERE id in (' + #IDs + ')'
EXECUTE #SQL
END
Instead of dynamic SQL (which is also vulnerable to SQL Injection Attacks) and passing in a VARCHAR(MAX), consider using Table Valued Parameters:
-- Creates the TVP type - only needed once!
CREATE TYPE IntegerTableType AS TABLE
( Identities INT );
GO
CREATE PROCEDURE UPDATE_ALL_STATUS
#IDs IntegerTableType READONLY,
#status int = null
AS
BEGIN
UPDATE mytable
SET status = #status
WHERE id IN
(SELECT Identities FROM #IDs)
END
This MSDN article shows how to call these from your .NET code.

Resources