I have a script to rebuild/reorganize indexes as follows in Azure SQL server. Problem is that it causes the application to hang because all the database sessions will be taken from this script. It is calling the rebuild sql statement inside a cursor, so I was expecting that it will rebuild the indexes one after the other. But it seems all the index rebuilds will run parallel, otherwise there is no reason why the database starts to hang and no connection can be made during this process. If there are few indexes to rebuild, then it is fine. The problem starts when there are like 60 indexes to rebuild. Is there a way to force to build the indexes one after the other? (sequentially , not in parallel).
CREATE PROCEDURE [dbo].[RebuildIndexes]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #objectid int;
DECLARE #indexid int;
DECLARE #partitioncount bigint;
DECLARE #schemaname nvarchar(130);
DECLARE #objectname nvarchar(130);
DECLARE #indexname nvarchar(250);
DECLARE #partitionnum bigint;
DECLARE #partitions bigint;
DECLARE #frag float;
DECLARE #command nvarchar(4000);
-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function
-- and convert object and index IDs to names.
SELECT
object_id AS objectid,
index_id AS indexid,
partition_number AS partitionnum,
avg_fragmentation_in_percent AS frag
INTO #indexes_to_build
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 10.0 AND index_id > 0 and page_count > 200
ORDER BY avg_fragmentation_in_percent DESC;
-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR FOR SELECT * FROM #indexes_to_build;
-- Open the cursor.
OPEN partitions;
-- Loop through the partitions.
WHILE (1=1)
BEGIN;
FETCH NEXT
FROM partitions
INTO #objectid, #indexid, #partitionnum, #frag;
IF ##FETCH_STATUS < 0 BREAK;
SELECT #objectname = QUOTENAME(o.name), #schemaname = QUOTENAME(s.name)
FROM sys.objects AS o
JOIN sys.schemas as s ON s.schema_id = o.schema_id
WHERE o.object_id = #objectid;
SELECT #indexname = QUOTENAME(name)
FROM sys.indexes
WHERE object_id = #objectid AND index_id = #indexid;
SELECT #partitioncount = count (*)
FROM sys.partitions
WHERE object_id = #objectid AND index_id = #indexid;
-- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding.
IF #frag < 30.0
SET #command = N'ALTER INDEX ' + #indexname + N' ON ' + #schemaname + N'.' + #objectname + N' REORGANIZE';
IF #frag >= 30.0
SET #command = N'ALTER INDEX ' + #indexname + N' ON ' + #schemaname + N'.' + #objectname + N' REBUILD WITH (ONLINE = ON)';
IF #partitioncount > 1
SET #command = #command + N' PARTITION=' + CAST(#partitionnum AS nvarchar(10));
EXEC (#command);
PRINT N'Executed: ' + #command;
END;
-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;
-- Drop the temporary table.
DROP TABLE #indexes_to_build;
COMMIT;
END;
Is there a way to force to build the indexes one after the other?
TSQL always executes sequentially. And your ALTER INDEX commands are no exception.
The COMMIT at the end is the problem. That procedure, as written, will only run with IMPLICIT_TRANSACTIONS on. With a COMMIT after the loop, you are accumulating exclusive locks (Sch-M) on the target indexes as you rebuild them. As you work through a large number of indexes, you will block more and more other sessions.
You should not do that. Instead don't use IMPLICIT_TRANSACTIONS or COMMIT after each index rebuild. EG:
IF #partitioncount > 1
SET #command = #command + N' PARTITION=' + CAST(#partitionnum AS nvarchar(10));
EXEC (#command);
PRINT N'Executed: ' + #command;
COMMIT;
My suggestion is to use Ola Hallemgren's stored procedure name Index Optimize which you can download from here. Everybody use his scripts for index maintenance tasks.
You can run the stored procedure as shown below:
EXECUTE dbo.IndexOptimize
#Databases = 'mydbnamehere',
#FragmentationLow = NULL,
#FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
#FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
#FragmentationLevel1 = 5,
#FragmentationLevel2 = 30,
#UpdateStatistics = 'ALL',
#OnlyModifiedStatistics = 'Y'
For more information, please visit this Web page.
Related
I created many tables and I have noticed that I have created one useless column in all the tables. I want to create a stored procedure which will drop one specific column and can be useful in all the column.
I created this stored procedure but I'm getting an error. Help me please
You cannot parametrize table and column names with parameters - those are only valid for values - not for object names.
If this is a one-time operation, the simplest option would be to generate the ALTER TABLE ... DROP COLUMN ... statements in SSMS using this code:
SELECT
'ALTER TABLE ' + SCHEMA_NAME(t.schema_id) + '.' + t.Name +
' DROP COLUMN Phone;'
FROM
sys.tables t
and then execute this code in SSMS; the output from it is a list of statement which you can then copy & paste to a new SSMS window and execute.
If you really want to do this as a stored procedure, you can apply the same basic idea - and then just use code (a cursor) to iterate over the commands being generated, and executing them - something like this:
CREATE PROCEDURE dbo.DropColumnFromAllTables (#ColumnName NVARCHAR(100))
AS
BEGIN
DECLARE #SchemaName sysname, #TableName sysname
-- define cursor over all tables which contain this column in question
DECLARE DropCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT
SchemaName = s.Name,
TableName = t.Name
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
EXISTS (SELECT * FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.Name = #ColumnName);
-- open cursor and start iterating over the tables found
OPEN DropCursor
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(1000)
-- generate the SQL statement
SET #Stmt = N'ALTER TABLE [' + #SchemaName + '].[' + #TableName + '] DROP COLUMN [' + #ColumnName + ']';
-- execute that SQL statement
EXEC sp_executeSql #Stmt
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
END
CLOSE DropCursor
DEALLOCATE DropCursor
END
This procedure should work.
It loops through all cols and then deletes the column where sum(col) is zero.
Take a Backup of the Table
alter procedure deletecolumnsifzero #tablename varchar(1000)
as
set nocount on
declare #n int
declare #sql nvarchar(1000)
declare #sum_cols nvarchar(1000)
declare #c_id nvarchar(100)
set #n = 0
declare c1 cursor for
select column_name from information_schema.columns
where
table_name like #tablename
--Cursor Starts
open c1
fetch next from c1
into #c_id
while ##fetch_status = 0
begin
set #sql=''
set #sql='select #sum_cols = sum('+#c_id+') from ['+#tablename+']'
exec sp_Executesql #sql,N'#sum_cols int out,#tablename nvarchar(100)',#sum_cols out,#tablename
if(#sum_cols = 0)
begin
set #n=#n+1
set #sql=''
set #sql= #sql+'alter table ['+#tablename+'] drop column ['+#c_id+']'
exec sp_executesql #sql
end
fetch next from c1
into #c_id
end
close c1
deallocate c1
I need to create a stored procedure that:
Accepts a table name as a parameter
Find its dependencies (FKs)
Removes them
Truncate the table
I created the following so far based on http://www.mssqltips.com/sqlservertip/1376/disable-enable-drop-and-recreate-sql-server-foreign-keys/ . My problem is that the following script successfully does 1 and 2 and generates queries to alter tables but does not actually execute them. In another word how can execute the resulting "Alter Table ..." queries to actually remove FKs?
CREATE PROCEDURE DropDependencies(#TableName VARCHAR(50))
AS
BEGIN
SELECT 'ALTER TABLE ' + OBJECT_SCHEMA_NAME(parent_object_id) + '.[' + OBJECT_NAME(parent_object_id) + '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys WHERE referenced_object_id=object_id(#TableName)
END
EXEC DropDependencies 'TableName'
Any idea is appreciated!
Update:
I added the cursor to the SP but I still get and error:
"Msg 203, Level 16, State 2, Procedure DropRestoreDependencies, Line 75
The name 'ALTER TABLE [dbo].[ChildTable] DROP CONSTRAINT [FK__ChileTable__ParentTable__745C7C5D]' is not a valid identifier."
Here is the updated SP:
CREATE PROCEDURE DropRestoreDependencies(#schemaName sysname, #tableName sysname)
AS
BEGIN
SET NOCOUNT ON
DECLARE #operation VARCHAR(10)
SET #operation = 'DROP' --ENABLE, DISABLE, DROP
DECLARE #cmd NVARCHAR(1000)
DECLARE
#FK_NAME sysname,
#FK_OBJECTID INT,
#FK_DISABLED INT,
#FK_NOT_FOR_REPLICATION INT,
#DELETE_RULE smallint,
#UPDATE_RULE smallint,
#FKTABLE_NAME sysname,
#FKTABLE_OWNER sysname,
#PKTABLE_NAME sysname,
#PKTABLE_OWNER sysname,
#FKCOLUMN_NAME sysname,
#PKCOLUMN_NAME sysname,
#CONSTRAINT_COLID INT
DECLARE cursor_fkeys CURSOR FOR
SELECT Fk.name,
Fk.OBJECT_ID,
Fk.is_disabled,
Fk.is_not_for_replication,
Fk.delete_referential_action,
Fk.update_referential_action,
OBJECT_NAME(Fk.parent_object_id) AS Fk_table_name,
schema_name(Fk.schema_id) AS Fk_table_schema,
TbR.name AS Pk_table_name,
schema_name(TbR.schema_id) Pk_table_schema
FROM sys.foreign_keys Fk LEFT OUTER JOIN
sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id --inner join
WHERE TbR.name = #tableName
AND schema_name(TbR.schema_id) = #schemaName
OPEN cursor_fkeys
FETCH NEXT FROM cursor_fkeys
INTO #FK_NAME,#FK_OBJECTID,
#FK_DISABLED,
#FK_NOT_FOR_REPLICATION,
#DELETE_RULE,
#UPDATE_RULE,
#FKTABLE_NAME,
#FKTABLE_OWNER,
#PKTABLE_NAME,
#PKTABLE_OWNER
WHILE ##FETCH_STATUS = 0
BEGIN
-- create statement for dropping FK and also for recreating FK
IF #operation = 'DROP'
BEGIN
-- drop statement
SET #cmd = 'ALTER TABLE [' + #FKTABLE_OWNER + '].[' + #FKTABLE_NAME
+ '] DROP CONSTRAINT [' + #FK_NAME + ']'
EXEC #cmd
-- create process
DECLARE #FKCOLUMNS VARCHAR(1000), #PKCOLUMNS VARCHAR(1000), #COUNTER INT
-- create cursor to get FK columns
DECLARE cursor_fkeyCols CURSOR FOR
SELECT COL_NAME(Fk.parent_object_id, Fk_Cl.parent_column_id) AS Fk_col_name,
COL_NAME(Fk.referenced_object_id, Fk_Cl.referenced_column_id) AS Pk_col_name
FROM sys.foreign_keys Fk LEFT OUTER JOIN
sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id INNER JOIN
sys.foreign_key_columns Fk_Cl ON Fk_Cl.constraint_object_id = Fk.OBJECT_ID
WHERE TbR.name = #tableName
AND schema_name(TbR.schema_id) = #schemaName
AND Fk_Cl.constraint_object_id = #FK_OBJECTID -- added 6/12/2008
ORDER BY Fk_Cl.constraint_column_id
OPEN cursor_fkeyCols
FETCH NEXT FROM cursor_fkeyCols INTO #FKCOLUMN_NAME,#PKCOLUMN_NAME
SET #COUNTER = 1
SET #FKCOLUMNS = ''
SET #PKCOLUMNS = ''
WHILE ##FETCH_STATUS = 0
BEGIN
IF #COUNTER > 1
BEGIN
SET #FKCOLUMNS = #FKCOLUMNS + ','
SET #PKCOLUMNS = #PKCOLUMNS + ','
END
SET #FKCOLUMNS = #FKCOLUMNS + '[' + #FKCOLUMN_NAME + ']'
SET #PKCOLUMNS = #PKCOLUMNS + '[' + #PKCOLUMN_NAME + ']'
SET #COUNTER = #COUNTER + 1
FETCH NEXT FROM cursor_fkeyCols INTO #FKCOLUMN_NAME,#PKCOLUMN_NAME
END
CLOSE cursor_fkeyCols
DEALLOCATE cursor_fkeyCols
END
FETCH NEXT FROM cursor_fkeys
INTO #FK_NAME,#FK_OBJECTID,
#FK_DISABLED,
#FK_NOT_FOR_REPLICATION,
#DELETE_RULE,
#UPDATE_RULE,
#FKTABLE_NAME,
#FKTABLE_OWNER,
#PKTABLE_NAME,
#PKTABLE_OWNER
END
CLOSE cursor_fkeys
DEALLOCATE cursor_fkeys
END
For running use:
EXEC DropRestoreDependencies dbo, ParentTable
Use a cursor to go through your SELECT results, populating a variable with the single column, and executing that query with EXEC(#YourVariable). Be sure to use parens around the variable!
The issue is that you are only preparing the SQL statement and not executing it (I think)
CREATE PROCEDURE DropDependencies(#TableName VARCHAR(50))
AS
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'ALTER TABLE ' + OBJECT_SCHEMA_NAME(parent_object_id) + '.[' + OBJECT_NAME(parent_object_id) + '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys WHERE referenced_object_id=object_id(#TableName)
EXEC #SQL
END
EXEC DropDependencies 'TableName'
Whenever using EXEC though from constructed strings, ensure you aren't vulnerable to SQL Injection attacks.
Try Out SP_ExecuteSQL or Exec. I see that you are using Exec. Perhaps SP_ExecuteSQL will work?
I'm adding scripts to an SSDT project to manage the addition/update of data from static data tables. Some of these tables reference each other, so I need to be able to shut down the constraints during the update so that I can let the data sit in an inconsistent state until all the scripts are run (I've got a file for table A, and a separate one for Table B).
Once completed, I need to re-enable all the constraints (and let the system check the data to ensure it's consistent). The caveat, is that for various reasons some of the constraints are currently disabled - and I need to ensure that they restore back to the state they were in before my script.
I can't seem to find a view/table that lists the current status of the FK constraints.
Is there any way to have SSDT publish do this automatically? Or if not, can anyone recommend a good solution for doing this?
In the end, I used a combination of sys.Foreign_Keys and sys.check_constraints, since the disable all command disabled both of them. I'm recording the status of the constraints prior to doing my work, then re-enabling them back to the original state at the end.
If OBJECT_ID('tempdb..#tempConstraints') is not null Drop Table #tempConstraints;
GO
IF (SELECT OBJECT_ID('tempdb..#tmpScriptErrors')) IS NOT NULL DROP TABLE #tmpScriptErrors
GO
CREATE TABLE #tmpScriptErrors (Error int)
GO
Create Table #tempConstraints
(
ConstraintName nVarchar(200),
TableName nVarchar(200),
SchemaName nVarchar(200),
IsNotTrusted bit
);
GO
Begin Tran
Insert into #tempConstraints (ConstraintName, TableName, SchemaName, IsNotTrusted)
Select K.name, object_name(K.parent_object_id), SCHEMA_NAME(T.schema_id), K.Is_Not_Trusted
FROM sys.foreign_keys K
Inner Join sys.tables T on K.parent_object_id = T.object_id
Where is_disabled = 0
Union all
Select K.name, object_name(K.parent_object_id), SCHEMA_NAME(T.schema_id), K.Is_Not_Trusted
from sys.check_constraints K
Inner Join sys.tables T on K.parent_object_id = T.object_id
Where is_disabled = 0
--Disable the Constraints.
Print 'Disabling Constraints'
Exec sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
----------------------------------------------------------------------------------------
--Do Work Here
----------------------------------------------------------------------------------------
Declare #name nvarchar(200);
Declare #table nvarchar(200);
Declare #schema nvarchar(200);
Declare #script nvarchar(max);
Declare #NotTrusted bit;
Declare constraints_cursor CURSOR FOR
Select ConstraintName, TableName, SchemaName, IsNotTrusted
From #tempConstraints;
Open constraints_cursor;
Fetch Next from constraints_cursor
into #name, #table, #schema, #NotTrusted;
While ##FETCH_STATUS = 0
Begin
--Restore each of the Constraints back to exactly the state they were in prior to disabling.
If #NotTrusted = 1
Set #script = 'ALTER TABLE [' + #schema + '].[' + #table + '] WITH NOCHECK CHECK CONSTRAINT [' + #name + ']';
Else
Set #script = 'ALTER TABLE [' + #schema + '].[' + #table + '] WITH CHECK CHECK CONSTRAINT [' + #name + ']';
exec sp_executesql #script;
If ##ERROR <> 0
Begin
INSERT INTO #tmpScriptErrors (Error)
VALUES (1);
End
Fetch Next from constraints_cursor
into #name, #table, #schema, #NotTrusted;
End
Close constraints_cursor;
Deallocate constraints_cursor;
If exists (Select 'x' from #tmpScriptErrors)
ROLLBACK TRAN;
Else
COMMIT TRAN;
Drop table #tmpScriptErrors
GO
Drop table #tempConstraints
GO
How to check all stored procedure is ok in sql server if I drop a table or fields?
I found Cade's answer useful in formulating my own script for checking objects in a database, so I thought I'd share my script as well:
DECLARE #Name nvarchar(1000);
DECLARE #Sql nvarchar(1000);
DECLARE #Result int;
DECLARE ObjectCursor CURSOR FAST_FORWARD FOR
SELECT QUOTENAME(SCHEMA_NAME(o.schema_id)) + '.' + QUOTENAME(OBJECT_NAME(o.object_id))
FROM sys.objects o
WHERE type_desc IN (
'SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
--include the following if you have schema bound objects since they are not supported
AND ISNULL(OBJECTPROPERTY(o.object_id, 'IsSchemaBound'), 0) = 0
;
OPEN ObjectCursor;
FETCH NEXT FROM ObjectCursor INTO #Name;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Sql = N'EXEC sp_refreshsqlmodule ''' + #Name + '''';
--PRINT #Sql;
BEGIN TRY
EXEC #Result = sp_executesql #Sql;
IF #Result <> 0 RAISERROR('Failed', 16, 1);
END TRY
BEGIN CATCH
PRINT 'The module ''' + #Name + ''' does not compile.';
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
FETCH NEXT FROM ObjectCursor INTO #Name;
END
CLOSE ObjectCursor;
DEALLOCATE ObjectCursor;
It won't catch everything (dynamic SQL or latebound objects), but it can be useful - call sp_refreshsqlmodule on all non-schema bound stored procedures (you can call it before to ensure that dependencies are updated and then query the dependencies, or call it afterwards and see if anything is broken):
DECLARE #template AS varchar(max)
SET #template = 'PRINT ''{OBJECT_NAME}''
EXEC sp_refreshsqlmodule ''{OBJECT_NAME}''
'
DECLARE #sql AS varchar(max)
SELECT #sql = ISNULL(#sql, '') + REPLACE(#template, '{OBJECT_NAME}',
QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME))
FROM INFORMATION_SCHEMA.ROUTINES
WHERE OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') IS NULL
OR OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') = 0
EXEC (
#sql
)
In addition to the script from Michael Petito you can check for issues with late-bound objects in SPs (deferred name resolution) like this:
-- Based on comment from http://blogs.msdn.com/b/askjay/archive/2012/07/22/finding-missing-dependencies.aspx
-- Check also http://technet.microsoft.com/en-us/library/bb677315(v=sql.110).aspx
select o.type, o.name, ed.referenced_entity_name, ed.is_caller_dependent
from sys.sql_expression_dependencies ed
join sys.objects o on ed.referencing_id = o.object_id
where ed.referenced_id is null
I basically did the same thing, but wrote it to be CURSORless which is super fast.
DECLARE #Name nvarchar(1000);
DECLARE #Sql nvarchar(1000);
DECLARE #Result int;
DECLARE #Objects TABLE (
Id INT IDENTITY(1,1),
Name nvarchar(1000)
)
INSERT INTO #Objects
SELECT QUOTENAME(SCHEMA_NAME(o.schema_id)) + '.' + QUOTENAME(OBJECT_NAME(o.object_id))
FROM sys.objects o
WHERE type_desc IN (
'SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
--include the following if you have schema bound objects since they are not supported
AND ISNULL(OBJECTPROPERTY(o.object_id, 'IsSchemaBound'), 0) = 0
DECLARE #x INT
DECLARE #xMax INT
SELECT #xMax = MAX(Id) FROM #Objects
SET #x = 1
WHILE #x < #xMax
BEGIN
SELECT #Name = Name FROM #Objects WHERE Id = #x
SET #Sql = N'EXEC sp_refreshsqlmodule ''' + #Name + '''';
--PRINT #Sql;
BEGIN TRY
EXEC #Result = sp_executesql #Sql;
IF #Result <> 0 RAISERROR('Failed', 16, 1);
END TRY
BEGIN CATCH
PRINT 'The module ''' + #Name + ''' does not compile.';
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
SET #x = #x + 1
END
Couple of ways that come to mind
Most obvious way run the procedures
check dependencies on the table before you drop the table or a field. then check out those dependent proceudres
generate scripts on all procedures and search for that field or table
Query sysobjects
Once I made change to a table such as column rename, I have to alter all the stored procedures, functions and views that refer the table column. Obviously I have to manually alter them one by one. But my database contains hundreds of objects like these. So I wanted to make sure I have altered all the depending objects. One solution is to recompile all the objects (via a script). But recompilation happens on each object’s next execution only. But what I want is to validate them and get the details now.
For that I can use “sp_refreshsqlmodule” instead of “sp_recompile”. This will refresh each object and throws an error if its not parsing correctly.
Here is the script below;
-- table variable to store procedure names
DECLARE #tblObjects TABLE (ObjectID INT IDENTITY(1,1), ObjectName
sysname)
-- get the list of stored procedures, functions and views
INSERT INTO #tblObjects(ObjectName)
SELECT '[' + sc.[name] + '].[' + obj.name + ']'
FROM sys.objects obj
INNER JOIN sys.schemas sc ON sc.schema_id = obj.schema_id
WHERE obj.[type] IN ('P', 'FN', 'V') -- procedures, functions, views
-- counter variables
DECLARE #Count INT, #Total INT
SELECT #Count = 1
SELECT #Total = COUNT(*) FROM #tblObjects
DECLARE #ObjectName sysname
-- start the loop
WHILE #Count <= #Total BEGIN
SELECT #ObjectName = ObjectName
FROM #tblObjects
WHERE ObjectID = #Count
PRINT 'Refreshing... ' + #ObjectName
BEGIN TRY
-- refresh the stored procedure
EXEC sp_refreshsqlmodule #ObjectName
END TRY
BEGIN CATCH
PRINT 'Validation failed for : ' + #ObjectName + ', Error:' +
ERROR_MESSAGE() + CHAR(13)
END CATCH
SET #Count = #Count + 1
END
If any object throws an error I can now attend to it and manually fix the issue with it.
None of the answers given can find the error resulting from renaming or dropping a table
but be happy, I have a solution on SQL Server 2017 and higher versions:
DECLARE #NumberRecords INT
DECLARE #RowCount INT
DECLARE #Name NVARCHAR(MAX)
DECLARE #Command NVARCHAR(MAX)
DECLARE #Result int
DECLARE #Names TABLE (
[RowId] INT NOT NULL IDENTITY(1, 1),
[Name] NVARCHAR(MAX),
[Type] NVARCHAR(MAX)
)
INSERT INTO #Names
SELECT
QUOTENAME(SCHEMA_NAME([Objects].schema_id)) + '.' + QUOTENAME(OBJECT_NAME([Objects].object_id)) [Name],
type_desc [Type]
FROM sys.objects [Objects]
WHERE type_desc IN ('SQL_STORED_PROCEDURE',
'SQL_TRIGGER',
'SQL_SCALAR_FUNCTION',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'VIEW')
ORDER BY [Name]
SET #RowCount = 1
SET #NumberRecords = (SELECT COUNT(*) FROM #Names)
WHILE (#RowCount <= #NumberRecords)
BEGIN
SELECT #Name = [Name]
FROM #Names
WHERE [RowId] = #RowCount
SET #Command = N'EXEC sp_refreshsqlmodule ''' + #Name + ''''
BEGIN TRY
EXEC #Result = sp_executesql #Command
IF #Result <> 0
BEGIN
RAISERROR('Failed', 16, 1)
END
ELSE
BEGIN
IF (NOT EXISTS (SELECT *
FROM sys.dm_sql_referenced_entities(#Name, 'OBJECT')
WHERE [is_incomplete] = 1))
BEGIN
DELETE
FROM #Names
WHERE [RowId] = #RowCount
END
END
END TRY
BEGIN CATCH
-- Nothing
END CATCH
SET #RowCount = #RowCount + 1
END
SELECT [Name],
[Type]
FROM #Names
I tried "Cade Roux" Answer , it went wrong and I fixed it as following
SELECT 'BEGIN TRAN T1;' UNION
SELECT REPLACE('BEGIN TRY
EXEC sp_refreshsqlmodule ''{OBJECT_NAME}''
END TRY
BEGIN CATCH
PRINT ''{OBJECT_NAME} IS INVALID.''
END CATCH', '{OBJECT_NAME}',
QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME))
FROM INFORMATION_SCHEMA.ROUTINES
WHERE OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') IS NULL
OR OBJECTPROPERTY(OBJECT_ID(QUOTENAME(ROUTINE_SCHEMA) + '.'
+ QUOTENAME(ROUTINE_NAME)),
N'IsSchemaBound') = 0
UNION
SELECT 'ROLLBACK TRAN T1;'
Same idea, but more universal - you check all user defined objects with bodies
And it shows you error during compiling. This is really useful after renaming/removing objects/columns etc
Just run it after database schema update to make sure that all body objects still valid
DECLARE #obj_name AS sysname, #obj_type AS sysname
DECLARE obj_cursor CURSOR FOR
SELECT SCHEMA_NAME(o.schema_id) + '.' + o.name, o.type_desc
FROM sys.objects o
INNER JOIN sys.sql_modules m ON o.object_id = m.object_id
WHERE o.is_ms_shipped = 0 AND m.is_schema_bound = 0
ORDER BY o.type_desc, SCHEMA_NAME(o.schema_id), o.name
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO #obj_name, #obj_type
WHILE (##FETCH_STATUS <> -1)
BEGIN
BEGIN TRY
EXEC sp_refreshsqlmodule #obj_name
--PRINT 'Refreshing ''' + #obj_name + ''' completed'
END TRY
BEGIN CATCH
PRINT 'ERROR - ' + #obj_type + ' ''' + #obj_name + ''':' + ERROR_MESSAGE()
END CATCH
FETCH NEXT FROM obj_cursor INTO #obj_name, #obj_type
END
CLOSE obj_cursor
DEALLOCATE obj_cursor
My approach was a little bit different. I've created alter script for a bunch of procs in SSMS and then waited for few seconds so SSMS process them and Ive got what I wanted:
O then SSMS right border a red dot for any line in error, which I can easily check, correct and later execute same script to update with correct values.
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