繁体   English   中英

使用函数和视图的TSQL查询非常慢

[英]TSQL query that uses function and view is very slow

好的,首先要提前感谢你,如果你仔细阅读了这一切,因为它可能会在几个层面上非常痛苦。

  1. 这是一篇很长的帖子
  2. 这很糟糕
  3. 它可能会让你的大脑受伤

但从好的方面来说,在阅读完整篇文章之后,我感觉答案是非常明显和简单的,所以你有这个适合你。

所以我会简单地告诉你这个问题,然后更详细地说:

果壳

  • 我在SQL Server 2008r2中有一个查询需要很长时间才能完成。
  • 我有几个表包含有关孩子及其父母的信息。
  • 一个表中的子项可以在另一个表中具有父项,然后可以在另一个表中具有父项(仅有3个表)。
  • 我希望能够将一个孩子的名字作为一个字符串,并找出它是祖先的heirarchy并将其作为一个句点分隔的字符串返回。 所以Grandpappy.Grandpa.Dad.Me
  • 我有这一切都在工作,它只是永远消失所以我做了一些愚蠢的事情,或者表现不佳,或者很可能两者兼而有之。
  • 我无法控制桌子,他们就是这样,我对他们无能为力。 我创建了一个视图和一个函数(你将在下面看到),这就是我可以控制的。
  • 下面的表名和值显然是虚构的。

详细说明

以下是表明儿童和父母的表格。 在这个例子中,我们将处理水果,蔬菜和行星。

  • 一个星球没有父母。
  • 水果的父母是行星或水果。
  • 蔬菜的父母是水果,行星或蔬菜。

我们来看看他们......

表1 = Planets (我没有父母)

ID, Name
1, Earth
2, Saturn

表2 = Fruits (我的父母要么是行星要么是水果)

ID, Name, PlanetName, FruitName
1, Kiwi, Earth, null
2, Strawberry, Saturn, null
3, Banana, null, Strawberry

表3 = Vegetables (我的父母是行星或水果或蔬菜)

ID, Name, FruitName, PlanetName, VegetableName
1, Potato, Kiwi, null, null
2, Squash, null, Earth, null
3, Pumpkin, null, null, Potato

表4 = BigTable (这将是主要的慢查询使用的那个。它有一个只包含一个孩子名字的列,它可能是一个行星或水果或蔬菜)

ID, Name, OneOfTheThree
1, John, Earth
2, Steve, Kiwi
3, Joe, Saturn
4, Jane, Potato

我们有表格,我们有数据,现在我想做什么?

我想创建一个查询,查看BigTable中的所有OneOfTheThree值,并找出他们的血统(爸爸,祖父等等)是什么,并将其返回给调用者。

所以我的想法是这样做:

  • 创建一个视图,将三个表(Planet,Fruit,Vegetable)拉到一个显示Name和Parent的视图中。
  • 创建一个接受名称的函数。 然后,它使用该视图来找出父对象的名称。 然后它会查看父母对于那个父母是谁,并且一直持续到父母为空并且它停止(因为那是祖先链的顶端...我们一直到了没有父母的星球)。
  • 创建一个查询来查询BigTable,然后在BigTable的OneOfTheThree列上使用上面的函数来获取OneOfTheThree中名称的祖先。

所以我这样做了:

我的观点

View = vwEverybodyAndTheirParents

-- Planets
SELECT Name, null AS Parent
FROM Planets
UNION
-- Fruits
SELECT Name, PlanetName AS Parent
FROM Fruits
UNION
-- Vegetables
SELECT Name, CASE WHEN FruitName IS NOT NULL THEN FruitName WHEN PlanetName IS NOT NULL THEN Planet ELSE NULL END AS Parent
FROM Vegetables

好吧,这给了我一切,它是父母。 现在,对于抓取该视图的函数,并为我提供完整祖先的句点分隔字符串:

我的功能

CREATE FUNCTION dbo.fnGetMyParent(@NameToGetParentsFor varchar(255))
RETURNS varchar(255)
AS
    DECLARE @InternalName varchar(255)
    DECLARE @ParentName varchar(255)
    DECLARE @ConcatenatedParentStringToReturn varchar(max)

    SELECT @ParentName = Parent
            ,@ConcatenatedParentStringToReturn = Name
    FROM vwEverybody
    WHERE Name = @NameToGetParentsFor

    WHILE @ParentName IS NOT NULL
    BEGIN
        SELECT @InternalName = Name, 
                @ParentName = Parent
        FROM vwEverybody
        WHERE Name = @ParentName

        SET @ConcatenatedParentStringToReturn = RTRIM(InternalName) + "." + RTRIM(@ConcatenatedParentStringToReturn)
    END

    RETURN @ConcatenatedParentStringToReturn
END 

这个函数工作正常(虽然可能编码不好,性能不佳?),所以上面的所有例子如果我这样调用它:

dbo.fnGetMyParent('Potato')

我找回了串联的字符串:

Earth.Kiwi.Potato

问题

好的,所以现在最终解决问题......永远需要的大问题:

SELECT Name,
       OneOfTheThree,
       fnGetMyParent(OneOfTheThree) as HeirarchyOfParents
FROM BigTable

我可以看到为什么它可能需要花费很长时间才能执行需要随后抓取视图的函数的每个值。 所以...

我向你提问

  • 我怎样才能加快速度呢?
  • 我是否需要在视图上放置索引?
  • 是我的方法,我应该这样做吗?
  • 如果是这样,你推荐什么?

非常感谢你,如果你做到这一点!

首先,当你使用sql时,你应该尽量避免使用循环(除非情况要求)

其次,不需要视图或函数,因为您的查询应该很容易一次写入。

select 
  bt.Name
  ,bt.OneOfTheThree 
  ,p.Name+'.'+isnull(f.Name,'')+'.'+isnull(v.Name,'')+'.'+bt.Name as HeirarchyOfParents
from BigTable bt
left join Vegetables v
  on bt.OneOfTheThree = v.name
left join Fruits f
  on coalesce(v.FruitName,bt.OneOfTheThree) = f.Name
left join Planets p
  on coalesce(f.PlanetName,v.PlanetName,bt.OneOfTheThree) = p.Name

如果表与其他表一致,则可以删除最后一个连接,因为它不会带来新信息(行星名称已经存在)。

如果你能够做到这一点,你可以带来的改进是表上的索引。

好的,有了这些新信息,我能想到的最简单的方法如下:

;with ftemp as (
  select 
    name as path
    ,PlanetName
    ,name as root
    ,name as name
    ,FruitName as parent
    ,0 as cnt
  from fruits
  union all
  select 
    fruits.name + '.' + ftemp.path
    ,ftemp.PlanetName
    ,root
    ,fruits.name
    ,cnt+1
  from fruits
  join ftemp
    on fruits.name= ftemp.parent
)
,fg as (
  select 
    name
    ,max(cnt) as cnt
  from ftemp
  group by name
)
,f as (
  select 
    ftemp.*
  from ftemp
  join fg
    on  ftemp.cnt = fg.cnt
    and ftemp.name = fg.name
)
,vtemp (same ideea)
,vg (same ideea)
,v (same ideea)
select 
  bt.Name
  ,bt.OneOfTheThree 
  ,p.Name+'.'+isnull(f.Path+'.','')+isnull(v.Path+'.','')+bt.Name as HeirarchyOfParents
from BigTable bt
left join v
  on bt.OneOfTheThree = v.name
left join f
  on coalesce(v.FruitName,bt.OneOfTheThree) = f.Name
left join Planets p
  on coalesce(f.PlanetName,v.PlanetName,bt.OneOfTheThree) = p.Name

但是通过这种方法......我不知道它会产生什么样的性能。 因此,您需要完成查询和测试。

希望能帮助到你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM