【问题标题】:Creating View from Related Child Tables从相关子表创建视图
【发布时间】:2013-09-10 19:37:48
【问题描述】:

我有以下通用表结构(请原谅在我设计的示例中以美国为中心的汽车制造商):

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)
)

我想在这些表的顶部创建一个视图,以便我可以检索所有福特和雪佛兰,并在视图中生成一个告诉我品牌的列。我的第一个刺是这样的:

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)

但这在我的嘴里留下了不好的味道,我担心性能。以不同的 CTE 值检索所有福特和雪佛兰,然后对它们执行联合,我会更好吗?我完全走错了吗?我还需要包含 Model 列(以及两个子表共有的其他一些列),这显然会使我的视图变成一系列巨大的案例陈述。处理这种情况的“正确”方法是什么?

编辑:我想我应该补充一点,这个架构已经存在,所以改变基础表是不可能的。

【问题讨论】:

  • 是否可以让来自FordFordId 等于1 和来自ChevyChevyId 等于1? :)
  • 哈,不,谢天谢地,这是不可能的。每个 Id 值都是唯一的。
  • 你如何预防呢? SQL Server 优化器是否知道这是不可能的?如果没有,那么如果您将在联接中使用您的视图,那么您的联合可能会很慢
  • 这是一个很好的问题。它的工作方式是,在创建 Ford 时,首先将一个条目添加到 Car 表中,然后使用该添加中的 SCOPE_IDENTITY() 来添加到 Ford 或 Chevy 表中。如您所知,该系统已接近 10 年,并且有许多值得怀疑的方面。关于减速的评论,因为这确实是不可发现的,非常有用。

标签: sql sql-server sql-server-2008 common-table-expression sql-view


【解决方案1】:

首先,让我们来看看两种方法各自的优缺点:

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;

在联接中使用第一个会更好,尤其是在您不使用所有列的情况下。例如,假设您在使用 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;

现在,如果你想做简单的选择:

select Name from vw_people1;

select Name from vw_people2;

第一个是从people 中简单选择(根本不会查询vw_Car1)。第二个会更复杂 - FordChevy 都将被查询。 您可能认为第一种方法更好,但让我们尝试另一个查询:

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

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

第二个会更快,尤其是如果您在 Model 列上有索引。

=> sql fiddle demo - 查看这些查询的查询计划。

【讨论】:

  • 感谢您的详细解答。因此,如果我通常要根据视图中的某些 where 标准进行检索,我最好采用联合策略?
  • @ledbutter 好吧,这取决于您的查询,我认为没有灵丹妙药,尤其是在您无法更改架构的情况下。在您的情况下,您可以将模型移动到 Cars 表中并使用连接策略(如果您可以更改架构)。如果您采用联合策略,请注意使用您的表连接视图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-05
  • 2015-08-27
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多