【问题标题】:Set Variable in SQL Function with Condition使用条件在 SQL 函数中设置变量
【发布时间】:2015-01-15 16:44:43
【问题描述】:

尝试根据当前日期设置一个变量并通过 SQL 函数传递给查询。它在 IF 语句中出错。有什么想法吗?

  CREATE FUNCTION CS_AwaredRCPs
     (   
     @currentDate DATE,
    @fiscalYear INT

    IF DATEPART(m,@currentDate) > 10
        SET @fiscalYear = DATEPART(yyyy,@currentDate)
    ELSE
        SET @fiscalYear = DATEPART(yyyy,@currentDate) - 1
    END 
     )
    RETURNS TABLE 
    AS
    RETURN 
    (
    SELECT      dbo.tbl_requirementManagement.postaward_specialist_id, 
            SUM(dbo.tbl_requirementManagement.actual_award_value) AS    AwardValue, 
            COUNT(dbo.tbl_requirementManagement.postaward_specialist_id) AS AwardCount
    FROM        dbo.tbl_requirementManagement RIGHT OUTER JOIN
            dbo.vw_ContractSpecialists ON   dbo.tbl_requirementManagement.postaward_specialist_id = dbo.vw_ContractSpecialists.user_certificateSerialNumber
    GROUP BY    dbo.tbl_requirementManagement.statusID, dbo.tbl_requirementManagement.postaward_specialist_id, dbo.tbl_requirementManagement.fiscal_year
    HAVING     (dbo.tbl_requirementManagement.statusID = 4) AND 
           (dbo.tbl_requirementManagement.postaward_specialist_id <> 0) AND 
           (dbo.tbl_requirementManagement.fiscal_year = @fiscalYear)

    )

`

【问题讨论】:

  • 不使用时为什么要传入@FiscalYear? IF / ELSE 在所有情况下都会分配它,因此为@FiscalYear 传入的值从未实际使用过。
  • 我对需要申报或通过什么感到困惑。

标签: sql-server function parameter-passing


【解决方案1】:

你根本不需要使用IF,你可以简单地做你的谓词:

fiscal_year = DATEPART(YEAR, @currentDate) 
                + CASE WHEN DATEPART(MONTH, @CurrentDate) <= 10 THEN -1 ELSE 0 END

那么你根本不需要@FiscalYear 参数。值得一提的是,您应该在函数中更改许多其他内容

  1. 使用表格别名可以显着减少文本量,因此您可以只使用rm,而不是一遍又一遍地使用dbo.tbl_requirementManagement

  2. 1234563澄清一下,你有(dbo.tbl_requirementManagement.statusID = 4),所以你在tbl_requirementManagement中没有匹配的地方,statusID将是NULL,而NULL = 4返回NULL,这是不正确的,因此不会返回该行.
  3. 您的谓词不引用聚合,因此应该在 WHERE 子句中,而不是 HAVING

所以你的最终功能变成:

CREATE FUNCTION CS_AwaredRCPs (@currentDate DATE)
RETURNS TABLE
AS
RETURN
(   SELECT  rm.postaward_specialist_id,
            SUM(rm.actual_award_value) AS AwardValue, 
            COUNT(rm.postaward_specialist_id) AS AwardCount
    FROM    dbo.tbl_requirementManagement AS rm
            INNER JOIN dbo.vw_ContractSpecialists AS cs
                ON rm.postaward_specialist_id = cs.user_certificateSerialNumber 
    WHERE   rm.fiscal_year = DATEPART(YEAR,@currentDate) 
                                + CASE WHEN DATEPART(MONTH, @CurrentDate) <= 10 THEN -1 ELSE 0 END
    AND     rm.statusID = 4
    AND     rm.postaward_specialist_id <> 0
    GROUP BY rm.statusID, rm.postaward_specialist_id, rm.fiscal_year
);

【讨论】:

  • 非常好。我正要发布类似的内容,至少在指出 @FiscalYear 参数的无用并将整个内容保留为内联 TVF 方面。但是你的附加点是正确的,所以+1 :)
  • -Gareth 感谢您的帮助和解释。我是 SQL 函数的新手,这肯定有帮助!
  • 没问题。需要记住的一件事,正如在另一条评论中所提到的那样,您最初拥有的内联表值函数 (TVF) 将比多语句 TVF 执行得更好(如在接受的答案中)。原因是内联 TVF 的行为更像一个视图,因为它的定义在每次调用时都会扩展到外部查询中,因此可以对其进行优化,而执行多语句 TVF RBAR跨度>
【解决方案2】:

您不能在函数parameter list 内进行if-else 检查。将If-else 部分移动到函数body。试试这个。

CREATE FUNCTION Cs_awaredrcps (@currentDate DATE)
RETURNS @ret_table TABLE (
  postaward_specialist_id INT,
  AwardValue              INT,
  AwardCount              INT )
AS
  BEGIN
      DECLARE @fiscalYear INT

      IF Datepart(Month, @currentDate) > 10
        SET @fiscalYear = Datepart(yyyy, @currentDate)
      ELSE
        SET @fiscalYear = Datepart(yyyy, @currentDate) - 1

      INSERT INTO @ret_table
      SELECT dbo.tbl_requirementManagement.postaward_specialist_id,
             Sum(dbo.tbl_requirementManagement.actual_award_value)        AS AwardValue,
             Count(dbo.tbl_requirementManagement.postaward_specialist_id) AS AwardCount
      FROM   dbo.tbl_requirementManagement
             RIGHT OUTER JOIN dbo.vw_ContractSpecialists
                           ON dbo.tbl_requirementManagement.postaward_specialist_id = dbo.vw_ContractSpecialists.user_certificateSerialNumber
      GROUP  BY dbo.tbl_requirementManagement.statusID,
                dbo.tbl_requirementManagement.postaward_specialist_id,
                dbo.tbl_requirementManagement.fiscal_year
      HAVING ( dbo.tbl_requirementManagement.statusID = 4 )
             AND ( dbo.tbl_requirementManagement.postaward_specialist_id <> 0 )
             AND ( dbo.tbl_requirementManagement.fiscal_year = @fiscalYear )

      RETURN
  END 

【讨论】:

  • 如果它是内联表值函数而不是多语句表值函数,从性能的角度来看会好得多。只需将其更改为 case 表达式而不是开头的 IF 将大大提高性能。
猜你喜欢
  • 2022-01-17
  • 2013-08-09
  • 2017-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多