簡體   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