jump to navigation

Looking into Views in SQL Server September 19, 2011

Posted by fofo in Sql Server 2005, SQL Server 2008.
Tags: , ,
trackback

In this post I would like to look into Views in SQL Server.

As always I will demonstrate all the various concepts with hands-on examples.

I know that all these concepts are well documented elsewhere. I will try to look into views with step by step instructions and many screenshots.

Many people have found useful my style of posting. They feel they can follow the steps and master the technology.

Views have many advantages and when used correctly can benefit our applications.Please note that I will use T-SQL to create/alter/drop views. I will not use SSMS wizzards and graphic tools.

Views are virtual tables.What is stored in SQL Server as a distinct object is the definition of the view(SELECT statement) and nothing else. Some people refer to views as a stored query producing a virtual table.The data that is accessible through the view is not stored in the database.

View can provide data to client applications.We use them to customise data with different filters and sorts for different users or different applications. The end user or application do not have to know the structure of the data. We can restrict users to specific rows and columns of a table so they do not have access to unecessary or sensitive data. Many times we use views to provide aggregate data to the user and join columns from multiple tables so that they look like a single table.We can also use views to partition data.

One thing many people do not know is that a user or a role that someone else has granted them access to a particular view can have access to the underlying data even if that user or role does not have explicit access to the underlying tables.So permissions granted for a view supersede permissions revoked for the underlying tables.

Now let’s write some views. We need an instance of SQL Server runing in our machine. I have SQL Server 2008 R2 Developer edition.You can use any edition of SQL Server you might have installed in your machine.We need a database as well. I will use the Northwind database.

You can download and install the free SQL Server Express edition from here. If you need the installation scripts for the sample Northwind database, click here

There are some rules/restrictions regarding creating views.

  • You cannot have an ORDER BY clause unless there is a TOP or FOR XML clause present
  • Obviously the name of the view must follow all the rules for identifiers and must be unique for each schema in the database
  • Nested views cannot exceed 32 levels
  • Views cannot have rules or defaults
  • You can have only INSTEAD OF triggers applied to views
  • You cannot create temporary views or views on temporary tables
  • COMPUTE,COMPUTE BY on INTO clauses are not allowed in views

We can create views using the SSMS designers or use the Query editor and type raw T-SQL.I will use the later method.

1) Launch SQL Server Management Studio and connect to the local instance of SQL Server.

2) We will create a view that selects every product in the “Products” table with a unit price higher than the average unit price

If you want to see the full definition of how to create a view click here

USE Northwind
GO

IF OBJECT_ID ('[Products_Above_Average_Price]', 'V') IS NOT NULL
 DROP VIEW Products_Above_Average_Price ;
 GO

CREATE  VIEW [Products_Above_Average_Price] AS
SELECT ProductName,UnitPrice
FROM Products
WHERE UnitPrice>(SELECT AVG(UnitPrice) FROM Products)

SELECT * FROM Products_Above_Average_Price

This is a very easy to create a view. Copy the code and paste in the query window in the SSMS. Run the query and see the results.

Let’s create another view. In this view we are looking to find the customers with the most orders.


USE Northwind
go

IF OBJECT_ID ('[vwCustomersTopTwenty ]', 'V') IS NOT NULL
DROP VIEW vwCustomersTopTwenty  ;
GO

CREATE VIEW dbo.vwCustomersTopTwenty
AS
SELECT TOP (20) WITH TIES
c.CustomerID,
c.CompanyName,
COUNT(*) AS NumOrders
FROM dbo.Customers AS c
INNER JOIN dbo.Orders AS o
ON c.CustomerID=o.CustomerID
GROUP BY c.CustomerID, c.CompanyName
ORDER BY NumOrders DESC;
GO

SELECT * FROM dbo.vwCustomersTopTwenty

This is a very easy to create a view. Copy the code and paste in the query window in the SSMS. Run the query and see the results.

We can see the newly created views in the object explorer. Have a look at the picture below.



You can select a view and then  right-click on it. You can do various things. You can delete, rename an existing view. You can can create a new view.You can see the design of the existing view.You can select the top 1.000 rows.You can also script the view and see the T-SQL for creating,altering,dropping the view.

Now I would like to show you how to encrypt a view. Sometimes we need to encrypt the defintion of the view.

CREATE VIEW dbo.EncryptedView
WITH ENCRYPTION
AS
SELECT LastName, FirstName,city
FROM dbo.Employees
WHERE City='London';
GO
SELECT * FROM EncryptedView;

EXEC sp_helptext 'dbo.EncryptedView';

If we try to get the T-SQL statement that is the definition of the view, we will fail. That is what I try to do with the statement above but I fail.I use the sp_helptext system stored procedure to do that.

This is what I get when I execute the code above.

The text for object ‘dbo.EncryptedView’ is encrypted.

Run the T-SQL code above so you can verify yourself what I have just told you and to see the result of the view.

We can also alter the definition of a view. We can do that using the ALTER keyword.In the following example I am altering the previous view so I can add the “WITH CKECK OPTION”. With this option, we force all data modification statements executed against the view to follow the criteria set within select statement

alter VIEW dbo.EncryptedView
WITH ENCRYPTION
AS
SELECT LastName, FirstName,city
FROM dbo.Employees
WHERE City='London'
WITH CHECK OPTION;
GO

Now I will try to update the data through the view. I will try to update the data in the view by setting the values in the city column to  “Paris”

UPDATE dbo.EncryptedView
SET city = 'paris';

GO

When I run the statement above I get an error, as expected. The error I get follows.

“Msg 550, Level 16, State 1, Line 1
The attempted insert or update failed because the target view either specifies WITH CHECK OPTION or spans a view that specifies WITH CHECK OPTION and one or more rows resulting from the operation did not qualify under the CHECK OPTION constraint.
The statement has been terminated.”

Another option many people use when creating views is “SCHEMABINDING”.

SCHEMABINDING is an option that when applied to Views forces SQL Server to create dependencies between any objects referenced within the View and the View. We cannot  have their definitions altered whilst the schema-bound View exists. This ensures the integrity of the View.

CREATE VIEW vwSample

With SCHEMABINDING

As

SELECT

CustomerID,

CompanyName,

ContactName

FROM DBO.CUSTOMERS -- Two part name [ownername.objectname]

GO

SELECT * FROM VWSAMPLE

ALTER TABLE dbo.Customers

DROP COLUMN CompanyName

I create the view vwSample with the schemabinding on.Note that I use the schema-table name (dbo.Customers) when referencing the table.

When I try to alter the definition of the table by dropping a column that is referenced in the view (CompanyName) I get an error:

“Msg 5074, Level 16, State 1, Line 1
The object ‘vwSample’ is dependent on column ‘CompanyName’.”

You can also use the Template Explorer to create a view. Have a look at the picture below.

We can drop the view, by using T-SQL


DROP VIEW [dbo].[vwCustomersTopTwenty]

We can use another bit of T-SQL code to drop the database.We query the sys.views system view to see if the view exists and thenwe drop the view.


IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vwCustomersTopTwenty]'))
DROP VIEW [dbo].[vwCustomersTopTwenty]
GO

We can use views to update data. When data is updated through the view changes will affect only one table.

Columns that are based on expressions, set operators, aggregate functions cannot be updated.

I will create a view below.I get data from both the Categories and Product tables. Then I have 3 update statements. The first two update statements will succeed but the last one will fail.The first two updates will attempt to change the data for one table only. The third one will fail as it attempts to update data in two tables. This is the error messageI get when I try to update multiple base tables.

“View or function ‘dbo.vwProductInCategories’ is not updatable because the modification affects multiple base tables.”


CREATE VIEW dbo.vwProductInCategories
AS
SELECT  C.CategoryName ,
C.Description ,
P.ProductID ,
P.ProductName ,
p.QuantityPerUnit
FROM    dbo.Products AS P
INNER JOIN dbo.Categories AS C ON P.CategoryID = C.CategoryID
GO

SELECT * FROM dbo.vwProductInCategories

-- This will work
UPDATE dbo.vwProductInCategories
SET ProductName = 'Genen Good'
WHERE ProductID = 15

-- This will work
UPDATE dbo.vwProductInCategories
SET CategoryName = 'Meat'
WHERE ProductID = 19

-- This will not work
UPDATE dbo.vwProductInCategories
SET ProductName = 'chocolate', CategoryName = 'Milk'
WHERE ProductID = 21

Run the T-SQL above to see the results for yourself.

We can create indexed views as well.  We all know that Indexes enhance performance. We can have indexes on one or multiple columns and we can have faster searches and sorting through the indexes. When we index views we materialise the view. That means the view data is stored as well along with the definition of the view.Ιndexed views boost performance when we have complex aggregations or joins.We must use the SCHEMABINDING option.

Indexed view definitions must use the two part name (schemaname.tablename).Before I create a view I will write a T-SQL query that performs some aggregate calculation.

I also use the “SET STATISTICS IO ON” , which causes SQL Server to display information regarding the amount of disk activity generated by Transact-SQL statements.


SET STATISTICS IO ON

SELECT SUM(UnitPrice*Quantity*(1.00-Discount)) AS Sales,
OrderDate, ProductID
FROM dbo.[Order Details] OD JOIN dbo.Orders O
ON OD.OrderID = O.OrderID
WHERE  ProductID in (7, 89, 22, 34)
AND OrderDate >= '05/01/1998'
GROUP BY OrderDate, ProductID
ORDER BY Sales DESC;

Have a look at the picture below to see the logical and physical reads required.

Have a look also at the execution plan generated for the above query.

I create a view in the following example. I get the Product sales by date.

CREATE VIEW dbo.vwSalesByProductByDate
WITH SCHEMABINDING
AS
SELECT OrderDate, ProductID,
SUM(UnitPrice*Quantity*(1.00-Discount)) AS Sales,
COUNT_BIG(*) AS LineItemCount
FROM  dbo.[Order Details] OD JOIN dbo.Orders O
ON OD.OrderID=O.OrderID
GROUP BY  OrderDate, ProductID
GO

SELECT * FROM dbo.vwSalesByProductByDate
GO

EXEC sp_spaceused 'dbo.vwSalesByProductByDate'

--Create the index on the view.

CREATE  UNIQUE CLUSTERED INDEX idx_vwSalesByProductByDate
ON dbo.vwSalesByProductByDate (OrderDate, ProductID)
GO

EXEC sp_spaceused 'dbo.vwSalesByProductByDate'

Then I select the data from the view. Then I use the sp_spaceused stored procedure (Displays the number of rows, disk space reserved, and disk space used by a table, indexed view) to see the disk space reserved for the results.

Have a look at the picture below to see the results of that statement.So now we can prove thet data is materialised in the view.

Then I create a clustered index on that view, so I have an indexed view. Then I use the sp_spaceused stored procedure  to see the disk space reserved for the results.They are different.

Have a look at the picture below and see the results of that statement.

Now we need to run the query again (the one we created just before we created the view)


SET STATISTICS IO ON

SELECT SUM(UnitPrice*Quantity*(1.00-Discount)) AS Sales,
OrderDate, ProductID
FROM dbo.[Order Details] OD JOIN dbo.Orders O
ON OD.OrderID = O.OrderID
WHERE  ProductID in (7, 89, 22, 34)
AND OrderDate >= '05/01/1998'
GROUP BY OrderDate, ProductID
ORDER BY Sales DESC;

Have a look at the picture below to see the logical and physical reads required.

Have a look also at the execution plan generated for the above query.

Compare your findings with the relevant results befere we created the indexed view.  You can see now clearly that the query is using the clustered index and there is a  huge performance gain.

Do not use indexed views when the data in the underlying tables changes frequently.

In this final part I would like to talk about partitioned views.

A partitioned view is a database object that unions together tables with certain attributes.Tables with the same structure are combined to provide a unified view of a set of data, but that are divided on a “partitioning column.”

I will create a new database called “sample”.I will have 3 tables and I will populate them with data. The tables are divided/partitioned into the “city” column. The tables are CustomersParis,CustomersAthens,CustomersLondon


Create database [sample]
GO

USE [sample]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[CustomersParis](
[CustomerID] [char](5) NOT NULL,
[CompanyName] [varchar](40) NOT NULL,
[ContactName] [varchar](30) NULL,
[Address] [varchar](60) NULL,
[City] [varchar](15) NOT NULL,
[Region] [nvarchar](15) NULL,
[PostalCode] [nvarchar](10) NULL,
[Country] [nvarchar](15) NOT NULL,
[Phone] [nvarchar](24) NULL,
CONSTRAINT [PK_Cust1] PRIMARY KEY CLUSTERED
(
[City] ASC,
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[CustomersParis]  WITH CHECK ADD CHECK  (([City]='Paris'))
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[CustomersLondon](
[CustomerID] [char](5) NOT NULL,
[CompanyName] [varchar](40) NOT NULL,
[ContactName] [varchar](30) NULL,
[Address] [varchar](60) NULL,
[City] [varchar](15) NOT NULL,
[Region] [nvarchar](15) NULL,
[PostalCode] [nvarchar](10) NULL,
[Country] [nvarchar](15) NOT NULL,
[Phone] [nvarchar](24) NULL,
CONSTRAINT [PK_Cust2] PRIMARY KEY CLUSTERED
(
[City] ASC,
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[CustomersLondon]  WITH CHECK ADD CHECK  (([City]='London'))
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[CustomersAthens](
[CustomerID] [char](5) NOT NULL,
[CompanyName] [varchar](40) NOT NULL,
[ContactName] [varchar](30) NULL,
[Address] [varchar](60) NULL,
[City] [varchar](15) NOT NULL,
[Region] [nvarchar](15) NULL,
[PostalCode] [nvarchar](10) NULL,
[Country] [nvarchar](15) NOT NULL,
[Phone] [nvarchar](24) NULL,
CONSTRAINT [PK_Cust] PRIMARY KEY CLUSTERED
(
[City] ASC,
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[CustomersAthens]  WITH CHECK ADD CHECK  (([City]='Athens'))
GO

Download the script from here. In this .sql file you will find the script to create the database, the tables and populate the tables with data.

Run the script to create the tables and to populate them.

Now we will create the partiotioned view.We  union those tables back together using a partitioned view.


USE [sample]

CREATE VIEW PartitionedView AS
SELECT * FROM dbo.CustomersAthens
UNION ALL
SELECT * FROM dbo.CustomersLondon
UNION ALL SELECT * FROM
dbo.CustomersParis GO

SELECT * FROM PartitionedView
WHERE city ='athens'

Run the T-SQL code to create the view and to use the view to query the underlying tables.

Hope it helps!!!

Comments»

1. Dot Net Rules : Looking into Views in SQL Server - October 14, 2011

[…] found useful my style of posting. They feel they can follow the steps and master the technology.(Read more) Share Posted: Παρασκευή, 14 Οκτωβρίου 2011 2:23 πμ από το […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: