Question

I have the following general table structure (forgive the United States-centric carmakers in my contrived example):

CREATE TABLE Car (
    [Id] int PRIMARY KEY
)

CREATE TABLE Ford (
    [FordId] int PRIMARY KEY, --also a foreign key on Car
    [Model]  nvarchar(max)
)

CREATE TABLE Chevy (
    [ChevyId] int PRIMARY KEY, --also a foreign key on Car
    [Model]  nvarchar(max)
)

I am wanting to create a view on top of these tables so that I can retrieve all Fords and Chevys and just have a generated column in the view that tells me the make. My first stab was this:

SELECT 
    c.CarId,
    case when f.FordId is not null then 'Ford' else 'Chevy' end 
FROM Car as c
LEFT JOIN Ford as f on c.Id = f.FordId
LEFT JOIN Chevy as ch on c.Id = ch.ChevyId
WHERE (f.FordId is not null or ch.ChevyId is not null)

But that leaves a bad taste in my mouth and I am concerned about performance. Would I be better off retrieving all the Fords and Chevys in separate CTE values and just performing a union on them? Am I on the wrong track entirely? I will also be needing to include the Model column (as well as a few other columns common to the two child tables), which would obviously make my view turn into a giant series of case statements. What is the "proper" way to handle such a situation?

EDIT: Thought I should add that this schema already exists so changing the underlying tables is not possible.

Was it helpful?

Solution

First of all, let's try to see pros and cons of each of 2 approaches:

create view vw_Car1
as
  SELECT 
      c.Id,
      case when f.FordId is not null then 'Ford' else 'Chevy' end as Maker,
      coalesce(f.Model, ch.Model) as Model
  FROM Car as c
  LEFT JOIN Ford as f on c.Id = f.FordId
  LEFT JOIN Chevy as ch on c.Id = ch.ChevyId
  WHERE (f.FordId is not null or ch.ChevyId is not null);

create view vw_Car2
as
  select FordId as id, 'Ford' as Maker, Model from Ford
  union all
  select ChevyId as id, 'Chevy' as Maker, Model from Chevy;

The first one is better when you use it in joins, especially if you'll not using all of your columns. For example, let's say you have a view when you're using your vw_Car:

create table people (name nvarchar(128), Carid int);

insert into people
select 'John', 1 union all
select 'Paul', 2;

create view vw_people1
as
select
    p.Name, c.Maker, c.Model
from people as p
   left outer join vw_Car1 as c on c.ID = p.CarID;

create view vw_people2
as
select
    p.Name, c.Maker, c.Model
from people as p
   left outer join vw_Car2 as c on c.ID = p.CarID;

Now, if you want to do simple select:

select Name from vw_people1;

select Name from vw_people2;

First one would be simple select from people (vw_Car1 will not be queried at all). Second one will be more complex - Ford and Chevy will be both queried. You could think that first approach is better, but let's try another query:

select *
from vw_people1
where Maker = 'Ford' and Model = 'Fiesta';

select *
from vw_people2
where Maker = 'Ford' and Model = 'Fiesta';

Here second one will be faster, especially if you have index on Model column.

=> sql fiddle demo - see query plans of these queries.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top