Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What to replace left join in a view so i can have an indexed view?

Tags:

I have normalized tables in a database and to denormalize it, I created a view out of two tables. When I tried to create a clustered index on the view, it wouldn't let me, as the view was created with a left outer join. I used a left join because I want the null values to show up in the resulting view, much like how it was suggested in this earlier post.

Question on join where one column one side is null

The table structure and relationship is very much similar to what was described in the above link.

I seemed to hit a wall here as I couldn't convert my left join into an inner join, as that would exclude all records with null values on any of the joined columns. My questions are:

  1. Why is indexing not allowed on outer or self joins?
  2. Are there any performance hits on this kind of un-indexed view?
  3. Anyone knows any workaround to this problem?

I've just finished a SQL Server course yesterday so don't know how to proceed. Would appreciate any comments. Cheers.

like image 670
Noble_Bright_Life Avatar asked Jun 25 '11 09:06

Noble_Bright_Life


People also ask

Can we use left join in indexed view?

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.

How do you create an indexed view?

To create an indexed view, you use the following steps: First, create a view that uses the WITH SCHEMABINDING option which binds the view to the schema of the underlying tables. Second, create a unique clustered index on the view. This materializes the view.

Can you have an index on a view?

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 can't I use outer join in an indexed view?

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.


2 Answers

Here is an alternative. You want a materialized view of A not containing B. That isn't directly available... so instead, materialize two views. One of all A's and one of only A's with B's. Then, get only A's not having B's by taking A except B. This can be done efficiently:

Create two materialized views (mA and mAB) (edit: mA could just be the base table). mA lacks the join between A and B (thus containing all A's period [and therefore containing those records WITHOUT matches in B]). mAB joins between A and B (thus containing only A's with B's [and therefore excluding those records WITHOUT matches in B]).

To get all A's without matches in B, mask out those that match:

with ids as (   select matchId from mA with (index (pk_matchid), noexpand)   except   select matchId from mAB with (index (pk_matchid), noexpand) ) select * from mA a join ids b on a.matchId = b.matchId; 

This should yield a left anti semi join against both your clustered indexes to get the ids and a clustered index seek to get the data out of mA you are looking for.

Essentially what you are running into is the basic rule that SQL is much better at dealing with data that IS there than data that ISN'T. By materializing two sources, you gain some compelling set based options. You have to weigh the cost of these views against those gains yourself.

like image 199
cocogorilla Avatar answered Nov 07 '22 17:11

cocogorilla


There is a "workaround" here that involves check for NULL in the join and having a NULL representation value in the table

NULL value

INSERT INTO Father (Father_id, Father_name) values(-255,'No father') 

The join

JOIN [dbo].[son] s on isnull(s.father_id, -255) = f.father_id 
like image 39
Magnus Avatar answered Nov 07 '22 18:11

Magnus