问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/18662247/creating-view-from-related-child-tables