UDF 与子查询性能问题

Posted

技术标签:

【中文标题】UDF 与子查询性能问题【英文标题】:UDF vs Subquery Performance issue 【发布时间】:2013-06-10 11:07:16 【问题描述】:

我已尝试找到每个客户 12 个月的每月总销售额和总回送值。

如果使用其他 2 个子查询,则查询变为每行使用 26 个子查询,并且 SQL 语句非常大。

然后我创建了两个子选择以进行销售和发回总计...

结果查询变短,但执行时间大于 20-30 次。

    这正常吗 如果子查询返回 NULL,我如何返回 0..

编辑:

(Select sum(chh2.cha_meblag)
 from dbo.CARI_HESAP_HAREKETLERI chh2
 where chh2.cha_normal_Iade=1 and
       chh2.cha_kod=ch.cari_kod and
       (chh2.cha_tarihi>='20130101' and chh2.cha_tarihi<='20130131')
) as Ocai

【问题讨论】:

像这样的问题是不可能在不看代码的情况下智能回答的。不过,对于您的第一个问题,使用 UDF 的查询会比没有 UDF 的等效查询慢,这并不让我感到惊讶。 我想发布 1 的代码。但是当我用谷歌搜索它时,我看到了一些这样的问题,只是想得到一个简单的答案。对于第二个。 (选择 sum(chh2.cha_meblag) from dbo.CARI_HESAP_HAREKETLERI chh2 where chh2.cha_normal_Iade=1 and chh2.cha_kod=ch.cari_kod and (chh2.cha_tarihi>='20130101' and chh2.cha_tarihi 【参考方案1】:

根据您的子查询,您似乎可以在没有相关子查询的情况下解决您的问题,并且只能使用联接。但是,只有一部分查询可以使用,所以很难确定。这就是为什么在发布问题时最好发布尽可能多的相关代码。尽管如此,据我所知,你有这样的事情:

SELECT  (   SELECT  SUM(chh2.cha_meblag)
            FROM    dbo.CARI_HESAP_HAREKETLERI chh2
            WHERE   chh2.cha_normal_Iade = 1 
            AND     chh2.cha_kod = ch.cari_kod
            AND     chh2.cha_tarihi >= '20130101' 
            AND     chh2.cha_tarihi <= '20130131'
        ) AS Ocai,
        (   SELECT  SUM(chh2.cha_meblag)
            FROM    dbo.CARI_HESAP_HAREKETLERI chh2
            WHERE   chh2.cha_normal_Iade = 1 
            AND     chh2.cha_kod = ch.cari_kod
            AND     chh2.cha_tarihi >= '20130201' 
            AND     chh2.cha_tarihi <= '20130228'
        ) AS Ocai,
        ... etc for 12 months
FROM    SomeTable ch

你可以把它变成:

SELECT  ch.cari_kod,
        [20130101] = ISNULL([20130101], 0),
        [20130201] = ISNULL([20130201], 0),
        [20130301] = ISNULL([20130301], 0),
        [20130401] = ISNULL([20130401], 0),
        [20130501] = ISNULL([20130501], 0),
        [20130601] = ISNULL([20130601], 0),
        [20130701] = ISNULL([20130701], 0),
        [20130801] = ISNULL([20130801], 0),
        [20130901] = ISNULL([20130901], 0),
        [20131001] = ISNULL([20131001], 0),
        [20131101] = ISNULL([20131101], 0),
        [20131201] = ISNULL([20131201], 0)
FROM    SomeTable
        LEFT JOIN
        (   SELECT  chh2.cha_kod,
                    [20130101] = SUM(CASE WHEN chh2.cha_tarihi >= '20130101' AND chh2.cha_tarihi < '20130201' THEN chh2.cha_meblag ELSE 0 END),
                    [20130201] = SUM(CASE WHEN chh2.cha_tarihi >= '20130201' AND chh2.cha_tarihi < '20130301' THEN chh2.cha_meblag ELSE 0 END),
                    [20130301] = SUM(CASE WHEN chh2.cha_tarihi >= '20130301' AND chh2.cha_tarihi < '20130401' THEN chh2.cha_meblag ELSE 0 END),
                    [20130401] = SUM(CASE WHEN chh2.cha_tarihi >= '20130401' AND chh2.cha_tarihi < '20130501' THEN chh2.cha_meblag ELSE 0 END),
                    [20130501] = SUM(CASE WHEN chh2.cha_tarihi >= '20130501' AND chh2.cha_tarihi < '20130601' THEN chh2.cha_meblag ELSE 0 END),
                    [20130601] = SUM(CASE WHEN chh2.cha_tarihi >= '20130601' AND chh2.cha_tarihi < '20130701' THEN chh2.cha_meblag ELSE 0 END),
                    [20130701] = SUM(CASE WHEN chh2.cha_tarihi >= '20130701' AND chh2.cha_tarihi < '20130801' THEN chh2.cha_meblag ELSE 0 END),
                    [20130801] = SUM(CASE WHEN chh2.cha_tarihi >= '20130801' AND chh2.cha_tarihi < '20130901' THEN chh2.cha_meblag ELSE 0 END),
                    [20130901] = SUM(CASE WHEN chh2.cha_tarihi >= '20130901' AND chh2.cha_tarihi < '20131001' THEN chh2.cha_meblag ELSE 0 END),
                    [20131001] = SUM(CASE WHEN chh2.cha_tarihi >= '20131001' AND chh2.cha_tarihi < '20131101' THEN chh2.cha_meblag ELSE 0 END),
                    [20131101] = SUM(CASE WHEN chh2.cha_tarihi >= '20131101' AND chh2.cha_tarihi < '20131201' THEN chh2.cha_meblag ELSE 0 END),
                    [20131201] = SUM(CASE WHEN chh2.cha_tarihi >= '20131201' AND chh2.cha_tarihi < '20140101' THEN chh2.cha_meblag ELSE 0 END)
            FROM    dbo.CARI_HESAP_HAREKETLERI chh2
            WHERE   chh2.cha_normal_Iade = 1 
            GROUP BY chh2.cha_kod
        ) chh2
            ON chh2.cha_kod = ch.cari_kod;

这甚至可能不需要子查询,但是正如我所说,如果没有您的完整查询,我只能推测您的表结构以及您实际想要的结果。

回答 1 是的,UDF 执行比子查询更差是正常的。当我搜索SQL-Server UDF versus subquery时,有很多关于This answer的帖子是谷歌的顶部

【讨论】:

【参考方案2】:

第二个问题的答案是将子查询包装在coalesce()

coalesce((Select sum(chh2.cha_meblag)
          from dbo.CARI_HESAP_HAREKETLERI chh2
          where chh2.cha_normal_Iade=1 and
                chh2.cha_kod=ch.cari_kod and
                (chh2.cha_tarihi>='20130101' and chh2.cha_tarihi<='20130131')
         ), '') as Ocai

正如 Martin 在他的评论中指出的那样,coalesce() 有我认为是 SQL Server 中的错误。在这种情况下,它将执行第一个表达式两次,一次是根据NULL 检查值,第二次是获取值。鉴于此,ISNULL() 更好(尽管我通常更喜欢标准函数):

isnull((Select sum(chh2.cha_meblag)
          from dbo.CARI_HESAP_HAREKETLERI chh2
          where chh2.cha_normal_Iade=1 and
                chh2.cha_kod=ch.cari_kod and
                (chh2.cha_tarihi>='20130101' and chh2.cha_tarihi<='20130131')
         ), '') as Ocai

【讨论】:

ISNULL is better @MartinSmith 。 . .感谢您提醒我有关 SQL Server 的缺点。有修复的计划吗?

以上是关于UDF 与子查询性能问题的主要内容,如果未能解决你的问题,请参考以下文章

内联表重视 UDF 性能

Hive UDF 性能太慢

SQL UDF 和查询优化 [重复]

SQL Server 2005 标量 UDF 性能

加入与子查询

用于 UDF 性能统计的 MS SQL DMV - 如何找到前 10 个最差的 UDF