I'm using MSSQL Server 2008 R2 and I'm trying to optimize my Views when I stumbled upon Indexed Views. Unfortunately most of my Views use a Left Outer Joins which is not supported with Indexed Views. After a bunch of research, I'm left confused the best way to go about this. The way I see it, I have the following options:
1) Convert the left joins to inner joins using the trick to simulate a left join with "OR (IsNull(a) AND IsNull(b))"
I found this solution in a couple places, but there was mention of a performance loss.
2) Convert the left joins to inner joins and replace the nullable column's nulls with empty guids (00000000-0000-0000-0000-000000000000) and add a single row in the right table with a matching guid.
This appears the most obvious performance-wise, but it seems a waste of space for every row that would otherwise be NULL.
3) Break my view up into two views. The first view being the majority of my logic that is Indexable. And the second view deriving from the first view and adding the left joins.
The idea here being, there might be a performance gain via the base view being indexed. And that even querying the derived view would gain at least some of the performance benefit.
4) Don't index my views
Would leaving the view the way it is be more performant than any of the above options?
5) The idea I didn't think of
I scripted my basic scenario as follows:
CREATE TABLE [dbo].[tbl_Thumbnails]( [ThumbnailId] [uniqueidentifier] NOT NULL, [Data] [image] NULL, [Width] [smallint] NOT NULL, [Height] [smallint] NOT NULL CONSTRAINT [PK_tbl_Thumbnails] PRIMARY KEY CLUSTERED ( [ThumbnailId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO CREATE TABLE [dbo].[tbl_Tags]( [TagId] [uniqueidentifier] NOT NULL, [ThumbnailId] [uniqueidentifier] NULL CONSTRAINT [PK_tbl_Tags] PRIMARY KEY CLUSTERED ( [TagId] 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 CREATE VIEW [dbo].[v_Tags] WITH SCHEMABINDING AS SELECT dbo.tbl_Tags.TagId, dbo.tbl_Tags.ThumbnailId FROM dbo.tbl_Tags LEFT OUTER JOIN dbo.tbl_Thumbnails ON dbo.tbl_Tags.ThumbnailId = dbo.tbl_Thumbnails.ThumbnailId GO INSERT INTO tbl_Tags VALUES ('16b23bb8-bf17-4784-b80a-220da1163584', NULL) INSERT INTO tbl_Tags VALUES ('e8b50f03-65a9-4d1e-b3b4-268f01645c4e', 'a45e357b-ca9c-449a-aa27-834614eb3f6e') INSERT INTO tbl_Thumbnails VALUES ('a45e357b-ca9c-449a-aa27-834614eb3f6e', NULL, 150, 150)
Now, doing the following query yields "Cannot create index on view "Test.dbo.v_Tags" because it uses a LEFT, RIGHT, or FULL OUTER join, and no OUTER joins are allowed in indexed views. Consider using an INNER join instead.":
CREATE UNIQUE CLUSTERED INDEX [TagId] ON [dbo].[v_Tags] ( [TagId] ASC ) GO
This is expected behavior, but what course of action would you recommend to gets the best performance from my scenario? The take home point here is best performance.
A legitimately needed left join was present in the view and any attempt to create a index raised a "not allowed construct" error. After hours of trial and error I managed to do it work. It's not at all an elegant approach, but it works for outer joins.
Indexes can help improve the performance of a nested-loop join in several ways. The biggest benefit often comes when you have a clustered index on the joining column in one of the tables. The presence of a clustered index on a join column frequently determines which table SQL Server chooses as the inner table.
Q: Why can't I use OUTER JOIN in an Indexed view? A: Rows can logically disappear from an Indexed view based on OUTER JOIN when you insert data into a base table. This makes the OUTER JOIN view to be increasingly updated, which is relatively difficult to implement.
Indexes can only be created on views which have the same owner as the referenced table or tables. This is also called an intact ownership-chain between the view and the table(s). Typically, when table and view reside within the same schema, the same schema-owner applies to all objects within the schema.
Why are you indexing your views? You mentioned "waste of space" in your solution 2, but did you know that when you index your view, you persist it on the DB?
In other words, you make a copy of the data the view would return on the DB and every time the data is updated on the source tables, some internal mechanism of SQL Server has to update it on this new data structure created because now SQL server reads from the view, not the tables anymore.
If you use Profiler + DTA or even DMVS you can come up with the correct indexes to be created on your tables that any view would benefit from
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With