MS SQL Stored Procedure for counting Dynamic table items - sql

I had wrote this below stored procedure and getting incorrect statement.
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
BEGIN
DECLARE #Counter INT
DECLARE #SQLQ VARCHAR(200)
SET NOCOUNT ON;
--SET #TableName = 'Member';
SET #SQLQ = 'SELECT COUNT(*) FROM dbo.[' + #TableName + ']';
--Preparing the above sql syntax into a new statement(get_counter).
--Getting an error here I had googled the prepare statement but don't know why facing this error.
PREPARE get_counter FROM #SQLQ;
#Counter = EXEC get_counter; -- here #resutl gets the value of the count.#TableName
DEALLOCATE PREPARE get_counter; -- removing the statement from the memory.
END
Then I had wrote another one:
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
BEGIN
DECLARE #Counter INT
DECLARE #SQLQ VARCHAR(200)
SET NOCOUNT ON;
--SET #TableName = 'Member';
SET #SQLQ = 'SELECT COUNT(*) FROM dbo.[' + #TableName + ']';
--Preparing the above sql syntax into a new statement(get_counter).
Execute #SQLQ; -- here #resutl gets the value of the count.#TableName
--DEALLOCATE PREPARE get_counter; -- removing the statement from the memory.
Return #Counter;
END
It is running fine but I can't get the result in the Counter , anyone please help me(I know that I haven't assigned any value to the counter but if I do I get error).
After your answer martin I had replace my code with yours now its :
ALTER PROCEDURE dbo.[Counter] #SchemaName SYSNAME = 'dbo' , #TableName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLQ NVARCHAR(1000)
DECLARE #Counter INT;
SET #SQLQ = 'SELECT #Counter = COUNT(*) FROM ' +
Quotename(#SchemaName) + '.' + Quotename(#TableName);
EXEC sp_executesql
#SQLQ ,
N'#Counter INT OUTPUT',
#Counter = #Counter OUTPUT
Return SELECT #Counter
END
Now I had retrieved it .
ALTER PROCEDURE dbo.[CreateBusinessCode]
#MemberID bigint,
#len int,
#RewardAccountID bigint,
#ConnectionStatusID tinyint,
#Assign smalldatetime
AS
BEGIN
SET NOCOUNT ON;
DECLARE #counter INT
EXEC #counter = dbo.Counter 'dbo','member';
Select #counter;
END

You should use SYSNAME for object identifiers and Quotename rather than concatenating the square brackets yourself.
ALTER PROCEDURE dbo.[Counter] #TableName SYSNAME,
#SchemaName SYSNAME = 'dbo'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLQ NVARCHAR(1000)
DECLARE #Counter INT;
SET #SQLQ = 'SELECT #Counter = COUNT(*) FROM ' +
Quotename(#SchemaName) + '.' + Quotename(#TableName);
EXEC sp_executesql
#SQLQ ,
N'#Counter INT OUTPUT',
#Counter = #Counter OUTPUT
SELECT #Counter
END

In SQL Server, if you want to get a result into a variable, you have two choices.
The first is to use cursors.
The second is to do dynamic SQL:
declare #sql varchar(max) = whatever;
declare #cnt int;
declare #cntTable table as (cnt int);
insert into #cntTable
exec(#sql);
select #cnt = t.cnt
from #cntTable
It is cumbersome, but one or the other does work.

try this one.It doesnt query the actual table but will provide you the count of rows.This is better way if you have large tables and you need approximate count.
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
begin
declare #objectid int,#counter int
select #objectid = object_id from sys.all_objects where name = #tablename and schema_id=SCHEMA_ID('dbo')
select #counter = sum(rows) from sys.partitions where object_id= #objectid
and index_id in (0,1)
select #counter
end
go

Related

SQL Server Default Column value from function

I want to set default value to Id column in Person table with a function
that goes like this:
Function:
IF OBJECT_ID ( 'GetLastId','FN') IS NOT NULL
DROP function GetLastId;
GO
CREATE FUNCTION [dbo].[GetLastId]
(#TableName nvarchar(max))
RETURNS int
AS
BEGIN
DECLARE #LastId int;
DECLARE #sql nvarchar(max);
SET #sql = 'SELECT #LastId = ISNULL(MAX(Id), 0) + 1 FROM ' + #TableName + ';'
EXECUTE sp_executesql #sql, N'#LastId int output', #LastId = #LastId output;
RETURN #LastId
END
and then:
UPDATE Person
SET Id = dbo.GetLastId('Person')
Running this code throws an error:
Only functions and some extended stored procedures can be executed from within a function.
So how to fix this and make it work as a default value?
And please do not say "Use triggers..." as I intend to use it with Entity Framework Core as default value for primary keys.
Thanks
You want a stored procedure, not a function:
create procedure [dbo].[GetLastId] (
#TableName nvarchar(max),
#LastId int output
) as
begin
declare #sql nvarchar(max);
set #sql = 'select #LastId = ISNULL(MAX(Id), 0) + 1 from ' + #TableName + ';'
EXECUTE sp_executesql #sql,
N'#LastId int output',
#LastId=#LastId output;
end;
You should also use quotename() around the table name to prevent unexpected things from happening.
Then you would call this as:
declare #lastId int;
exec dbo.GetLastId('Person', #lastid output);
update Person
set Id = #lastId;
You need to create stored procedure instead of function
create procedure [dbo].[GetLastId] (
#TableName nvarchar(max),
#ColumnName nvarchar(200),
#LastId int output
) as
begin
declare #sql nvarchar(max);
set #sql = 'select #LastId = ISNULL(MAX('+ #ColumnName +'), 0) + 1 from ' + #TableName + ';'
EXECUTE sp_executesql #sql,
N'#LastId int output',
#LastId=#LastId output;
end;
Then you can execute sp like below
declare #lastId int
exec dbo.GetLastId 'Person', 'Id' , #lastid output;
select #lastId
update Person
set Id = #lastId;

How to store a query result in a variable

SQL Server stored procedure:
CREATE PROCEDURE [dbo].[CheckTableStatus]
#DatabaseName AS NVARCHAR(50) = 'DBA',
#ProjectID AS NVARCHAR(50) = 'CommandLog'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TableCount as int
SET #Temp = 'DECLARE #cnt as int;'
SET #Temp = #Temp + 'USE '+ #DatabaseName +'; SELECT #cnt=COUNT(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables WHERE TABLE_NAME=''' + #ProjectID + ''';'
PRINT #Temp
EXEC sp_executesql #temp
--ASSIGN OUTPUT TO #TableCount
IF #TableCount > 0
-- Do something
END
How do I assign the results of #temp being executed to variable #TableCount?
If your goal is to get the count from #temp, try using an OUTPUT with your dynamic SQL:
EXEC sp_executesql #temp, N'#cnt int OUTPUT', #TableCount OUTPUT
These additions to sp_executesql will output the #cnt value in your dynamic SQL to the #TableCount in your current context.
Use the OUTPUT parameter:
CREATE PROCEDURE [dbo].[CheckTableStatus]
#DatabaseName as nvarchar(50) = 'DBA',
#ProjectID as nvarchar(50) = 'CommandLog'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TableCount as int,#Temp nvarchar(max)='';
-- SET #Temp = 'DECLARE #cnt as int;'
SET #Temp = #Temp + 'USE '+ #DatabaseName +'; SELECT #cnt=COUNT(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables WHERE TABLE_NAME=''' +
#ProjectID + ''';'
print #Temp
EXEC sp_executesql #temp,
N'#cnt int out',#TableCount out;
--ASSIGN OUTPUT TO #TableCount
IF #TableCount >0
-- Do something
print #TableCount;
END
But why not use the OBJECT_ID function?
Also your code is prone to SQL-injection.
You can set value as
SET #TableCount = #cnt

Dynamic SQL output of a query to a variable

I would like to output the result of the dynamic SQL into a variable called #Count but not sure what the syntax or even the code should like to accomplish this.
The code looks as follows:
declare #tab nvarchar(255) = 'Person.person'
declare #Count int
declare #SQL nvarchar(max) = 'select count(*) from '+ #tab
exec(#SQl)
select #Count
thank you
Here's another way to do it that also safely addresses the SQL Injection isuues:
/* Counts the number of rows from any non-system Table, *SAFELY* */
-- The table name passed
DECLARE #PassedTableName as NVarchar(255) = 'Person.Person';
-- Make sure this isn't a SQL Injection attempt
DECLARE #ActualTableName AS NVarchar(255)
SELECT #ActualTableName = TABLE_SCHEMA + '.' + TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = PARSENAME(#PassedTableName,1)
AND TABLE_SCHEMA = PARSENAME(#PassedTableName,2)
-- make a temp table to hold the results
CREATE TABLE #tmp( cnt INT );
-- create the dynamic SQL
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'SELECT COUNT(*) FROM ' + #ActualTableName + ';'
-- execute it and store the output into the temp table
INSERT INTO #tmp( cnt )
EXEC(#SQL);
-- Now, finally, we can get it into a local variable
DECLARE #result AS INT;
SELECT #result = cnt FROM #tmp;
You can utilize sp_executesql to execute your count() query, and output it #Count.
Try this:
-- Set the table to count from
declare #tab nvarchar(255) = 'Person.person'
-- Assign the SQL query
declare #SQL nvarchar(255) = N'SELECT count(*) FROM ' + #tab
-- Pepare for sp_executesql
declare #Count int
declare #Params nvarchar(100) = N'#Count int output'
-- Set the count to #Count
exec sp_executesql #SQL, #Params, #Count=#Count output
-- Output #Count
select #Count
One last thing: Person.person looks like you might be trying to reference a person column from a Person table. But the above query is a literal representation of what it looks like you're trying to achieve in your question.
The below question is pretty much identical to what you are asking here.
sp_executeSql with output parameter
DECLARE #retval int
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #tablename nvarchar(50)
SELECT #tablename = N'products'
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #tablename;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;

How to get the MAX count and MIN count from a Dynamic table

This is what I tried and got an error
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#MAXCount"
Code:
DECLARE #TempTable NVARCHAR(MAX)
DECLARE #MAXCount VARCHAR(10)
DECLARE #MINCount VARCHAR(10)
DECLARE #SQLSelect NVARCHAR(MAX)
SET #TempTable = 'Test_'+CONVERT(VARCHAR(10),##SPID)
SET #SQLSelect = 'SELECT #MAXCount = MAX(RowID), #MINCount = MIN(RowID) FROM Work_Tables.dbo.'+#TempTable+' (NOLOCK)'
EXEC SP_EXECUTESQL #SQLSelect
You have to pass variables as OUTPUT parameters:
DECLARE #TempTable NVARCHAR(MAX) = 'Test_'+ '1'; -- CONVERT(VARCHAR(10),##SPID)
DECLARE #MAXCount VARCHAR(10);
DECLARE #MINCount VARCHAR(10);
DECLARE #SQLSelect NVARCHAR(MAX);
SET #SQLSelect = 'SELECT #MAXCount = MAX(RowID), #MINCount = MIN(RowID) FROM dbo.'
+#TempTable+' (NOLOCK)';
EXEC dbo.SP_EXECUTESQL #SQLSelect
,N'#MAXCount VARCHAR(10) OUTPUT, #MinCount VARCHAR(10) OUTPUT'
,#MAXCount OUTPUT
,#MINCount OUTPUT;
SELECT #MAXCount, #MINCount;
LiveDemo
Notes:
It looks like poor design to create table per SPID. Related: SELECT * FROM sales + #yymm
Table name should has SYSNAME datatype, you could add QUOTENAME for additional protection against SQL Injection
Concatenating SQL query could be error-prone, you could use REPLACE
NOLOCK could lead to uncommited reads.
Something like:
DECLARE #MAXCount VARCHAR(10)
,#MINCount VARCHAR(10);
DECLARE #SQLSelect NVARCHAR(MAX) =
N'SELECT #MAXCount = MAX(RowID), #MINCount = MIN(RowID)
FROM <table_name> WITH (NOLOCK)';
DECLARE #TempTable SYSNAME = QUOTENAME('Test_'+ '1'); -- CONVERT(VARCHAR(10),##SPID)
SET #SQLSelect = REPLACE(#SQLSelect, '<table_name>', #TempTable);
EXEC dbo.SP_EXECUTESQL #SQLSelect
,N'#MAXCount VARCHAR(10) OUTPUT, #MinCount VARCHAR(10) OUTPUT'
,#MAXCount OUTPUT
,#MINCount OUTPUT;
SELECT #MAXCount, #MINCount;

dynamic column name in sql server 2008 in stored procedure

Can I use the following syntax in stored procedure,
set #count = (select count(*) from [dbo].[employee] where #column_name ='T')
CREATE PROCEDURE Proc_Name
#Column_Name NVARCHAR(128),
#COUNT INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT #count = count(*) from [dbo].[employee] where ' + QUOTENAME(#Column_Name)
+ N' =''T'''
EXECUTE sp_executesql #Sql
,N'#COUNT INT OUTPUT'
,#COUNT OUTPUT
END