'SQL Declaring Variables in a View
I have created a view which uses dynamic variables in the where statement below.
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
)
I would like to save this as a view in the database but you can't declare variables inside a view. I have looked at all the info on using CTEs or Table Valued Functions but am not sure at all of the syntax. I have tried creating the CTEs and Table Valued Functions using examples from various sources but no luck.
Was hoping that someone could explain how I could still use the variables but allow me to save this as a view in the database.
Solution 1:[1]
It does not make sense for a view's structure to be dependent on variables.
Instead you need to make @wkFileYear and @wkFileSemester columns in your view that you can query against.
Alternatively you could create a db function / stored procedure which returns the data you need, if you want to use variables.
Here's an example of what the stored proc might look like.
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
Solution 2:[2]
You can cheat a little bit by using a CTE to resolve any variables that would like to declare. If the CTE for these settings only has a single row, then we can use a cross join to merge the settings so that they are available to each row in the view.
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
In this particular case, if by convention or application logic there will only ever be a single row in the dbo.FileSemesters
table that has SystemCurrentFlag = 1
then we can further simplify this query to a simple join:
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
To complete the set, the following is how you would package the cleaned up original query into a 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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Chris Schaller |