After performing a database restore, I want to run a dynamic script to fix ophaned users. My script below loops through all users that are displayed after executing sp_change_users_login 'report' and applys an "alter user [username] with login = [username]" statement to fix SID conflicts. I'm getting an "incorrect syntax error on line 15" and can't figure out why...help..
DECLARE #Username varchar(100), #cmd varchar(100)
DECLARE userLogin_cursor CURSOR FAST_FORWARD
FOR
SELECT UserName = name FROM sysusers
WHERE issqluser = 1 and (sid IS NOT NULL AND sid <> 0×0)
AND suser_sname(sid) IS NULL
ORDER BY name
FOR READ ONLY
OPEN userLogin_cursor
FETCH NEXT FROM userLogin_cursor INTO #Username
WHILE ##fetch_status = 0
BEGIN
SET #cmd = ‘ALTER USER ‘+#username+‘ WITH LOGIN ‘+#username
EXECUTE(#cmd)
FETCH NEXT FROM userLogin_cursor INTO #Username
END
CLOSE userLogin_cursor
DEALLOCATE userLogin_cursor
Orphaned users can be fixed by using the [dbo].[sp_change_users_login] stored procedure.
Loop through all your users and execute the procedure
Good Luck
DECLARE #UserCount INT
DECLARE #UserCurr INT
DECLARE #userName VARCHAR(100)
DECLARE #vsql NVARCHAR(4000)
DECLARE #Users TABLE(
id INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
userName VARCHAR(100))
INSERT INTO #Users(UserName)
SELECT [name] FROM
--
master.[dbo].sysUsers -- SQL 2008 & SQL 2005
--master.dbo.sysxlogins -- SQL 2000
SELECT #UserCount = max([id]) FROM #Users
SET #UserCurr = 1
WHILE (#UserCurr <= #UserCount)
BEGIN
SELECT #userName=userName FROM #Users WHERE [id] =#UserCurr
SET #vsql = '[dbo].[sp_change_users_login] ''AUTO_FIX'',''' + #userName + ''''
-- EXEC(#vsql)
PRINT #vsql
SET #UserCurr = #UserCurr + 1
END
DECLARE #Username VARCHAR(100),
#cmd VARCHAR(100)
DECLARE userlogin_cursor CURSOR FAST_FORWARD FOR
SELECT username = name
FROM sysusers
WHERE issqluser = 1
AND (sid IS NOT NULL
AND sid <> 0x01)
AND Suser_sname(sid) IS NULL
ORDER BY name
FOR READ ONLY
OPEN userlogin_cursor
FETCH NEXT FROM userlogin_cursor INTO #Username
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'ALTER USER [' + #username + '] WITH LOGIN = [' + #username + ']'
EXECUTE(#cmd)
FETCH NEXT FROM userlogin_cursor INTO #Username
END
CLOSE userlogin_cursor
DEALLOCATE userlogin_cursor
I've used a similar approach, wrapping the code in a stored procedure:
USE [master]
GO
CREATE PROCEDURE [sp_AutoFixAllUsers]
AS
BEGIN
DECLARE #AutoFixCommand NVARCHAR(MAX)
SET #AutoFixCommand = ''
SELECT --dp.[name], dp.[sid] AS [DatabaseSID], sp.[sid] AS [ServerSID],
#AutoFixCommand = #AutoFixCommand + ' '
+ 'EXEC sp_change_users_login ''Auto_Fix'', ''' + dp.[name] + ''';'-- AS [AutoFixCommand]
FROM sys.database_principals dp
INNER JOIN sys.server_principals sp
ON dp.[name] = sp.[name] COLLATE DATABASE_DEFAULT
WHERE dp.[type_desc] IN ('SQL_USER', 'WINDOWS_USER', 'WINDOWS_GROUP')
AND sp.[type_desc] IN ('SQL_LOGIN', 'WINDOWS_LOGIN', 'WINDOWS_GROUP')
AND dp.[sid] <> sp.[sid]
IF (#AutoFixCommand <> '')
BEGIN
PRINT 'Fixing users in database: ' + DB_NAME()
PRINT #AutoFixCommand
EXEC(#AutoFixCommand)
PRINT ''
END
END
GO
I then used the sys.sp_MS_marksystemobject stored procedure to make my stored procedure available in all user databases (allowing it to operate on local objects)
EXEC sys.sp_MS_marksystemobject 'sp_AutoFixAllUsers'
You can then run it as follows:
EXEC [MyDB].[dbo].[sp_AutoFixAllUsers]
Or for every database using sp_msforeachdb:
EXEC sp_msforeachdb '[?].[dbo].[sp_AutoFixAllUsers]'
Related
If i have to grant access to Database using T-SQL and also verify if user already exist in SQL ,
If user don't exist then create account first and then grant access to database.
If user exist, just grant access to database.
I only create users in SQL. Not in Windows.
What will be by T-SQL Query to achieve above. ?
Try to create your login first, and the your user. This code first checks where all your users are assigned to which databases. Afterwards it checks if there is a login created and then it checks if the user exists. It is also set dynamically to you can just enter a DBName.
Example
---Get information on which users has access to my datase
set nocount on
declare #permission table (
Database_Name sysname,
User_Role_Name sysname
)
declare #dbs table (dbname sysname)
declare #Next sysname
insert into #dbs
select name from sys.databases order by name
select top 1 #Next = dbname from #dbs
while (##rowcount<>0)
begin
insert into #permission
exec('use [' + #Next + ']
SELECT ''' + #Next + ''', a.name as ''User or Role Name''
FROM [' + #Next + '].sys.database_principals a
left join [' + #Next + '].sys.database_permissions d on a.principal_id = d.grantee_principal_id
order by a.name, d.class_desc')
delete #dbs where dbname = #Next
select top 1 #Next = dbname from #dbs
end
set nocount off
--Declare my Variables
Declare #DBName VARCHAR(30)
DECLARE #IsWindowsUser int = 0
DECLARE #UserName nvarchar(50) = 'hestt4545tt'
DECLARE #PassWord nvarchar(50) = 'hest123123'
DECLARE #LoginExists int
DECLARE #UserExists int
DECLARE #LoginSQL nvarchar(MAX)
DECLARE #UserSQL nvarchar(MAX)
DECLARE #MultiDatabase nvarchar(max) ='LegOgSpass,LoadConfiguration'
--SET #DBName = 'LegOgSpass'
DECLARE myCursor CURSOR FOR
select [value] from string_split(#MultiDatabase,',')
OPEN myCursor
FETCH NEXT FROM myCursor INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
exec('USE '+ #DBName)
IF #IsWindowsUser = 0
BEGIN
/* Users are typically mapped to logins, as OP's question implies,
so make sure an appropriate login exists. */
SET #LoginExists = (Select count(principal_id) FROM sys.server_principals WHERE name = #UserName)
---Check if login exists - else create it
IF #LoginExists = 0
BEGIN
/* Syntax for SQL server login. See BOL for domain logins, etc. */
SET #LoginSQL = 'USE ' +#DBName + ' CREATE LOGIN '+#UserName +' WITH PASSWORD = '''+#PassWord+''''
PRINT 'Login doesnt exists'
EXEC (#LoginSQL)
PRINT 'Therefore i make a new login now'
SET #LoginExists = (Select count(principal_id) FROM sys.server_principals WHERE name = #UserName)
IF #LoginExists = 1
PRINT 'Login is now created and exists'
BEGIN
SET #UserExists = (SELECT count(principal_id) FROM sys.database_principals WHERE name = #UserName)
IF #UserExists =0
PRINT 'User doesnt exists'
BEGIN
SET #UserSQL = 'USE ' +#DBName+ ' CREATE USER ' +#UserName +' FOR LOGIN '+#UserName
EXEC (#UserSQL)
PRINT 'User is now created'
END
END
END
ELSE
BEGIN
SET #UserExists = (select COUNT(distinct User_Role_Name) from #permission where User_Role_Name =#UserName and Database_Name = #DBName)
IF #UserExists =0
BEGIN
PRINT 'Login already exists - go create user for access to database'
SET #UserSQL = 'USE ' +#DBName+ ' CREATE USER ' +#UserName +' FOR LOGIN '+#UserName
EXEC (#UserSQL)
PRINT 'User is now created'
END
END
END
ELSE
BEGIN
SET #LoginExists = (Select count(principal_id) FROM sys.server_principals WHERE name = REPLACE(REPLACE(#UserName,'[',''),']',''))
---Check if login exists - else create it
IF #LoginExists = 0
BEGIN
/* Syntax for SQL server login. See BOL for domain logins, etc. */
SET #LoginSQL = 'USE ' +#DBName + ' CREATE LOGIN '+#UserName +' FROM WINDOWS'
PRINT 'Windows Login doesnt exists'
EXEC (#LoginSQL)
PRINT 'Therefore i make a new window login now'
SET #LoginExists = (Select count(principal_id) FROM sys.server_principals WHERE name = #UserName)
IF #LoginExists = 1
PRINT 'Windows Login is now created and exists'
BEGIN
SET #UserExists = (SELECT count(principal_id) FROM sys.database_principals WHERE name = #UserName)
IF #UserExists =0
PRINT 'User doesnt exists'
BEGIN
SET #UserSQL = 'USE ' +#DBName+ ' CREATE USER ' +#UserName +' FOR LOGIN '+#UserName
EXEC (#UserSQL)
PRINT 'User is now created'
END
END
END
ELSE
BEGIN
SET #UserExists = (select COUNT(distinct User_Role_Name) from #permission where User_Role_Name =REPLACE(REPLACE(#UserName,'[',''),']','') and Database_Name = #DBName)
IF #UserExists =0
BEGIN
PRINT 'Window Login already exists - go create user for access to database'
SET #UserSQL = 'USE ' +#DBName+ ' CREATE USER ' +#UserName +' FOR LOGIN '+#UserName
EXEC (#UserSQL)
PRINT 'User is now created'
END
END
END
FETCH NEXT FROM myCursor INTO #DBName
--Ending cursor
END
CLOSE myCursor
DEALLOCATE myCursor
I have a script like this:
declare #username nvarchar(255)
declare #Alterstatement nvarchar(2000)
declare #userloginname nvarchar(255)
declare #InsertIntoHistory nvarchar(2000)
declare getusername cursor
for
select name from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
open getusername
Fetch next from getusername into #username
while ##FETCH_STATUS=0
begin
set #userloginname = '[' + #username + ']'`
set #Alterstatement = 'Alter Login' +#userloginname +'Disable'
set #InsertIntoHistory = 'Insert into DisabledAccountHistory (DisabledName,ServerName) values('''
+ #username + ''','''+ ##servername +''')'
exec(#alterstatement)
exec(#InsertIntoHistory)
Fetch next from getusername
into #username
end
close getusername
deallocate getusername
I'm using this script to disable some users and insert into a history table. But when I run this, some problem shows up. For example when I have only one user that I need to disable, it will run 3 times and insert 3 rows into the history table. How can I ask it just run 1 time for each user?
I would get rid of the cursor completely as it isn't needed here. You can still disable all the logins and insert the data into your history table.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'ALTER LOGIN ' + QUOTENAME(name) + ' DISABLE;'
from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
group by name
exec sp_executesql #SQL
Insert into DisabledAccountHistory
(
DisabledName
, ServerName
)
select name
, ##servername
from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
group by name
I'm using SQL Server Management Studio and I have multiple server connections and I need to know where a specific stored procedure exists in every server which contains multiple databases!
Looking for a query to run on each server
Here is a little script that you could run on each server.
I know that it uses a while loop, which is generally not preferred, someone might be able to improve on it.
Run the script from the master database.
SQL Query:
set nocount on
declare #procname varchar(150) = '<SPROCNAME_TO_FIND>' -- SET THIS TO THE STORED PROCEDURE NAME YOU ARE LOOKING FOR
declare #alldbonserver table
( databasename varchar(150),
doneflag bit )
declare #foundtbl table
( countcol int,
dbname varchar(150) )
declare #errortbl table
( dbname varchar(150) )
insert #alldbonserver
( databasename,
doneflag )
select sdb.name,
0
from sys.databases sdb
declare #curdbname varchar(150),
#sqlcmd varchar(max)
while exists (select 1
from #alldbonserver
where doneflag = 0)
begin
select top 1
#curdbname = databasename
from #alldbonserver
where doneflag = 0
select #sqlcmd = 'select distinct 1, ''' + #curdbname + ''' as dbname from ' + #curdbname + '.sys.objects where type = ''P'' and name = ''' + #procname + ''''
begin try
insert #foundtbl
( countcol, dbname )
exec(#sqlcmd)
end try
begin catch
insert #errortbl
values ( #curdbname )
end catch
update #alldbonserver
set doneflag = 1
where databasename = #curdbname
end
select dbname as 'Databases with stored procedure'
from #foundtbl
select dbname as 'Unable to access databases'
from #errortbl
set nocount off
Sample output:
Databases with stored procedure
-----------------------------------------
MainDatabase
SomeOtherDatabase
Unable to access databases
-----------------------------------------
model
ReportServer$SQLTemp
VeryPrivateDatabase
If you are using SSMS, you could create a server registration group that contains all of the servers you connect to, right click on the group and run this query against the group: exec sp_MSforeachdb 'select * from [?].sys.procedures where name=''<your_name_here>'''
This query will enumerate each of the databases on each of the servers in the group (note: the single quotes are like that for a reason...)
I just put this together on SQL2012. It should return all Stored Procs on a Server for a given name. If you leave #SPName blank, you will get all of them.
The Query utilizes sys.all_objects so you could easily change it to work the same for tables, functions, any or all db objects.
DECLARE #SPName VARCHAR(256)
SET #SPName = 'My_SP_Name'
DECLARE #DBName VARCHAR(256)
DECLARE #varSQL VARCHAR(512)
DECLARE #getDBName CURSOR
SET #getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (DBName VARCHAR(256),
SchemaName VARCHAR(256),
SPName VARCHAR(256))
OPEN #getDBName
FETCH NEXT
FROM #getDBName INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #varSQL = 'USE [' + #DBName + '];
INSERT INTO #TmpTable
SELECT '''+ #DBName + ''' AS DBName,
SCHEMA_NAME(schema_id) AS SchemaName,
name AS SPName
FROM sys.all_objects
WHERE [type] = ''P'' AND name LIKE ''%' + #SPName + '%'''
EXEC (#varSQL)
FETCH NEXT
FROM #getDBName INTO #DBName
END
CLOSE #getDBName
DEALLOCATE #getDBName
SELECT *
FROM #TmpTable
DROP TABLE #TmpTable
I was wondering if someone could help me with creating a while loop to iterate through several databases to obtain data from one table from two columns. this is was I have done so far. nothing works because i do not know how to make the select statement work through each database with regards to the table that I am querying from each database (dbo.tbldoc)
DECLARE #Loop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
DECLARE #tableName VARCHAR(255)
SET #Loop = 1
SET #DBName = ''
WHILE #Loop = 1
BEGIN
SELECT [name] FROM sys.databases
WHERE [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
SET #Loop = ##ROWCOUNT
IF #Loop = 0
BREAK
SET #SQL = ('USE ['+ #DBNAME +']')
IF EXISTS(SELECT [name] FROM sys.tables WHERE name != 'dbo.tbldoc' )
BEGIN
SELECT SUM(PGCOUNT), CREATED FROM **dbo.tbldoc**
END
ELSE
--BEGIN
PRINT 'ErrorLog'
END
I would consider sp_MSForEachDB which is a lot easier...
Edit:
EXEC sp_MSForEachDB 'USE [?]; IF DB_NAME() LIKE ''Z%%''
BEGIN
END
'
CREATE TABLE #T
(dbname sysname NOT NULL PRIMARY KEY,
SumPGCOUNT INT,
CREATED DATETIME)
DECLARE #Script NVARCHAR(MAX) = ''
SELECT #Script = #Script + '
USE ' + QUOTENAME(name) + '
IF EXISTS(SELECT * FROM sys.tables WHERE OBJECT_ID=OBJECT_ID(''dbo.tbldoc''))
INSERT INTO #T
SELECT db_name() AS dbname, SUM(PGCOUNT) AS SumPGCOUNT, CREATED
FROM dbo.tbldoc
GROUP BY CREATED;
'
FROM sys.databases
WHERE state=0 AND user_access=0 and has_dbaccess(name) = 1
AND [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
IF (##ROWCOUNT > 0)
BEGIN
--PRINT #Script
EXEC (#Script)
SELECT * FROM #T
END
DROP TABLE #T
My code to search for data from more than one database would be:
use [master]
go
if object_id('tempdb.dbo.#database') is not null
drop TABLE #database
go
create TABLE #database(id INT identity primary key, name sysname)
go
set nocount on
insert into #database(name)
select name
from sys.databases
where name like '%tgsdb%' --CHANGE HERE THE FILTERING RULE FOR YOUR DATABASES!
and source_database_id is null
order by name
Select *
from #database
declare #id INT, #cnt INT, #sql NVARCHAR(max), #currentDb sysname;
select #id = 1, #cnt = max(id)
from #database
while #id <= #cnt
BEGIN
select #currentDb = name
from #database
where id = #id
set #sql = 'select Column1, Column2 from ' + #currentDb + '.dbo.Table1'
print #sql
exec (#sql);
print '--------------------------------------------------------------------------'
set #id = #id + 1;
END
go
DECLARE #Loop int
DECLARE #MaxLoop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
SET #Loop = 1
SET #DBName = ''
set nocount on
SET #MaxLoop = (select count([name]) FROM sys.databases where [name] like 'Z%')
WHILE #Loop <= #MaxLoop
BEGIN
SET #DBName = (select TableWithRowsNumbers.name from (select ROW_NUMBER() OVER (ORDER by [name]) as Row,[name] FROM sys.databases where [name] like 'Z%' ) TableWithRowsNumbers where Row = #Loop)
SET #SQL = 'USE [' + #DBName + ']'
exec (#SQL)
...
...
set #Loop = #Loop + 1
END
***Note: I didn't add the check if exists here.
I ended up writing one last week on the fly for some stuff I was doing.
Blog post here:
http://tsells.wordpress.com/2012/02/14/sql-server-database-iterator/
Here is the code.
SET NOCOUNT ON
GO
use master
go
Declare
#dbname nvarchar(500),
#variable1 int,
#variable2 int,
#variable3 int,
#totaldb int = 0,
#totaldbonserver int = 0,
#totaldbwithmatches int = 0
-- Get non system databases
Declare mycursor CURSOR for select name, database_id from SYS.databases where database_id > 4 order by name desc
open mycursor
fetch next from mycursor into #dbname, #variable1
while (##FETCH_STATUS <> -1)
BEGIN
DECLARE #ParmDefinition NVARCHAR(500)
Declare #mysql nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].INFORMATION_SCHEMA.TABLES where Upper(TABLE_NAME) like ''MyTable''';
SET #ParmDefinition = N'#variable2OUT int OUTPUT'
set #totaldbonserver = #totaldbonserver + 1
Execute sp_executesql #mysql, #ParmDefinition, #variable2 OUTPUT
if #variable2 = 1
BEGIN
DECLARE #ParmDefinition2 NVARCHAR(500)
Declare #mysql2 nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].dbo.MyTable';
SET #ParmDefinition2 = N'#variable2OUT int OUTPUT'
Execute sp_executesql #mysql2, #ParmDefinition2, #variable3 OUTPUT
set #totaldb = #totaldb + 1
if #variable3 > 1
BEGIN
Print #dbname + ' matched the criteria'
set #totaldbwithmatches = #totaldbwithmatches + 1
END
ELSE
Select 1
END
fetch next from mycursor into #dbname, #variable1
END
PRINT 'Total databases on server: '
Print #totaldbonserver
PRINT 'Total databases tested () : '
Print #totaldb
PRINT 'Total databases with matches: '
Print #totaldbwithmatches
CLOSE mycursor
DEALLOCATE mycursor
This doesn't use a loop. Hope this helps!
Note that "TABLE_OWNER" is that same as "SCHEMA Owner" and "TABLE_TYPE" will identify if the item is a table OR view.
--This will return all tables, table owners and table types for all database(s) that are NOT 'Offline'
--Offline database information will not appear
Declare #temp_table table(
DB_NAME varchar(max),
TABLE_OWNER varchar(max),
TABLE_NAME varchar(max),
TABLE_TYPE varchar(max),
REMARKS varchar(max)
)
INSERT INTO #temp_table (DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE,REMARKS)
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; EXEC sp_tables'
SELECT DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE
FROM #temp_table
--Uncomment below if you are seaching for 1 database
--WHERE DB_NAME = '<Enter specific DB Name>'
--For all databases other than 'System Databases'
WHERE DB_NAME not in ('master','model','msdn','tempdb')
order by 1,2,3
You don't have to use a "USE DATABASE" statement. You can select from the particular database table by using a 3 part identifier as in:
select * from MyDatabase.dbo.MyTable
There is a view in my DB that someone defined with a * from one table. I just added a new column to that table and I want the view to reflect the new column. Besides re-executing the view creation script, is there another way to rebuild the view? I am looking for something similar to how sp_recompile will recompile a stored procedure (or more accurately flag it to be compiled next time it is called).
Update: On a long shot I tried calling sp_recompile on the view and while the call worked, it didn't rebuild the view.
Update 2: I would like to be able to do this from a script. So the script that adds the columns to the table could also update the view. So like I said, something similar to sp_recompile.
I believe what you're looking for is
sp_refreshview [ #viewname = ] 'viewname'
Updates the metadata for the specified
non-schema-bound view. Persistent
metadata for a view can become
outdated because of changes to the
underlying objects upon which the view
depends.
See Microsoft Docs
In order to rebuild all views of a SQL Server database, you could use the following script:
DECLARE #view_name AS NVARCHAR(500);
DECLARE views_cursor CURSOR FOR
SELECT TABLE_SCHEMA + '.' +TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'VIEW'
AND OBJECTPROPERTY(OBJECT_ID(TABLE_NAME), 'IsMsShipped') = 0
ORDER BY TABLE_SCHEMA,TABLE_NAME
OPEN views_cursor
FETCH NEXT FROM views_cursor
INTO #view_name
WHILE (##FETCH_STATUS <> -1)
BEGIN
BEGIN TRY
EXEC sp_refreshview #view_name;
PRINT #view_name;
END TRY
BEGIN CATCH
PRINT 'Error during refreshing view "' + #view_name + '".';
END CATCH;
FETCH NEXT FROM views_cursor
INTO #view_name
END
CLOSE views_cursor;
DEALLOCATE views_cursor;
This is a slightly modified version from this blog posting. It uses the sp_refreshview stored procedure, too.
As well as Cory's answer, you could define it properly using schemabinding and the full column list.
CREATE VIEW MyView
WITH SCHEMABINDING
AS
SELECT
col1, col2, col3, ..., coln
FROM
MyTable
GO
Slightly modified script that refreshes all views, calls sp_recompile, sp_refresh and gets list from sys.views:
DECLARE #view_name AS NVARCHAR(500);
DECLARE views_cursor CURSOR FOR SELECT DISTINCT name from sys.views
OPEN views_cursor
FETCH NEXT FROM views_cursor
INTO #view_name
WHILE (##FETCH_STATUS <> -1)
BEGIN
BEGIN TRY
EXEC sp_recompile #view_name;
EXEC sp_refreshview #view_name;
PRINT #view_name;
END TRY
BEGIN CATCH
PRINT 'Error during refreshing view "' + #view_name + '".';
END CATCH;
FETCH NEXT FROM views_cursor
INTO #view_name
END
CLOSE views_cursor;
DEALLOCATE views_cursor;
sp_refreshview does not seem to be relyable! When I used the code from Uwe Keim/BogdanRB I got many errors even if the view has no invalid references! The following code did the trick for me (to determine which view is invalid after schema changes):
DECLARE #view_name AS NVARCHAR(500);
DECLARE #Query AS NVARCHAR(600);
SET #Query = '';
DECLARE views_cursor CURSOR FOR SELECT DISTINCT ('[' + SCHEMA_NAME(schema_id) + '].[' + name + ']') AS Name FROM sys.views
OPEN views_cursor
FETCH NEXT FROM views_cursor
INTO #view_name
WHILE (##FETCH_STATUS <> -1)
BEGIN
EXEC sp_recompile #view_name;
SELECT #Query = 'SELECT ''' + #view_name + ''' AS Name, COUNT(*) FROM ' + #view_name + ' AS Count; ';
EXEC (#Query);
-- PRINT #view_name;
FETCH NEXT FROM views_cursor
INTO #view_name
END
CLOSE views_cursor;
DEALLOCATE views_cursor;
Here is my favorite script for this (I modified an old sp_exec checking script I had), it uses EXEC sp_refreshsqlmodule #name
SET NOCOUNT ON;
-- Set ViewOnly to 1 to view missing EXECUTES. Set to 0 to correct missing EXECUTEs
DECLARE
#ViewOnly INT; SET #ViewOnly = 0;
-- Role to set execute permission on.
DECLARE
#ROLE sysname ; set #ROLE = QUOTENAME('spexec');
DECLARE
#ID INT,
#LAST_ID INT,
#NAME NVARCHAR(2000),
#SQL NVARCHAR(2000);
DECLARE #Permission TABLE (
id INT IDENTITY(1,1) NOT NULL,
spName NVARCHAR(2000),
object_type NVARCHAR(2000),
roleName NVARCHAR(2000),
permission NVARCHAR(2000),
state NVARCHAR(2000)
)
--Initialise the loop variable
SET #LAST_ID = 0
--Get all the stored procs into a temp table.
WHILE #LAST_ID IS NOT NULL
BEGIN
-- Get next lowest value
SELECT #ID = MIN(object_id)
FROM sys.objects
WHERE object_id > #LAST_ID
-- Looking for Stored Procs, Scalar, Table and Inline Functions
AND type IN ('P','FN','IF','TF','AF','FS','FT','PC', 'V')
SET #LAST_ID = #ID
IF #ID IS NOT NULL
BEGIN
INSERT INTO #Permission
SELECT o.name,
o.type_desc,
r.name,
p.permission_name,
p.state_desc
FROM sys.objects AS o
LEFT outer JOIN sys.database_permissions AS p
ON p.major_id = o.object_id
LEFT OUTER join sys.database_principals r
ON p.grantee_principal_id = r.principal_id
WHERE o.object_id = #ID
AND o.type IN ('P','FN','IF','TF','AF','FS','FT','PC', 'V')
--Exclude special stored procs, which start with dt_...
AND NOT o.name LIKE 'dt_%'
AND NOT o.name LIKE 'sp_%'
AND NOT o.name LIKE 'fn_%'
END
END
--GRANT the Permissions, only if the viewonly is off.
IF ISNULL(#ViewOnly,0) = 0
BEGIN
--Initialise the loop variable
SET #LAST_ID = 0
WHILE #LAST_ID IS NOT NULL
BEGIN
-- Get next lowest value
SELECT #ID = MIN(id)
FROM #Permission
WHERE roleName IS NULL
AND id > #LAST_ID
SET #LAST_ID = #ID
IF #ID IS NOT NULL
BEGIN
SELECT #NAME = spName
FROM #Permission
WHERE id = #ID
PRINT 'EXEC sp_refreshsqlmodule ' + #NAME
-- Build the DCL to do the GRANT
SET #SQL = 'sp_refreshsqlmodule [' + #NAME + ']'
-- Run the SQL Statement you just generated
EXEC (#SQL)
END
END
--Reselect the now changed permissions
SELECT o.name,
o.type_desc,
r.name,
p.permission_name,
p.state_desc
FROM sys.objects AS o
LEFT outer JOIN sys.database_permissions AS p
ON p.major_id = o.object_id
LEFT OUTER join sys.database_principals r
ON p.grantee_principal_id = r.principal_id
WHERE o.type IN ('P','FN','IF','TF','AF','FS','FT','PC', 'V')
AND NOT o.name LIKE 'dt_%'
AND NOT o.name LIKE 'sp_%'
AND NOT o.name LIKE 'fn_%'
ORDER BY o.name
END
ELSE
BEGIN
--ViewOnly: select the stored procs which need EXECUTE permission.
SELECT *
FROM #Permission
WHERE roleName IS NULL
END
Right-click on the view and choose Refresh from the popup menu?
You can use this sp:
CREATE PROCEDURE dbo.RefreshViews
#dbName nvarchar(100) = null
AS
BEGIN
SET NOCOUNT ON;
DECLARE #p nvarchar(250) = '#sql nvarchar(max) out'
DECLARE #q nvarchar(1000)
DECLARE #sql nvarchar(max)
if #dbName is null
select #dbName = DB_NAME()
SELECT #q = 'SELECT #sql = COALESCE(#sql + '' '', '''') + ''EXEC sp_refreshview ''''[' + #dbName + '].['' + TABLE_SCHEMA + ''].['' + TABLE_NAME + '']'''';''
FROM [' + #dbName + '].INFORMATION_SCHEMA.Views '
EXEC sp_executesql #q , #p ,#sql out
EXEC sp_executesql #sql
END
GO