Update: Please read the summary post of all the 11 Limitations of the view SQL SERVER – The Limitations of the Views – Eleven and more…
One of the most prominent limitations of the View it is that it does not support COUNT(*); however, it can support COUNT_BIG(*) operator. In the following case, you see that if View has COUNT (*) in it already, it cannot have a clustered index on it. On the other hand, a similar index would be created if we change the COUNT (*) to COUNT_BIG (*).For an easier understanding of this topic, let us see the example here.
USE tempdb
GO
IF EXISTS (SELECT * FROM sys.views WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[SampleView]'))
DROP VIEW [dbo].[SampleView]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[mySampleTable]') AND TYPE IN (N'U'))
DROP TABLE [dbo].[mySampleTable]
GO
-- Create SampleTable
CREATE TABLE mySampleTable (ID1 INT, ID2 INT, SomeData VARCHAR(100))
INSERT INTO mySampleTable (ID1,ID2,SomeData)
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY o1.name),
ROW_NUMBER() OVER (ORDER BY o2.name),
o2.name
FROM sys.all_objects o1
CROSS JOIN sys.all_objects o2
GO
-- Create View
CREATE VIEW SampleView
WITH SCHEMABINDING
AS
SELECT COUNT(*) TableCount, ID2
FROM dbo.mySampleTable
GROUP BY ID2
GO
-- Create Index on View
CREATE UNIQUE CLUSTERED INDEX [IX_ViewSample] ON [dbo].[SampleView]
(
ID2 ASC
)
GO
/* Above statement will thrown an error
Msg 10136, Level 16, State 1, Line 1
Cannot create index on view "tempdb.dbo.SampleView" because it uses the aggregate COUNT. Use COUNT_BIG instead.
*/
-- Aleter View to replace COUNT with BIG_COUNT
ALTER VIEW SampleView
WITH SCHEMABINDING
AS
SELECT COUNT_BIG(*) TableCount, ID2
FROM dbo.mySampleTable
GROUP BY ID2
GO
-- Now let us create Index again - this time successfully
CREATE UNIQUE CLUSTERED INDEX [IX_ViewSample] ON [dbo].[SampleView]
(
ID2 ASC
)
GO
Here is a screenshot of an error that occurred when Views used COUNT(*) and there was an attempt to create an Index on it.
If you are wondering about the reason behind allowing COUNT_BIG and COUNT, here is a quick explanation for this. Itzik Ben-Gan explained me the reason for this situation. He has also clarified the reasons in his book series, ‘Inside T-SQL‘. Here is a concise summary of the explanation. This response from Itzik is produced unaltered and with his permissions:
If the query is a grouped query, SQL Server needs to keep track of the count in each group in order to known whether a group needs to be modified or removed altogether upon DELETE/UPDATE of rows against the underlying tables. As for why the COUNT_BIG and not just COUNT, since SQL Server materializes the counts along with the rest of the view’s data, I guess this has to do with support for groups with more rows than the maximum four-byte integer.
BTW, unrelated to views but along similar lines, see what happens if you add to a clustered table more than the maximum four-byte integer number of rows with the same non-unique clustered index key. The uniqueifiers SQL Server uses internally to distinguish between rows with the same clustered index key is a four-byte integer. Once it overflows, you get error 666 and are not allowed to add more rows with the same clustered index key.
Now, with uniqueifiers for clustering keys I understand the choice to go for four bytes since there are great space savings and therefore read performance benefits as a result, and we are talking about an extreme case for this to happen . But with grouped queries, usually the number of groups is not too large, but groups themselves can be large. Imagine a situation where you try to add more rows to a table that has an indexed view and SQL Server rejects the insert because of a four-byte int overflow in the target group count.
I hope it is clear now. If you want to learn more about this, you can continue reading his book Inside T-SQL.
Let me know what you think of these limitations, as well as your opinions about the example.
Reference: Pinal Dave (https://blog.sqlauthority.com)
7 Comments. Leave new
Nice explanation Dave, and the reason for not having aggregates is very informative.
But, I don’t think we should say this is a limitation with Views.
Views and Indexed Views though with similar syntax are different data-structures in our SQL Server database.
There is a limitation with Indexed views that you can’t have columns with aggregate functions except COUNT_BIG(), which is a base requirement for Indexed Views.
Comments & suggestions are welcome.
~Manoj
Hi, Nice post about these limitations, however i still am not convinced of not using them. Recently i implemented indexed views, which were a great performance gain. However, i see these limitations in some parts.
Great series anyway!
And a little side note ;-) Why not use an object_id(‘object’) is null instead of the exists when you drop the existing tables?
Good post pinal, getting into behind the scenes working of views. Thanks for putting out the snippet from Itzik’s Book.
Hi Pinal
IF EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[mySampleTable]’) AND TYPE IN (N’U’))
what is the purpose of using ” N’ “,?
It is easier to use
if object_id(‘MySampleTable’) is not null
begin
drop table MySampleTable
end
in stead of doing an exists on sys.objects
The N’string’ tells SQL Server to treat the string as National char(2 byte Unicode) where the string ‘string’ would be treated as char (1 byte). This avoids an implicit cast to the correct type in string comparsions.
Hello Pinal bhai,
I read this post, but is that really a limitation of view?
In your earlier post, It is written that – “Index created on View not used often”
Or
as you used the word “OFTEN”, does it mean that while using COUNT(*) or BIG_COUNT(*), creating an index on view will use that index?