SQL Server Views … gotcha while using sp_refreshview

Last week I blogged about use of Views in SQL Server. We also saw different types of view and best practices for designing new view and limitations that may prevent us creating a view too. As a part of best practice while designing we should always include keyword, WITH SCHEMABINDING which prevents any accidental changes to base table on which we have defined the view.

Today’s post is about case when we have not use SCHEMABINDING with view, and in that case if we change underlying structure it will not appear in view till we use system stored procedure called SP_REFRESHVIEW. The syntax is something like this,

-- Sudo Code
sp_refreshview [ @viewname= ] 'viewname'

Snippet 1

USE master
CREATE DATABASE wxy    
GO

-- Creating Sample DB to work with
USE wxy
go

-- Creating Sample Table 1
SELECT  p.BusinessEntityID,
        p.FirstName,
        p.LastName,
        a.AddressLine1,
        a.City,
        sp.Name [State/Province],
        cr.Name [Country],
        a.PostalCode
INTO    wxy.dbo.foo1
FROM    AdventureWorks2008.Person.Person p
        JOIN AdventureWorks2008.Person.Address a ON p.BusinessEntityID = a.AddressID
        JOIN AdventureWorks2008.Person.StateProvince sp ON a.StateProvinceID = sp.StateProvinceID
        JOIN AdventureWorks2008.Person.CountryRegion cr ON sp.CountryRegionCode = cr.CountryRegionCode
GO

-- Creating Sample Table 2
SELECT  p.BusinessEntityID,
        p.FirstName,
        p.LastName,
        a.AddressLine1,
        a.City,
        sp.Name [State/Province],
        cr.Name [Country],
        a.PostalCode
INTO    wxy.dbo.foo2
FROM    AdventureWorks2008.Person.Person p
        JOIN AdventureWorks2008.Person.Address a ON p.BusinessEntityID = a.AddressID
        JOIN AdventureWorks2008.Person.StateProvince sp ON a.StateProvinceID = sp.StateProvinceID
        JOIN AdventureWorks2008.Person.CountryRegion cr ON sp.CountryRegionCode = cr.CountryRegionCode
GO

-- Creating Sample Table 3
SELECT  p.BusinessEntityID,
        p.FirstName,
        p.LastName,
        a.AddressLine1,
        a.City,
        sp.Name [State/Province],
        cr.Name [Country],
        a.PostalCode
INTO    wxy.dbo.foo3
FROM    AdventureWorks2008.Person.Person p
        JOIN AdventureWorks2008.Person.Address a ON p.BusinessEntityID = a.AddressID
        JOIN AdventureWorks2008.Person.StateProvince sp ON a.StateProvinceID = sp.StateProvinceID
        JOIN AdventureWorks2008.Person.CountryRegion cr ON sp.CountryRegionCode = cr.CountryRegionCode
GO

-- Creating View 1
CREATE VIEW dbo.viewFoo1 WITH SCHEMABINDING
AS  SELECT  FirstName,
            LastName,
            City,
            [STATE/Province],
            Country
    FROM    dbo.foo1
go

-- Creating View 2
CREATE VIEW dbo.viewFoo2
AS  SELECT  FirstName,
            LastName,
            City,
            [STATE/Province],
            Country
    FROM    dbo.foo2
go

-- Creating View 3
CREATE VIEW dbo.viewFoo3
AS  SELECT  *
    FROM    dbo.foo3
go


-- Test Run
SELECT  *
FROM    dbo.viewFoo1
go
SELECT  *
FROM    dbo.viewFoo2
go
SELECT  *
FROM    dbo.viewFoo3
go

-- Altering Underlying Structure
ALTER TABLE foo1 DROP COLUMN Country
GO

ALTER TABLE foo2 DROP COLUMN Country
GO

ALTER TABLE foo3 DROP COLUMN Country
GO

-- Refreshing for View 3 will work while for View 2 it will generate error
EXECUTE sp_refreshview 'dbo.viewFoo2'
EXECUTE sp_refreshview 'dbo.viewFoo3'


-- House Cleaning

DROP VIEW dbo.viewFoo1
GO
DROP VIEW dbo.viewFoo2
GO
DROP VIEW dbo.viewFoo3
GO
DROP TABLE dbo.foo1
GO
DROP TABLE dbo.foo2
GO
DROP TABLE dbo.foo3
GO
DROP DATABASE wxy
GO

If you execute above snippet, you will get an error when you will try to remove column from first view, viewFoo1 because we are using SCHEMABINDING which will prevent any changes to underlying structure.

view_b[3] 1) Error Because of SCHEMABINDIG

2) Error because of Column Names
view_a

Views 2 and 3 … viewFoo2 and viewFoo3 still will let us drop column because we are not using SCHEMABINDING. But again when we will try to refresh the view 2 we will be greeted with error because we are specifying columns in its definition rather then simply using SELECT * … but we can still can refresh view 3 by using SP_REFRESHVIEW without any error.

Snippet 2

USE AdventureWorks2008
GO
-- Create First View

CREATE VIEW dbo.fooView1 AS
SELECT p.BusinessEntityID,p.FirstName,p.LastName,a.AddressLine1,a.City,sp.Name [State/Province],
cr.Name [Country],a.PostalCode FROM AdventureWorks2008.Person.Person p 
JOIN AdventureWorks2008.Person.Address a ON p.BusinessEntityID=a.AddressID
JOIN AdventureWorks2008.Person.StateProvince sp ON a.StateProvinceID=sp.StateProvinceID
JOIN AdventureWorks2008.Person.CountryRegion cr ON sp.CountryRegionCode=cr.CountryRegionCode
GO

-- Create Second View

CREATE VIEW dbo.fooView2 AS
SELECT * FROM dbo.fooView1
GO

-- Alter First View
ALTER VIEW dbo.fooView1 AS
SELECT p.BusinessEntityID,p.FirstName,p.LastName,a.AddressLine1,a.City 
FROM AdventureWorks2008.Person.Person p 
JOIN AdventureWorks2008.Person.Address a ON p.BusinessEntityID=a.AddressID
GO

-- Run the Second View
SELECT * FROM dbo.fooView2
GO

-- Refresh Second View 
EXEC sys.sp_refreshview 'dbo.fooView2'
GO

-- Re run the Second View
SELECT * FROM dbo.fooView2
GO

-- House Cleaning
DROP VIEW dbo.fooView1
GO
DROP VIEW dbo.fooView2
GO

In this snippet, view 2 … fooView2 is based on fooView1 (NOTE: in real life this is really really bad practice and in general it is not recommended to create a view that is based on another view. It can cause maintenance or alteration nightmares … but here for sake of understanding the concept we are using this example). So, now when we change definition of view1, if we execute just view 2 without using SP_REFRESHVIEW you will see error something like this screenshot.

view_c

But if refresh the view using SP_REFRESHVIEW it will work just fine.

If you already noticed … this SP_REFRESHVIEW is really a good help ONLY IF YOU HAVE USED SELECT * IN VIEW DEFINATION rather then STATING INDIVIDUAL COLUMN NAME. So this is kind of contradicting stuff … as a best practice you should always try to avoid using SELECT * , but if you are intend to use SP_REFRESHVIEW then you may have only that option to create view !!!

So, I would still take side of best practice that you should in general avoid creating view by simply SELECT * … but if you have view with such a definition then it’s your lucky day … you can use SP_REFRESHVIEW without any troubles … smile_wink

That’s it for now …

It’s Just a Thought … fingerscrossed

Gaurang Sign

Leave a Reply

Your email address will not be published. Required fields are marked *