回退查看(性能/优化问题)

Rob*_*III 5 performance sql-server optimization

我有一张表,上面有东西的关税;在这种情况下,关税表并不重要,“关税值”很重要。在此演示 SQL Fiddle 中tariff_plantariffplans表的 FK (未包含在示例中)。每个“东西”的关税是tariff_type(出于演示目的简化为一个简单的字符)。

例如,我有一个默认的关税计划(key = default);这是适用于每个客户的资费,除非为该客户定义了另一个值tariff_type。为客户分配了关税计划(plan_x在我的示例中为键 = )。

如果有定义项目的关税abc并且d在默认的计划。在 plan_x 中,我为a和定义了“覆盖”值c

所以,我做的是我选择default计划(化名p低于p rimary)和左加入“覆盖”计划(plan_x)把它(别名s下面小号econdary):

select *
from tariff_values as p
left outer join tariff_values s 
    on (p.tariff_type = s.tariff_type) and (s.tariff_plan = 'plan_x')
where (p.tariff_plan = 'default')
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,这导致:

id   tariff_plan tariff_type tariff_foo tariff_bar id   tariff_plan tariff_type tariff_foo tariff_bar
---- ----------- ----------- ---------- ---------- ---- ----------- ----------- ---------- ----------
1    default     a           0.10       0.20       5    plan_x      a           0.09       0.19
2    default     b           0.55       0.66       NULL NULL        NULL        NULL       NULL
3    default     c           1.99       2.99       6    plan_x      c           0.99       1.99
4    default     d           9.99       6.33       NULL NULL        NULL        NULL       NULL
Run Code Online (Sandbox Code Playgroud)

因为我想把它抽象出来,所以我想把它放到一个表值函数中,这样我就可以创建一个“动态视图”:

select * from dbo.get_tariffplan_for('plan_x', default);
Run Code Online (Sandbox Code Playgroud)

这应该会产生一个类似于表的“虚拟表”(或“动态视图”)tariff_values,因此:没有两个关税 foo 和两个关税栏,让应用程序决定使用哪个。因此,我求助于ISNULLCase when...构造来“覆盖”默认值:

select p.tariff_type,
    ISNULL(s.tariff_foo, p.tariff_foo) as tariff_foo, 
    ISNULL(s.tariff_bar, p.tariff_bar) as tariff_bar,
    ISNULL(s.tariff_plan, p.tariff_plan) as tariff_plan,
    CASE WHEN s.id IS NULL THEN 1 ELSE 0 END as isfallback
from tariff_values as p
left outer join tariff_values s
    on (p.tariff_type = s.tariff_type) and (s.tariff_plan = 'plan_x')
where (p.tariff_plan = 'default')
Run Code Online (Sandbox Code Playgroud)

这导致:

tariff_type tariff_foo tariff_bar tariff_plan isfallback
----------- ---------- ---------- ----------- -----------
a           0.09       0.19       plan_x      0
b           0.55       0.66       default     1
c           0.99       1.99       plan_x      0
d           9.99       6.33       default     1
Run Code Online (Sandbox Code Playgroud)

我现在需要做的就是将此查询填充到 TVF 中:

CREATE FUNCTION get_tariffplan_for
(   
    @customerplan as varchar(50),
    @defaultplan as varchar(50) = 'default'
)
RETURNS TABLE 
AS RETURN 
(
    select p.tariff_type,
        ISNULL(s.tariff_foo, p.tariff_foo) as tariff_foo, 
        ISNULL(s.tariff_bar, p.tariff_bar) as tariff_bar,
        ISNULL(s.tariff_plan, p.tariff_plan) as tariff_plan,
        CASE WHEN s.id IS NULL THEN 1 ELSE 0 END as isfallback
    from tariff_values as p
    left outer join tariff_values s
        on (p.tariff_type = s.tariff_type) and (s.tariff_plan = @customerplan)
    where (p.tariff_plan = @defaultplan)
);
Run Code Online (Sandbox Code Playgroud)

我们终于得到它了。我们可以按预期调用我们的函数(“动态视图”)(也可以在选择/连接等中使用它)

select * from dbo.get_tariffplan_for('plan_x', default);

--or:

select *
from foo
inner join dbo.get_tariffplan_for('plan_x', default) bar
    on foo.tariff_type = bar.tariff_type
Run Code Online (Sandbox Code Playgroud)

现在我的第一个问题是:

我有一种感觉,所有这些ISNULL(或COALESCE)和/或CASE WHEN ...特技似乎不必要地使事情复杂化,并且有些东西告诉我这可以更有效地完成。但是,我想不出更好和/或更有效的替代方案。

所以我希望这里有人对如何改进这一点有一些想法。

我的第二个问题是:

如果我有一个专门卖给某个客户的产品(q例如,tariff_type )怎么办?关税不会在默认关税计划中,所以我必须在上面添加另一个(使用工会)才能在结果集中获得该客户的所有独家关税。这将导致这样的查询: select

select p.tariff_type,
    ISNULL(s.tariff_foo, p.tariff_foo) as tariff_foo, 
    ISNULL(s.tariff_bar, p.tariff_bar) as tariff_bar,
    ISNULL(s.tariff_plan, p.tariff_plan) as tariff_plan,
    CASE WHEN s.id IS NULL THEN 1 ELSE 0 END as isfallback,
    0 as isexclusive
from tariff_values as p
left outer join tariff_values s
    on (p.tariff_type = s.tariff_type) and (s.tariff_plan = @customerplan)
where (p.tariff_plan = @defaultplan)

UNION

--Exclusive values
select p.tariff_type,
    p.tariff_foo, 
    p.tariff_bar,
    p.tariff_plan,
    0 as isfallback,
    1 as isexclusive
from tariff_values p
left outer join tariff_values s
    on (p.tariff_type = s.tariff_type) AND (s.tariff_plan = 'default')
where p.tariff_plan = 'plan_x'
    and s.id is null
Run Code Online (Sandbox Code Playgroud)

(在此 SQL 小提琴中演示)

在上面的示例中,我使用另一个left joinwith s.id is null(但可以以其他方式重写(使用count, exists, not in, having...无论如何))来检索客户专属关税。但也许有更好的方法来代替工会?

r.m*_*r.m 1

有几个问题,因为我的代表太低,无法通过评论发布

  1. 您想对输出做什么?您可以在多大程度上更改数据模型?

    • 如果表布局和输出都必须与您的示例中完全相同,那么我看不到任何重大改进。外连接+ isnull 还不错。

      • 为了提高性能,您可以:

        • 确保您要加入的键上有索引
        • 开始使用计划的合成键(应该比 varchar 更好)
      • 如果更改 DDL 或数据可以,您还可以:

        • 创建一个专用表来包含默认值(没有关税计划字段)。我们将这个表称为“default_tariff”表,将另一个表称为“special_tariff_types”
  2. 关于最后一个要求,我猜这意味着可能有不存在默认情况的关税计划?完整的外部连接也应该有效。

  3. 我猜你正在使用 TVF 来避免将其置于正常视图中的麻烦?我有一种感觉,应该可以使用普通视图而不会造成性能损失,尽管查询会变得更加复杂。

这是一个建议:

CREATE FUNCTION get_tariffplan_for
(   
    @customerplan as varchar(50)
)
RETURNS TABLE 
AS RETURN 
(
    select p.tariff_type,
        ISNULL(s.tariff_foo, p.tariff_foo) as tariff_foo, 
        ISNULL(s.tariff_bar, p.tariff_bar) as tariff_bar,
        ISNULL(s.tariff_plan, p.tariff_plan) as tariff_plan,
        CASE WHEN s.id IS NULL THEN 1 ELSE 0 END as isfallback
    from default_tariff_values as p
    full outer join specific_tariff_values s
        on (p.tariff_type = s.tariff_type) and (s.tariff_plan = @customerplan)
);
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个提案(不改变数据模型)

CREATE FUNCTION get_tariffplan_for
(   
    @customerplan as varchar(50)
)
RETURNS TABLE 
AS RETURN 
(
    select p.tariff_type,
        ISNULL(s.tariff_foo, p.tariff_foo) as tariff_foo, 
        ISNULL(s.tariff_bar, p.tariff_bar) as tariff_bar,
        ISNULL(s.tariff_plan, p.tariff_plan) as tariff_plan,
        CASE WHEN s.id IS NULL THEN 1 ELSE 0 END as isfallback,
        CASE WHEN p.id IS NULL then 1 else 0 END as isexclusive
    from 
    (select tariff_type,tariff_foo,tariff_bar,tariff_plan 
    from tariff_values where tariff_plan = 'default') 
    as p
    full outer join 
        (select tariff_type,tariff_foo,tariff_bar,tariff_plan 
        from tariff_values where tariff_plan = @customerplan) 
        as s
        on (p.tariff_type = s.tariff_type)
);
Run Code Online (Sandbox Code Playgroud)