是否可以组合视图和存储过程中的列?
Posted
技术标签:
【中文标题】是否可以组合视图和存储过程中的列?【英文标题】:Is it possible to combine columns from a view and a stored procedure? 【发布时间】:2020-04-08 16:28:43 【问题描述】:我正在使用 SQL Server 2017。我正在尝试从 SQL Server 返回一组数据,其格式最终将进入 Excel 电子表格。数据很复杂,虽然我可以将两组数据输出到 Excel 中,然后使用代码创建 VLOOKUP
以将数据集结合在一起,但我试图避免增加的复杂性。
为了稍微简化,我为学生所学课程设置了一组分数。该表可能如下所示:
CREATE TABLE TempStudentMarks
(
StudentID VARCHAR(4),
CourseName VARCHAR(50),
Mark INT
)
INSERT INTO TempStudentMarks (StudentID, CourseName, Mark)
VALUES ('1234', 'English', 78),
('1234', 'Maths', 68),
('1234', 'Science', 58),
('4321', 'English', 66),
('4321', 'Maths', 76),
('4321', 'French', 86),
('5555', 'Maths', 69),
('5555', 'Science', 49),
('5555', 'French', 69),
('6666', 'English', 33),
('6666', 'Maths', 44),
('6666', 'Science', 55),
('6666', 'French', 66)
我创建了一个存储过程,它在一行中输出每个学生,在一列中输出每个课程。它使用动态 SQL,因为课程数量因年而异,并且取决于许多其他因素:
CREATE PROCEDURE spTemp_StudentMarksPivot
AS
DECLARE @SQL NVARCHAR(MAX)
DECLARE @PivotCols NVARCHAR(MAX)
SELECT
@PivotCols = COALESCE(@PivotCols + ',', '') +
QUOTENAME(CourseName)
FROM
(SELECT DISTINCT CourseName
FROM TempStudentMarks) AS HeaderData
PRINT @PivotCols
SET @SQL = 'SELECT StudentID, ' + @PivotCols + '
FROM (SELECT * FROM TempStudentMarks) SourceData
PIVOT (MAX(Mark) FOR CourseName IN (' + @PivotCols + ')) AS PivotData'
PRINT @SQL
EXEC sys.sp_executesql @SQL
这很好用;我得到的输出:
EXEC spTemp_StudentMarksPivot
看起来像这样:
StudentID English French Maths Science
--------- ----------- ----------- ----------- -----------
1234 78 NULL 68 58
4321 66 86 76 NULL
5555 NULL 69 69 49
6666 33 66 44 55
我还有一个视图可以进行一些统计分析。简化版如下所示:
CREATE VIEW vwStudentAggregates
AS
SELECT tsm.StudentID, AVG(Mark) AS Average, MAX(Mark) AS BestMark
FROM TempStudentMarks tsm
GROUP BY tsm.StudentID
当我针对该视图运行 SELECT
语句时,我得到以下信息:
StudentID Average BestMark
--------- ----------- -----------
1234 68 78
4321 76 86
5555 62 69
6666 49 66
所有这些都很好。
但我的问题是将两者结合起来,以实现如下输出:
StudentID English French Maths Science Average BestMark
--------- ----------- ----------- ----------- ----------- ---------- ----------
1234 78 NULL 68 58 68 78
4321 66 86 76 NULL 76 76
5555 NULL 69 69 49 62 69
6666 33 66 44 55 49 66
这可能吗?
鉴于我事先不知道列的数量或名称,我看不到如何创建临时表。我在这里查看了解决方案:
Insert results of a stored procedure into a temporary table
虽然我认为这可能会完成这项工作,但由于一些用户通过 Windows 身份验证登录,而其他用户必须使用 SQL 身份验证,所以情况变得复杂,因此,虽然该解决方案使用 OpenRowset,但我在有点说
SELECT *
INTO #MyTempTable
FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'EXEC getBusinessLineHistory')
因为我不知道如何创建一个适合所有人的连接字符串。
[编辑] 我只是在这个想法上玩了一点,并得到了一大堆错误,从
开始“过程 sp_configure,第 105 行用户无权执行此操作。”
并以
结尾“SQL Server 阻止访问 STATEMENT 'OpenRowset/OpenDatasource”
所以看起来不太好...(这项工作适用于 ICT 部门的安全性相当严格的客户,并且不太可能希望授予额外权限...
如果有人可以提供帮助或指导,我将不胜感激。
非常感谢 安德鲁
【问题讨论】:
为什么不只计算动态数据透视表中所需的一切?而不是使用引用限制PIVOT
运算符使用条件聚合,你可以做所有事情。
您好 - 感谢您的回复。你能解释一下它是如何工作的吗?如果只有三四门课程,是的,我可以看到使用 CASE 语句,但我可能有 60 门课程,数百名学生,以及其他变量,如 YearOfStudy 和 CalendarYear 需要考虑,所以我不考虑'认为那行不通。你在想我错过的其他方式吗?你能给我一个代码示例,这样我就可以看到你在想什么?非常感谢
您是说您当前的动态PIVOT
还没有处理所有这些吗?
对不起 - 我很明显很密集。我不知道你的意思...我的动态 Pivot 处理所有什么?!显然,它在适当的标题下方显示了动态创建的数字,但它没有像我想要的那样在末尾添加聚合标记。但我以为你是在建议我不要使用 PIVOT,因为它具有限制性......
【参考方案1】:
SET @SQL = '
SELECT PivotData.StudentID, ' + @PivotCols + ', vst.Average, vst.BestMark
FROM (SELECT * FROM TempStudentMarks) SourceData
PIVOT (MAX(Mark) FOR CourseName IN (' + @PivotCols + ')) AS PivotData
join vwStudentAggregates as vst on PivotData.StudentID = vst.StudentID
';
--or
SET @SQL = '
select *
from
(
SELECT StudentID, ' + @PivotCols + '
FROM (SELECT * FROM TempStudentMarks) SourceData
PIVOT (MAX(Mark) FOR CourseName IN (' + @PivotCols + ')) AS PivotData
) as pd
join vwStudentAggregates as vst on pd.StudentID = vst.StudentID
';
--you can even parameterize the procedure, to return or not the studentaggregates and conditionally construct the executed @SQL.
【讨论】:
完美 - 非常感谢。我没有意识到我可以在枢轴之后继续加入表格。这真的很有帮助。 -再次感谢。【参考方案2】:您正在为不同的表编写 sql,您希望结果在一个表中。 table joining
是这样做的方法。
查询:
select TempStudentMarks.StudentID, avg(TempStudentMarks.mark) as
average,max(TempStudentMarks.mark) as maximum,
Science.mark as science,French.Mark as french,Math.mark as math,
English.Mark as english from TempStudentMarks
LEFT JOIN
(
select StudentID,mark from TempStudentMarks where CourseName='Science'
) AS Science
on Science.StudentID=TempStudentMarks.StudentID
LEFT JOIN
(
select StudentID,mark from TempStudentMarks where CourseName='French'
) AS French
on French.StudentID=TempStudentMarks.StudentID
LEFT JOIN
(
select StudentID,mark from TempStudentMarks where CourseName='Maths'
) AS Math
on Math.StudentID=TempStudentMarks.StudentID
LEFT JOIN
(
select StudentID,mark from TempStudentMarks where CourseName='English'
) AS English
on English.StudentID=TempStudentMarks.StudentID
group by TempStudentMarks.StudentID,French.Mark,Science.Mark,English.Mark,Math.Mark
我不想写一个循环。如果你愿意,你可以自己试一试
【讨论】:
嗨 - 感谢您的建议。我不认为这对我有用,因为课程太多,而且课程列表每年都在变化,所以这个查询每年都需要重新编写。我想我可以走上使用游标并以这种方式循环的道路,但我想让它尽可能简单,而且游标似乎是最好避免的开销。还是谢谢。以上是关于是否可以组合视图和存储过程中的列?的主要内容,如果未能解决你的问题,请参考以下文章