SQL 在视图中声明变量

SQL Declaring Variables in a View

我创建了一个在下面的 where 语句中使用动态变量的视图。

DECLARE 
@wkFileYear SMALLINT = (select fs.FileYear from dbo.FileSemesters fs 
 where fs.systemcurrentflag = 1),
@wkFileSemester SMALLINT = (select fs.FileSemester from dbo.FileSemesters fs where fs.systemcurrentflag = 1)

Select R.FileType, R.FileYear, R.FileSemester, R.ID, R.ClassCode, R1.Raw
from
(
SELECT SC.FileType, SC.FileYear, SC.FileSemester, SC.ID, SC.ClassCode
FROM StudentClasses AS SC 
) as R
LEFT JOIN
(
SELECT  SAR.Filetype,SAR.FileYear, SAR.FileSemester, SAR.ID, SAR.ClassCode, SAR.Result as Raw
FROM    
StudentAssessmentResults AS SAR
) as R1 

on r.FileYear = R1.Fileyear     
and R.FileSemester = R1.FileSemester
and R.FileType = R1.FileType
and R.ClassCode = R1. ClassCode
and R.ID = R1.ID        

where 
R.FileType = 'A'
AND (R.FileYear = @wkFileYear) 
AND (R.FileSemester = 
case 
when (left(R.classcode,2) = '12' or left(R.classcode,2) = '11') and @wkFileSemester = 4 
then 3
else @wkFileSemester
end
)   

我想将其保存为数据库中的视图,但您不能在视图内声明变量。我查看了有关使用 CTE 或 Table Valued Functions 的所有信息,但我不确定所有语法。我尝试使用各种来源的示例创建 CTE 和 Table Value Functions,但没有成功。

希望有人能解释我如何仍然可以使用变量,但允许我将其保存为数据库中的视图。

视图的结构依赖于变量是没有意义的。

相反,您需要在您的视图中创建可以查询的@wkFileYear 和@wkFileSemester 列。

或者,如果您想使用变量,您可以创建一个数据库函数/存储过程,其中 returns 您需要的数据。

这是存储过程的示例。

        CREATE PROCEDURE [dbo].[sp_GetTestData]
            --Param
            @wkFileYear SMALLINT,
            @wkFileSemester SMALLINT
            AS
        BEGIN

        @wkFileYear = select fs.FileYear from dbo.FileSemesters fs where fs.systemcurrentflag = 1
        @wkFileSemester  = select fs.FileSemester from dbo.FileSemesters fs where fs.systemcurrentflag = 1

        Select R.FileType, R.FileYear, R.FileSemester, R.ID, R.ClassCode, R1.Raw
        from
        (
        SELECT SC.FileType, SC.FileYear, SC.FileSemester, SC.ID, SC.ClassCode
        FROM StudentClasses AS SC 
        ) as R
        LEFT JOIN
        (
        SELECT  SAR.Filetype,SAR.FileYear, SAR.FileSemester, SAR.ID, SAR.ClassCode, SAR.Result as Raw
        FROM    
        StudentAssessmentResults AS SAR
        ) as R1 
        on r.FileYear = R1.Fileyear     
        and R.FileSemester = R1.FileSemester
        and R.FileType = R1.FileType
        and R.ClassCode = R1. ClassCode
        and R.ID = R1.ID where 
        R.FileType = 'A'
        AND (R.FileYear = @wkFileYear) 
        AND (R.FileSemester = 
        case 
        when (left(R.classcode,2) = '12' or left(R.classcode,2) = '11') and @wkFileSemester = 4 
        then 3
        else @wkFileSemester
        end
        )
        end

您可以作弊一点,方法是使用 CTE 解析任何想要声明的变量。如果这些 settings 的 CTE 只有一行,那么我们可以使用交叉连接合并 settings 以便它们对每个视图中的行。

CREATE VIEW CurrentSemesterClasses 
AS

WITH SystemSettings as (
    -- Query to resolve the Variables, MUST only return a single row
    SELECT TOP 1 FileYear, FileSemester
    FROM dbo.FileSemesters
    WHERE SystemCurrentFlag = 1
)
SELECT R.FileType, R.FileYear, R.FileSemester, R.ID, R.ClassCode, R1.Raw
FROM StudentClasses R, SystemSettings
LEFT JOIN StudentAssessmentResults R1 
         ON R.FileYear = R1.Fileyear     
        and R.FileSemester = R1.FileSemester
        and R.FileType = R1.FileType
        and R.ClassCode = R1. ClassCode
        and R.ID = R1.ID
WHERE R.FileType = 'A'
  AND R.FileYear = SystemSettings.FileSemester
  AND R.FileSemester = CASE 
                            WHEN (left(R.classcode,2) = '12' or left(R.classcode,2) = '11') and SystemSettings.FileSemester = 4 then 3
                            ELSE SystemSettings.FileSemester
                       END

NOTE: I have simplified your original query by referencing the required tables directly rather than using the strange sub-query that was used. This syntax should behave the same or have better performance than the original, but it ultimately much easier to maintain.

There is no performance benefit to sub-querying just the columns that you think you need via sub-queries instead of joining directly onto tables. This syntax looks like you are expecting the sub-query to cache a subset of the data table but this is not how it works. Do not make the mistake of assuming the sub-query is resolved entirely first before the main query

在这种特殊情况下,如果按照约定 或应用程序逻辑,dbo.FileSemesters table 中将只有一行 SystemCurrentFlag = 1 那么我们可以进一步将这个查询简化为一个简单的连接:

CREATE VIEW CurrentSemesterClassesSimple 
AS
SELECT R.FileType, R.FileYear, R.FileSemester, R.ID, R.ClassCode, R1.Raw
FROM StudentClasses R
INNER JOIN dbo.FileSemesters S ON R.FileYear = s.FileYear AND S.SystemCurrentFlag = 1
LEFT JOIN StudentAssessmentResults R1 
         ON R.FileYear = R1.Fileyear     
        and R.FileSemester = R1.FileSemester
        and R.FileType = R1.FileType
        and R.ClassCode = R1. ClassCode
        and R.ID = R1.ID
WHERE R.FileType = 'A'
  AND R.FileSemester = CASE 
                            WHEN (left(R.classcode,2) = '12' or left(R.classcode,2) = '11') and S.FileSemester = 4 then 3
                            ELSE S.FileSemester
                       END

为了完成这个集合,下面是如何将清理后的原始查询打包成 TVF - Table Valued Function

CREATE FUNCTION CurrentSemesterClassesFn
(
    -- This function has no parameters
)
RETURNS
@Table_Var TABLE
(
    -- Please update the types of these columns to match your schema!
      FileType CHAR(1)
    , FileYear INT
    , FileSemester INT
    , ID INT
    , ClassCode VARCHAR(100)
    , [Raw] VARCHAR(max)
)
CREATE VIEW CurrentSemesterClassesSimple 
AS
BEGIN

DECLARE @wkFileYear SMALLINT, @wkFileSemester SMALLINT;
SELECT @wkFileYear = fs.FileYear
     , @wkFileSemester = fs.FileSemester
FROM dbo.FileSemesters fs 
WHERE fs.systemcurrentflag = 1;

INSERT INTO @Table_Var (FileType, FileYear, FileSemester, ID, ClassCode, Raw)
SELECT R.FileType, R.FileYear, R.FileSemester, R.ID, R.ClassCode, R1.Raw
FROM StudentClasses R
LEFT JOIN StudentAssessmentResults R1 
         ON R.FileYear = R1.Fileyear     
        and R.FileSemester = R1.FileSemester
        and R.FileType = R1.FileType
        and R.ClassCode = R1. ClassCode
        and R.ID = R1.ID
WHERE R.FileType = 'A'
  AND R.FileYear = @wkFileYear
  AND R.FileSemester = CASE 
                            WHEN (left(R.classcode,2) = '12' or left(R.classcode,2) = '11') and @wkFileSemester = 4 then 3
                            ELSE S.FileSemester
                       END

    RETURN
END