带有存储过程的每日报告

Posted

技术标签:

【中文标题】带有存储过程的每日报告【英文标题】:Daily report with stored procedure 【发布时间】:2017-05-22 10:11:07 【问题描述】:

我有这个 sp 来做一个需要每天做的报告。我怎样才能实现白天部分?就像我写的那样没关系,或者这是一些简单的方法?!

ALTER PROCEDURE [dbo].[pr_Report]
    @YearOfRegistration INT
AS 
    SELECT
        peCountryID,
        peCountryName as coName,
        ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
        ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS  rdValue2,
        COUNT(*) AS Total
    FROM 
        vPerson
    WHERE 
        @YearOfRegistration = 0 
        OR peYearOfRegistration = @YearOfRegistration
        AND (DATEPART(dd, peSubmitDate) = DATEPART(dd, GETDATE()) 
        AND DATEPART(MM, peSubmitDate) = DATEPART(MM, GETDATE()) 
        AND DATEPART(yy, peSubmitDate) = DATEPART(YY, GETDATE()))
    GROUP BY 
        peCountryofResidencyID, peCountryOfResidencyName

【问题讨论】:

【参考方案1】:

逻辑是正确的,但这是一种非常糟糕的方法。您应该尽可能避免对数据调用函数,尤其是在 where 子句中,因为这意味着基础列上的任何索引都不能使用。

你的谓词最好写成:

WHERE   peSubmitDate >= CAST(GETDATE() AS DATE)
AND     peSubmitDate < DATEADD(DAY, 1, CAST(GETDATE() AS DATE));

这样可以使用索引,你的查询是sargable

碰巧,将DATETIME 转换为DATE(反之亦然)实际上是不使用函数规则的一个例外,因此您可以将其缩短为:

WHERE   CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())

另一点是,尽管使用OR 来适应您的两种选择(按年份过滤或返回所有记录)可能看起来更好,但您会发现使用两个单独的查询会执行得更好。所以你的最终 SP 可能是:

ALTER PROC [dbo].[pr_Report] @YearOfRegistration INT
AS
BEGIN
    IF (@YearOfRegistration = 0)
    BEGIN
        SELECT  peCountryID,
                peCountryName as coName,
                ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
                ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS  rdValue2,
                COUNT(*) AS Total
        FROM    vPerson
        WHERE   CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())  
        GROUP BY peCountryofResidencyID,peCountryOfResidencyName
    END
    ELSE
    BEGIN
        SELECT  peCountryID,
                peCountryName as coName,
                ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
                ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS  rdValue2,
                COUNT(*) AS Total
        FROM    vPerson
        WHERE   CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())  
        AND     peYearOfRegistration = @YearOfRegistration
        GROUP BY peCountryofResidencyID,peCountryOfResidencyName;

    END
END

【讨论】:

“尽可能避免调用函数,尤其是在 where 子句中,因为这意味着不能使用基础列上的任何索引”与您使用 @987654329 的建议直接矛盾@、DATEADD()GETDATE()WHERE 子句中。也许您可以为少数可能感到困惑的人澄清一下。 我已经说过 datetime 和 date 之间的强制转换和转换是这个规则的例外,GETDATE() 是一个运行时常量,所以它在运行时计算一次,而不是针对返回的每一行。因此,DATEADD(DAY, 1, Column) is evaluated once for every column, but DATEADD(DAY, 1, GETDATE())` 每次查询都会评估一次,因此不会对性能产生不利影响。 我担心有些人只会听到“绝不where子句中使用函数”。应用于 的函数可能会影响可搜索性。至于GetDate(),每个 instance 都是一个单独的运行时常量,因此第一个示例中的两个调用可以返回不同的值。 (可以在here 找到有关该功能的一些讨论。)当两个电话返回不同的日期时,试图调试在午夜运行的报告的仆人可悲了。【参考方案2】:

getdate 返回一个日期时间,因此如果您只想比较日期而不是一天中的时间,您可以使用 cast。如果 peSubmitDate 的数据类型为 Date ,请使用此比较:

peSubmitDate  = cast(GetDate() as date)

如果是数据时间,那么像这样使用它: cast(peSubmitDate as date) = cast(GetDate() as date)

后一种性能更差,所以只有在是日期时间时才使用它

【讨论】:

感谢你们,还有一个问题 - 我如何将这个 select 语句与 peSubmitDate 子句和另一个一般(没有 Date 子句)的语句合并到一个 sp 中?

以上是关于带有存储过程的每日报告的主要内容,如果未能解决你的问题,请参考以下文章

带有 PHP PDO 的只读副本上的 MySQL 存储过程

带有枢轴的存储过程不会返回结果结构而是返回零值

报告存储过程 - 如何避免重复?

s-s-rS 报告未从存储过程中提取数据

s-s-rS 2008 报告无法使用存储过程

使用存储过程的 s-s-rS 报告