在表值函数中使用CTE的问题

时间:2011-06-22 12:04:57

标签: sql sql-server-2005 tsql sql-server-2008

我试图避免在我的项目中使用标量值函数,所以我决定尝试使用CTE将其中一个转换为表值函数。

我知道标量值函数的性能很差,因为它们必须为每一行执行,而SQL服务器无法以任何方式对其进行优化(即它充当黑盒子)。

这是我第一次尝试将其转换为表值函数...

CREATE FUNCTION [dbo].[fn_get_job_average] (@jobnumber VARCHAR(50))
RETURNS TABLE AS RETURN
(   

  WITH JobAverage AS
  (
    SELECT job.Jobnumber, CAST(AVG(CAST(jobMark.Mark AS DECIMAL(18,1))) AS DECIMAL(18,1)) AS Average
    FROM  job 
      INNER JOIN jobMark 
        ON job.Guid = jobMark.Guid
    WHERE job.Jobnumber = @jobnumber
    GROUP BY job.Jobnumber
  )
  SELECT Jobnumber,
    CASE
      WHEN EXISTS(SELECT * FROM JobAverage) THEN Average
      ELSE 0.0 -- This never executes???, i.e. for job records that don't have a mark nothing is returned
    END AS Average
  FROM JobAverage
)

我想输出一个包含工号和平均分数的表格。

对于有标记的作业,似乎没问题。也就是说,平均值与jobnumer一起返回。

对于没有商标的工作,似乎出错了。语句的ELSE部分不执行。也就是说,作为平均工作,我不会返回0.0。没有记录返回。我错过了什么吗?

抱歉,我不是一位经验丰富的SQL开发人员,所以我在上面的代码中可能会有一些明显的错误。但是,我很困惑,为什么它不起作用。

提前致谢。

2 个答案:

答案 0 :(得分:5)

未经测试但是这样的事情应该做你想要的。

   SELECT job.Jobnumber, COALESCE(CAST(AVG(CAST(jobMark.Mark AS DECIMAL(18,1))) AS DECIMAL(18,1)), 0.0) AS Average
    FROM  job 
      LEFT OUTER JOIN jobMark 
        ON job.Guid = jobMark.Guid
    WHERE job.Jobnumber = @jobnumber
    GROUP BY job.Jobnumber

无需使用CTE。

顺便说一句:你所做的就是在Jobnumber声明中检查CTE case中的存在。如果CTE中没有行,则 会在else部分中结束,但由于您在主查询的Jobnumber子句中使用CTE from,因此您将无法获得任何行,因为CTE Jobnumber没有返回任何行。

所以要完全清楚发生了什么。如果CTE case中没有行,则永远不会执行Jobnumber语句。

答案 1 :(得分:1)

EXISTS(SELECT * FROM JobAverage)表示“在整个JobAverage中根本没有任何行”。

是的,当然因为CASE正在JobAverage的输出行中执行

我想你想要的是什么:

  

每份工作的平均分数。
  没有作业标记的零

SELECT
    job.Jobnumber,
    ISNULL(
       CAST(AVG(CAST(jobMark.Mark AS DECIMAL(18,1))) AS DECIMAL(18,1))
      ,0) AS Average
FROM 
      job 
      LEFT JOIN
      jobMark ON job.Guid = jobMark.Guid
WHERE job.Jobnumber = @jobnumber
GROUP BY job.Jobnumber