简体   繁体   English

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

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

Ok, first of all thanks in advance if you read through this whole thing as it may be quite painful on several levels. 好的,首先要提前感谢你,如果你仔细阅读了这一切,因为它可能会在几个层面上非常痛苦。

  1. It's a long post 这是一篇很长的帖子
  2. It's gross 这很糟糕
  3. It's going to probably make your brain hurt 它可能会让你的大脑受伤

But on the plus side, after reading through this whole thing I have a feeling the answer is very obvious and simple, so you have that going for you. 但从好的方面来说,在阅读完整篇文章之后,我感觉答案是非常明显和简单的,所以你有这个适合你。

So I'll tell you the problem in a nutshell, and then in more detail: 所以我会简单地告诉你这个问题,然后更详细地说:

Nutshell 果壳

  • I have a query in SQL Server 2008r2 that is taking a very long time to complete. 我在SQL Server 2008r2中有一个查询需要很长时间才能完成。
  • I have several tables that contain information about a child and its parent. 我有几个表包含有关孩子及其父母的信息。
  • A child in one table can have a parent in another table which then could have a parent in another table (there are only 3 tables). 一个表中的子项可以在另一个表中具有父项,然后可以在另一个表中具有父项(仅有3个表)。
  • I want to be able to take a child's name as a string and figure out it's heirarchy of ancestors and return that as a period delimited string. 我希望能够将一个孩子的名字作为一个字符串,并找出它是祖先的heirarchy并将其作为一个句点分隔的字符串返回。 So Grandpappy.Grandpa.Dad.Me . 所以Grandpappy.Grandpa.Dad.Me
  • I have this all working, it's just taking forever so I'm doing something stupid, or poorly performant, or most likely both. 我有这一切都在工作,它只是永远消失所以我做了一些愚蠢的事情,或者表现不佳,或者很可能两者兼而有之。
  • I have NO control over the tables, they are what they are and I can't do anything to them. 我无法控制桌子,他们就是这样,我对他们无能为力。 I created a view and a function (which you will see below) and that is all I can control. 我创建了一个视图和一个函数(你将在下面看到),这就是我可以控制的。
  • The table names and values below are obviously fictitious. 下面的表名和值显然是虚构的。

Detailed description 详细说明

Here are the tables that indicate children and parents. 以下是表明儿童和父母的表格。 In this example we will be dealing with Fruits, Vegetables, and Planets. 在这个例子中,我们将处理水果,蔬菜和行星。

  • A Planet has no parents. 一个星球没有父母。
  • A Fruit has a Parent who is a Planet, or a Fruit. 水果的父母是行星或水果。
  • A Vegetable has a Parent who is a Fruit, or a Planet, or a Vegetable. 蔬菜的父母是水果,行星或蔬菜。

Let's take a look at them... 我们来看看他们......

Table 1 = Planets (I have no parents) 表1 = Planets (我没有父母)

ID, Name
1, Earth
2, Saturn

Table 2 = Fruits (my parent is either a planet or a fruit) 表2 = Fruits (我的父母要么是行星要么是水果)

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

Table 3 = Vegetables (my parent is planet or a fruit or a vegetable) 表3 = Vegetables (我的父母是行星或水果或蔬菜)

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

Table 4 = BigTable (this will be the one the main slow query is using. It has a column that contains just a child's name and it could be a planet or a fruit or a vegetable) 表4 = BigTable (这将是主要的慢查询使用的那个。它有一个只包含一个孩子名字的列,它可能是一个行星或水果或蔬菜)

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

We have our tables and we have our data, what do I want to do now? 我们有表格,我们有数据,现在我想做什么?

I want to create a query that looks at all of the OneOfTheThree values in the BigTable and find out what their lineage is (who there dads, grand parents etc are) and return that to the caller. 我想创建一个查询,查看BigTable中的所有OneOfTheThree值,并找出他们的血统(爸爸,祖父等等)是什么,并将其返回给调用者。

So my thought was to do this: 所以我的想法是这样做:

  • Create a view that pulls the three tables (Planet, Fruit, Vegetable) into one single view that shows Name and Parent. 创建一个视图,将三个表(Planet,Fruit,Vegetable)拉到一个显示Name和Parent的视图中。
  • Create a function that takes in a Name. 创建一个接受名称的函数。 It then uses that view to find out who the Parent is for that Name. 然后,它使用该视图来找出父对象的名称。 It then looks to see who the Parent is for that Parent, and on and on until the Parent is null and it stops (because that's the top of the ancestry chain... we made it all the way to Planet, who has no parents). 然后它会查看父母对于那个父母是谁,并且一直持续到父母为空并且它停止(因为那是祖先链的顶端...我们一直到了没有父母的星球)。
  • Create a query to query BigTable and then use the above function on BigTable's OneOfTheThree column to get the ancestry of the name in OneOfTheThree. 创建一个查询来查询BigTable,然后在BigTable的OneOfTheThree列上使用上面的函数来获取OneOfTheThree中名称的祖先。

So I did it as follows: 所以我这样做了:

My view 我的观点

View = vwEverybodyAndTheirParents 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

Ok, that gives me everything and it's parents. 好吧,这给了我一切,它是父母。 Now for the function to crawl that view and give me the period delimited string of the full ancestry: 现在,对于抓取该视图的函数,并为我提供完整祖先的句点分隔字符串:

My function 我的功能

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 

This function works fine (though could be poorly coded and poorly performing?), so with all the above examples if I were to call it like so: 这个函数工作正常(虽然可能编码不好,性能不佳?),所以上面的所有例子如果我这样调用它:

dbo.fnGetMyParent('Potato')

I get back the concatenated string of: 我找回了串联的字符串:

Earth.Kiwi.Potato

The problem 问题

Ok, so now to finally get to the problem... the big query that takes forever: 好的,所以现在最终解决问题......永远需要的大问题:

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

I can see why it could take so long as for each value it executes the function which needs to then crawl a view. 我可以看到为什么它可能需要花费很长时间才能执行需要随后抓取视图的函数的每个值。 So... 所以...

My questions to you 我向你提问

  • How can I speed this up? 我怎样才能加快速度呢?
  • Do I need to put an index on the view? 我是否需要在视图上放置索引?
  • Is my approach off, and should I do this differently? 是我的方法,我应该这样做吗?
  • If so, what do you recommend? 如果是这样,你推荐什么?

A BIG THANK YOU if you made it this far! 非常感谢你,如果你做到这一点!

First of all when using sql you should avoid using loops as much as you can (unless the situation asks for it) 首先,当你使用sql时,你应该尽量避免使用循环(除非情况要求)

Second, there is no need of the view, or of the function as your query should be easily written in one go. 其次,不需要视图或函数,因为您的查询应该很容易一次写入。

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

The last join you can remove if the table is consistent with the others, as it does not bring new information (the planet name is already there). 如果表与其他表一致,则可以删除最后一个连接,因为它不会带来新信息(行星名称已经存在)。

The improvements that you can bring here are with indexes on the tables, if you are able to do that. 如果你能够做到这一点,你可以带来的改进是表上的索引。

Ok, with the new information, the easiest way I can think of is the following: 好的,有了这些新信息,我能想到的最简单的方法如下:

;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

With this approach though .. I have no idea on the performance it will yield. 但是通过这种方法......我不知道它会产生什么样的性能。 So it's up to you to complete the query and test. 因此,您需要完成查询和测试。

Hope it helps. 希望能帮助到你。

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

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