在 SQL Server 视图中有效地将行转换为列
Posted
技术标签:
【中文标题】在 SQL Server 视图中有效地将行转换为列【英文标题】:Efficiently convert rows to columns in SQL Server view 【发布时间】:2016-08-31 07:52:59 【问题描述】:我有 3 个表:testpackage
、testpackageReport
、testpackagereportdetail
,结构如下:
有了这个查询
SELECT
dbo.TestPackages.PackageNumber, dbo.TestPackages.Size,
dbo.TestPackages.Code, dbo.TestPackageReports.ReportNumber,
dbo.TestPackageReportDetails.Step, dbo.TestPackageReportDetails.Status,
dbo.TestPackageReports.SubmitDateTime,
dbo.TestPackageReportDetails.Id AS ReportDetailId
FROM
dbo.TestPackages
INNER JOIN
dbo.TestPackageReportDetails ON dbo.TestPackages.Id = dbo.TestPackageReportDetails.TestPackageId
INNER JOIN
dbo.TestPackageReports ON dbo.TestPackageReportDetails.TestPackageReportId = dbo.TestPackageReports.Id
结果是这样的:
PackageNumber Size Code ReportNumber Step Status SubmitDateTime ReportDetailId
1000220-G-02-TR 1.31 143 LC-0431 LineCheck Reject 2010-12-12 218
1000220-G-02-TR 1.31 143 LC-0131 LineCheck Accept 2011-12-12 220
1000220-G-02-TR 1.31 143 PT-0248 Test Accept 2012-12-12 513
1000220-G-02-TR 1.31 143 DR-0202 Drying Accept 2013-12-12 625
1000220-G-02-TR 1.31 143 AFP-AG-FL-0030 Flushing Accept 2015-12-12 745
但我需要像这样在一行中显示这些数据:
PackageNumber Size Code LineCheckReportNumber LineCheckStep LineCheckStatus linecheckSubmitDateTime ReportDetailId tesReportNumber testCheckStep testStatus testSubmitDateTime ReportDetailId
1000220-G-02-TR 1.31 143 LC-0431 LineCheck Accept 2011-12-12 220 PT-0248 Test Accept 2012-12-12 513
对于预期结果中的嘈杂数据,我删除了干燥和冲洗列。如您所见,我需要将所有这些记录显示为一行,另一个重要的是最大ReportDetailId
的数据,即accepted
,因为每个testpackage
可以有多个linecheck
或test
或flsuhing
或drying
报告
样本数据:
TestpackageTable
测试包报告
Testpackagereportdetail
如您所见,我使用实体框架编写查询,但速度很慢:
from i in _ctx.TestPackages
join testpackreportdet in _ctx.TestPackageReportDetails on i.Id equals
testpackreportdet.TestPackageId
join testPackageRepo in _ctx.TestPackageReports on testpackreportdet.TestPackageReportId equals testPackageRepo.Id into g1
from y1 in g1.DefaultIfEmpty()
group new y1, testpackreportdet by new i
into grouping
let MaxLinecheck = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "LineCheck")
.OrderByDescending(item => item.Id)
let MaxClean = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Clean")
.OrderByDescending(item => item.Id)
let MaxTest = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Test")
.OrderByDescending(item => item.Id)
let MaxFlush = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Flushing")
.OrderByDescending(item => item.Id)
let MaxDrying = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Drying")
.OrderByDescending(item => item.Id)
let MaxReins = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Reinstatment")
.OrderByDescending(item => item.Id)
let MaxMono = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Mono")
.OrderByDescending(item => item.Id)
let MaxPAD = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "PADTest")
.OrderByDescending(item => item.Id)
let MaxVariation = grouping.Select(item => item.testpackreportdet)
.Where(item => item != null && item.Step == "Variation")
.OrderByDescending(item => item.Id)
select new ViewDomainClass.TechnicalOffice.ViewTestPackageState()
Id = grouping.Key.i.Id,
PackageNumber = grouping.Key.i.PackageNumber,
Size = grouping.Key.i.Size.ToString(),
Code = grouping.Key.i.Code,
TestPackageOrder = grouping.Key.i.TestPackageOrder,
LineCheckState = MaxLinecheck.FirstOrDefault().Status,
LineCheckSubmitDateTime =
grouping.Where(
i => i.y1.Id == MaxLinecheck.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
LineCheckReportNumber =
grouping.Where(
i => i.y1.Id == MaxLinecheck.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
CleaningState = MaxClean.FirstOrDefault().Status,
CleanSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxClean.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
CleanReportNumber =
grouping.Where(i => i.y1.Id == MaxClean.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
TestState = MaxTest.FirstOrDefault().Status,
TestSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxTest.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
TestReportNumber =
grouping.Where(i => i.y1.Id == MaxTest.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
Drying = MaxDrying.FirstOrDefault().Status,
DryingSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxDrying.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
DryingReportNumber =
grouping.Where(i => i.y1.Id == MaxDrying.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
Flushing = MaxFlush.FirstOrDefault().Status,
FlushingSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxFlush.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
FlushingReportNumber =
grouping.Where(i => i.y1.Id == MaxFlush.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
ReInstatement = MaxReins.FirstOrDefault().Status,
ReInstatementSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxReins.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
ReInstatementReportNumber =
grouping.Where(i => i.y1.Id == MaxReins.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
Mono = MaxMono.FirstOrDefault().Status,
MonoSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxMono.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
MonoReportNumber =
grouping.Where(i => i.y1.Id == MaxMono.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
Variation = MaxVariation.FirstOrDefault().Status,
VariationSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxVariation.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
VariationReportNumber =
grouping.Where(i => i.y1.Id == MaxVariation.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber,
PAD = MaxPAD.FirstOrDefault().Status,
PADSubmitDateTime =
grouping.Where(i => i.y1.Id == MaxPAD.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.SubmitDateTime.ToString(),
PADReportNumber =
grouping.Where(i => i.y1.Id == MaxPAD.FirstOrDefault().TestPackageReportId)
.FirstOrDefault()
.y1.ReportNumber
).ToList();
【问题讨论】:
您是否要显示所有数据,例如表格中的样本? @AT-2016 我想在一条记录中显示所有数据。 @AT-2016 我的样本数据只针对一个测试包,可能我的数据中有多个测试包 您可以为此使用数据透视。这可能会有所帮助:***.com/questions/15745042/… @EhsanAkbar - 我不喜欢......所以问题应该以这样一种方式发布,即所有需要的都发布在问题中...... 【参考方案1】:您需要透视和动态 SQL。我建议将您的查询输出放入临时表中,然后使用它:
USE YourDatabase --Use your database
SELECT TP.PackageNumber,
TP.Size,
TP.Code,
R.ReportNumber,
RD.Step,
RD.[Status],
R.SubmitDateTime,
RD.Id AS ReportDetailId
INTO #temporary --it will automatically create #temporary table with results
FROM dbo.TestPackages TP
INNER JOIN dbo.TestPackageReportDetails RD
ON TP.Id = RD.TestPackageId
INNER JOIN dbo.TestPackageReports R
ON RD.TestPackageReportId = R.Id
USE tempdb --switch to tempdb
SELECT TOP 1 WITH TIES * INTO #temp
FROM #temporary
ORDER BY ROW_NUMBER() OVER (PARTITION BY PackageNumber, Size, Code, Step ORDER BY ReportDetailId DESC)
--Here we get only rows with maximum ReportDetailID over PackageNumber, Size, Code, Step
DROP TABLE #temporary --get rid of #temporary, now we use #temp
DECLARE @pvt_columns nvarchar(max), --to store columns for pivoting
@unpvt_columns nvarchar(max), --to store columns that will be converted into 1 datatype for unpivoting
@columns nvarchar(max), -- columns comma separated
@sql nvarchar(max) --store query to run
SELECT @pvt_columns = COALESCE(@pvt_columns,'') + ','+QUOTENAME(Step+name)
FROM (
SELECT name
FROM sys.columns
WHERE object_id = OBJECT_ID(N'#temp')
AND name not in ('PackageNumber','Size','Code','Step')
) names
CROSS JOIN (
SELECT DISTINCT Step
FROM #temp
) steps
SELECT @unpvt_columns = COALESCE(@unpvt_columns,'')+',CAST('+QUOTENAME(name)+' as nvarchar(max)) '+QUOTENAME(name),
@columns = COALESCE(@columns,'') + ','+QUOTENAME(name)
FROM sys.columns
WHERE object_id = OBJECT_ID(N'#temp')
AND name not in ('PackageNumber','Size','Code','Step')
SELECT @sql = N'
SELECT *
FROM (
SELECT PackageNumber,
Size,
Code,
Step+[Columns] as [Columns],
[Values]
FROM (
SELECT PackageNumber,
Size,
Code,
Step'+@unpvt_columns+'
FROM #temp) t
UNPIVOT (
[Values] FOR [Columns] IN ('+STUFF(@columns,1,1,'')+')
) unpvt
) p
PIVOT (
MAX([Values]) FOR [Columns] in ('+STUFF(@pvt_columns,1,1,'')+')
) pvt'
EXEC sp_executesql @sql
DROP TABLE #temp
输出:
PackageNumber Size Code DryingReportDetailId DryingReportNumber DryingStatus DryingSubmitDateTime FlushingReportDetailId FlushingReportNumber FlushingStatus FlushingSubmitDateTime LineCheckReportDetailId LineCheckReportNumber LineCheckStatus LineCheckSubmitDateTime TestReportDetailId TestReportNumber TestStatus TestSubmitDateTime
1000220-G-02-TR 1,31 143 625 DR-0202 Accept 2013-12-12 745 AFP-AG-FL-0030 Accept 2015-12-12 220 LC-0131 Accept 2011-12-12 513 PT-0248 Accept 2012-12-12
您可以SELECT
变量查看其中存储的内容,PRINT @sql
查看查询全文。
【讨论】:
太棒了!我的荣幸! :) 你好 SQL server 的人力。很抱歉我的问题。你能给我一些帮助吗:***.com/questions/39269277/…以上是关于在 SQL Server 视图中有效地将行转换为列的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server - Pivot 将行转换为列(带有额外的行数据)