SQL Server - Variable declared but still says "Must declare the scalar variable" - sql-server

I'm trying to run this set of SQL commands on Microsoft SQL Server but I am getting this error:
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#dbstatus".
I thought I did declare the variable so I'm not sure why it's still throwing the error?
DECLARE #dbname nvarchar(100)
DECLARE #dbstatus varchar(500)
DECLARE #sqlCommand NVARCHAR(1000)
create table #temptable (dbname nvarchar(100), status varchar(500))
DECLARE c1 CURSOR READ_ONLY
FOR
SELECT '[' + name + ']' FROM sys.databases WHERE name = 'EDDS1084543'
OPEN c1
FETCH NEXT FROM c1 INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlCommand = 'SET #dbstatus = (SELECT Status FROM ' + #dbname + '.[EDDSDBO].[dtSearchIndex])'
EXECUTE sp_executesql #sqlCommand
INSERT INTO #tempTable(dbname, [status])VALUES (#dbname, #dbstatus)
FETCH NEXT FROM c1 INTO #dbname
END
CLOSE c1
DEALLOCATE c1

EXEC/sp_executesql creates a new connection (SPID) to the SQL Server, which is not your current session, so it cannot see the variable. Check the documentation.
Basically, you have to declare the parameter you want to pass into the call, and give it a value. In this case, both have to include the OUTPUT specifier.
EXECUTE sp_executesql #sqlCommand, '#dbstatus varchar(500) output', #dbstatus output

The problem is here:
SET #sqlCommand = 'SET #dbstatus = (SELECT Status FROM ' + #dbname + '.[EDDSDBO].[dtSearchIndex])'
EXECUTE sp_executesql #sqlCommand
This causes the server to execute the value of #sqlCommand as a standalone statement. Within this statement, #dbstatus has not been declared as a variable, hence the error. This is what's getting executed:
SET #dbstatus = (SELECT Status FROM [value in #dbname].[EDDSDBO].[dtSearchIndex])
Try this instead:
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlCommand = 'SET #dbstatus = (SELECT Status FROM ' + #dbname + '.[EDDSDBO].[dtSearchIndex])'
EXECUTE sp_executesql #sqlCommand, '#dbstatus varchar(500) output', #dbstatus output
INSERT INTO #tempTable(dbname, [status])VALUES (#dbname, #dbstatus)
FETCH NEXT FROM c1
INTO #dbname
END

Related

Parameterized dynamic query within cursor causes ERROR "Procedure expects parameter '#params' of type 'ntext/nchar/nvarchar'"

I need to get the max edit date for each table in our database and store in a temp table. The cursor works fine but when I run exec sp_executesql #sql I get a parameter expectation error:
Parameterized dynamic query within Cursor gives ERROR Procedure expects parameter '#params' of type 'ntext/nchar/nvarchar'
What am I doing wrong?
SET NOCOUNT ON
IF OBJECT_ID('tempdb..##GetMaxVistaEditDate') IS NOT NULL
DROP TABLE ##GetMaxVistaEditDate
CREATE TABLE ##GetMaxVistaEditDate
(
MySchema nvarchar(max),
MyTable nvarchar(max),
MaxVistaEditDate DateTime
)
-- SELECT * FROM ##GetMaxVistaEditDate
DECLARE MyCursor CURSOR FOR
SELECT
SCHEMA_NAME(t.schema_id) Schemaname,
t.name AS TableName
FROM
sys.tables t
WHERE
Schema_Name(t.Schema_id) like 'R_PERS%'
OPEN MyCursor
DECLARE #Schema VARCHAR(100), #Table VARCHAR(100), #MaxVistaEditDate DATETIME
DECLARE #sql NVARCHAR(MAX) = '', #params NVARCHAR(MAX);
SET #params = N'#MaxVistaEditDate DateTime OUTPUT';
FETCH FROM MyCursor INTO #Schema, #Table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'DECLARE #MaxVistaEditDate DATETIME SELECT #MaxVistaEditDate = (SELECT MAX(VistaEditDate) FROM ' + #SCHEMA + '.' + #TABLE + ')'
EXEC sp_executesql #sql, #MaxVistaEditDate OUTPUT
-- PRINT #SQL
-- PRINT #MaxVistaEditDate
INSERT INTO ##GetMaxVistaEditDate
SELECT #Schema, #Table, #MaxVistaEditDate
FETCH FROM MyCursor INTO #Schema, #Table
END
CLOSE MyCursor
DEALLOCATE MyCursor
You don't have to declare the variables on the sql string, you have to do it on a different variable, and you already have one for that (you name it #params).
Change your #sql definition for the following
SET #SQL = 'Select #MaxVistaEditDate = (SELECT MAX(VistaEditDate) From ' + #SCHEMA + '.' + #TABLE + ')'
And change your call for this:
exec sp_executesql #sql ,#params, #MaxVistaEditDate = #MaxVistaEditDate OUTPUT
and it should work.
Note: Don't forget to close and deallocate the cursor.
you can find an answer in this post
SP_EXECUTESQL and Output Parameter
and your sp_executesql statement don't have parameter definition and you don't have to declare a variable inside the dynamic query
declare #MaxVistaEditDate datetime
exec sp_executesql #sql ,N'#MaxVistaEditDateOut datetime OutPut, #MaxVistaEditDateOut=#MaxVistaEditDate OUTPUT

Set variable as schema

I have a query that I want to be able to use across database schemas. Right now it is written so that I need to replace the schema in several places of the query. How do I set that up as a variable so that all I need to do is change it in one place?
You can do this with Dynamic SQL:
DECLARE #sql VARCHAR(MAX)
,#schema VARCHAR(255) = 'dbo'
SET #sql = 'SELECT *
FROM '+#schema+'.yourTable
'
EXEC (#sql)
You could use this in a cursor to loop through schema's:
DECLARE #Iterator varchar(255)
,#strSQL varchar(MAX)
DECLARE xyz CURSOR
FOR
--Select stuff to iterate over
SELECT name
FROM sys.schemas
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
--Do stuff
SET #strSQL = 'SELECT *
FROM '+#Iterator+'.yourTable
'
Exec (#strSQL)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
To test your dynamic SQL statements, you can change EXEC to PRINT and ensure that the resulting query is as you intended.
Synonyms might be the answer to your question.
You can use dynamic SQL. Here is an example -
DECLARE #Column varchar(25)
DECLARE #sqlStmt varchar(max)
SET #Column = 'MyColumn'
SET #sqlStmt = N'SELECT ' + #Column + ' FROM MyTable'
EXEC (#sqlStmt)

Search database through Views

I m trying to search database values through the views.
I m stuck at the below error.
USE AdventureWorks
GO
--EXEC Customer.sp_FindInViews Stephen, Sales
ALTER PROCEDURE Customer.sp_FindInViews #stringToFind VARCHAR(100), #schema sysname
AS
SET NOCOUNT ON
DECLARE
#ViewName AS nVarChar(128)
, #TmpQuery AS nVarChar(500)
, #Out3 as int
, #sqlCommand VARCHAR(8000)
, #where VARCHAR(8000)
, #columnName sysname
, #cursor VARCHAR(8000)
DECLARE Outer_Cursor CURSOR FOR
SELECT schema_name(schema_id)+'.'+name as "View_Name",schema_id FROM [sys].[all_views]
where schema_id in (#schema)
OPEN Cur_Views
FETCH NEXT FROM Cur_Views INTO #ViewName
WHILE ##Fetch_Status = 0
BEGIN
SET #sqlCommand = 'SELECT * FROM ' + #ViewName + ' WHERE'
SET #where = ''
DECLARE col_cursor CURSOR FOR
SELECT syscolumns.name FROM sys.sysobjects "sysobjects"
INNER JOIN sys.syscolumns "syscolumns"
on syscolumns.id = sysobjects.id
WHERE (sysobjects.type = 'V' and SCHEMA_NAME(sysobjects.uid) + '.' +sysobjects.name = #ViewName)
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
IF #where <> ''
SET #where = #where + ' OR'
---------------------------------------------------------------------------
SET #where = #where + ' ' + #columnName + ' LIKE ''' + #stringToFind + ''''
SET #sqlCommand = #sqlCommand + #where
CREATE TABLE #Data (var varchar)
SELECT #TmpQuery = #sqlCommand
INSERT #Data exec (#TmpQuery)
SELECT #Out3 = var from #Data
PRINT #Out3
DROP TABLE #Data
FETCH NEXT FROM col_cursor INTO #columnName
END
CLOSE col_cursor
DEALLOCATE col_cursor
CLOSE Outer_Cursor
DEALLOCATE Outer_Cursor
END
GO
The code compiles , but it does give the error when executed as below :
EXEC Customer.sp_FindInViews Stephen, Sales
Msg 16915, Level 16, State 1, Procedure sp_FindInViews, Line 19
A cursor with the name 'Outer_Cursor' already exists.
Msg 16905, Level 16, State 1, Procedure sp_FindInViews, Line 22
The cursor is already open.
Msg 16924, Level 16, State 1, Procedure sp_FindInViews, Line 23
Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.
I m not sure , why I m getting this error. I feel i m handling them. Any input on this , would be helpful.
Thanks.
Looks to me that you've changed cursor names. You start by declaring Outer_Cursor then open a cursor called Cur_Views.
Also when you fetch from the cursor you are only putting the cursor values in to 1 variable, in the cursor declaration you list 2 fields (View_Name and schema_id).
DECLARE Outer_Cursor CURSOR FOR
SELECT schema_name(schema_id)+'.'+name as "View_Name",schema_id FROM [sys].[all_views]
where schema_id in (#schema)
OPEN Cur_Views
FETCH NEXT FROM Cur_Views INTO #ViewName
The "cursor is already open" errors occur when you run the procedure a second time becuase the original cursors are still open (as the first attempt errored before being able to close them).
Not sure if it's the answer you've been looking for, but SQLSearch (http://www.red-gate.com/products/sql-development/sql-search/) is an excellent tool for searching databases (of course, you can set it to search views only) and it's free...

Can I apply an update statement to multiple databases at the same time?

Say I want to run the following:
update users set age = 10
on databases:
db1, db2, db3
All on the same server, I want to loop through and perform the same action.
Currently I am doing this manually using management studio via the dropdown.
Hoping there is a better way.
You could probably do it with dynamic SQL. Something like so:
create table #dbs (db_name sysname not null)
insert into #dbs values ('db1'),('db2'),('db3')
declare curs cursor for
select db_name from #dbs
declare #db sysname, #sql nvarchar(max)
open curs
while(1=1)
begin
fetch next from curs into #db
if (##fetch_status <> 0)
break
set #sql = 'update ' + quotename(#db) + '.dbo.users set age = 10'
exec(#sql)
end
close curs
deallocate curs
drop table #dbs
Not sure about doing it 'dynamically', i.e. a FOR-EACH style loop on all the databases in a server, but this should work:
USE db1
update users set age = 10
GO
USE db2
update users set age = 10
GO
USE db3
update users set age = 10
Designate a server as a central management server and then add the other servers to the server group. Then you can run the update on all databases within the group. http://msdn.microsoft.com/en-us/library/bb934126.aspx
use [WWAUTHxxx__] -- a db containing active databases.
set nocount on
declare #Catalog as nvarchar(32)
declare #LibraryName as varchar(255)
declare #dbtable as varchar(50)
declare #retval as nvarchar(50)
declare #sSQL as nvarchar(max)
declare #parmdef as nvarchar(500)
declare #retvalout as nvarchar(50)
Declare Library_Cursor Cursor for
select top(1000) xCatalog, xLibraryName
from Active_DBs
order by xcatalog
Open Library_Cursor;
Fetch Next from Library_Cursor into #Catalog, #LibraryName
while ##Fetch_status = 0
begin
set #dbTable = #Catalog + '.dbo.las_circperiods'
set #ParmDef = N'#retvalOUT int OUTPUT';
set #sSQL = N'Select #retvalout = count(*) from ' + #dbtable
+ ' where xlastcircdate is null'
exec sp_executesql #ssql,#parmdef,#retvalout=#retval output
if #retval > 0 -- check/print Sql and then activate.
-- I like checking to see the potentially affected databases.
begin
print #Catalog + ',' + #LibraryName + ',' + #retval
set #ssql = N'update ' + #dbTable
+ ' set xlastcircdate = '''' '
+ ' where xlastcircdate is null'
-- print #ssql -- View what you might will do
exec sp_executesql #ssql -- Do it.
end
Fetch Next from Library_Cursor into #Catalog, #LibraryName
end;
close Library_cursor
Deallocate Library_cursor

Executing remote stored procedure within sp_executesql

I'm trying to get IDENT_CURRENT value on the linked server. I've created a stored procedure sp_current_identity on the remote server that has output parameter.
CREATE PROCEDURE [dbo].[sp_current_identity] ( #strTableName nvarchar(255), #intRowId int OUTPUT )
AS
BEGIN
select IDENT_CURRENT(#strTableName)
END
After that I have created two synonyms:sp_current_identity and sometable.
I need to execute sp_current_identity using sp_executesql (I'm creating a custom DataAtapter to work with synonyms via LLBLGEN 3.1). Please see the following example:
declare #p4 int
set #p4=NULL
exec sp_executesql N'SET XACT_ABORT ON; INSERT INTO [db].[dbo].[sometable] ([FieldName], [TableName], [UserField]) VALUES (#p1, #p3, #p4) ;
exec dbo.sp_current_identity #p5, #p2
;SET XACT_ABORT OFF',N'#p1 varchar(50),#p2 int output,#p3 varchar(50),#p4 varchar(50), #p5 varchar(200)',
#p1='test24',#p2=#p4 output,#p3='test24',#p4='test5',#p5='sometable'
select #p4
It works fine when this code is executed on the remote server (where sp_current_identity is local stored procedure), but it causes an exception when the code is executed on the local server.
Here is the error:
Procedure or function 'sp_current_identity' expects parameter '#strTableName', which was not supplied.
Thanks for your help!
Have you considered running EXEC remoteserver.database.dbo.sp_executesql 'dynamic SQL'; instead of trying to execute the dynamic SQL locally? The sp_current_identity procedure has to exist at the place where the query is actually executed, not where the query is called from.
I found that I had to assemble my dynamic call to the remote server in two steps. I was trying to get the Database ID:
DECLARE #sql nvarchar(4000)
DECLARE #parmDefinition nvarchar(500)
SET #parmDefinition = N'#retvalOUTside int OUTPUT'
SET #sql = 'SELECT TOP 1 #retvalOUT = database_id FROM [' + #ServerName + '].master.sys.databases WHERE name = ''''' + #dbname + ''''''
DECLARE #SPSQL nvarchar(4000) = '
DECLARE #DBID INT;
DECLARE #parmDefinition nvarchar(500);
SET #parmDefinition = N''#retvalOUT int OUTPUT'';
DECLARE #SQLinside nvarchar(400) =''' + #sql + ''';
EXEC [' + #ServerName + '].master.dbo' + '.sp_executeSQL #SQLinside, #parmDefinition, #retvalOUT = #retvalOUTside OUTPUT'
EXEC sp_executeSQL #SPSQL, #parmDefinition, #retvalOUTside=#DBID OUTPUT

Resources