在 SQL Server 视图中有效地将行转换为列

Posted

技术标签:

【中文标题】在 SQL Server 视图中有效地将行转换为列【英文标题】:Efficiently convert rows to columns in SQL Server view 【发布时间】:2016-08-31 07:52:59 【问题描述】:

我有 3 个表:testpackagetestpackageReporttestpackagereportdetail,结构如下:

有了这个查询

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 可以有多个linechecktestflsuhingdrying 报告

样本数据:

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 将行数据转换为列标题

SQL Server - Pivot 将行转换为列(带有额外的行数据)

SQL Server 2008 R2,将列转换为行,将行转换为列[重复]

如何动态地将行转换为列,并为每列使用不同的列名

在 Oracle 中动态地将行转为列

在 SQL 中使用“Pivot”将行转换为列