如何在 SQL 服务器中对 table 进行 UNPIVOT?

How to UNPIVOT a table in SQL Server?

我正在尝试对我的数据进行逆透视,但得到了一些奇怪的结果。我怎样才能做到这一点?

下面是我的代码和结果截图。 (SQL Fiddle)

select distinct recId, caseNumber, servtype, mins
from
(
    select 
        recid
        ,caseNumber
        ,[preEnrollment_type]
        ,[preEnrollment_minutes]
        ,[screening_type]
        ,[screeningEnA_minutes]
        ,[ifsp_type]
        ,[ifsp_minutes]
    from
        CaseManagementProgressNote
    where
        [formComplete]=1
        and [reviewed]<>1
        and [dataentry]<>1
        and [caseManagementEntry]=1
        and [serviceCoordinator] <> 'webmaster@company.net'
        and [contactDateTime] >= '1/1/2015'
        and [childID] is not null
) as cp
unpivot
(
    servType for servTypes in ([preEnrollment_type],[screening_type],[ifsp_type])
) as up1
unpivot
(
    mins for minutess in ([preEnrollment_minutes],[screeningEnA_minutes],[ifsp_minutes])
) as up2
order by
    recId

顶部是 奇怪的 unpivoted 数据,底部是实际的 table.

正如您在 unpivoted 数据中看到的那样,[column]_type 重复了两次并且对应值不正确。

我需要

1439 964699 -NA-   null
1439 964699 SC     45
1439 964699 TCM FF 20

还要考虑到我还有更多专栏要 select。

这是我使用的参考资料mssqltips

SQL Fiddle 上面的例子。

您可以在这里使用 Cross Apply 代替 unpivot。我发现语法更容易理解,并且您将能够保留空值。尝试类似的东西:

select recid, casenumber, servtype, mins
from CaseManagementProgressNote a
cross apply (VALUES (preEnrollment_type, preEnrollment_minutes),
                    (screening_type, screeningEna_Minutes),
                    (ifsp_type, ifsp_minutes)) unpvt (servtype, mins)

您的印象似乎是您的两个 UNPIVOT 操作不知何故 linked。它们不是,除了第二个 UNPIVOT 是对第一个的结果执行的。

如果您查看第一个 UNPIVOT 的结果:

select *
from
(
    select 
        recid
        ,caseNumber
        ,[preEnrollment_type]
        ,[preEnrollment_minutes]
        ,[screening_type]
        ,[screeningEnA_minutes]
        ,[ifsp_type]
        ,[ifsp_minutes]
    from
        CaseManagementProgressNote
) as cp
unpivot
(
    servType for servTypes in ([preEnrollment_type],[screening_type],[ifsp_type])
) as up1

你会看到

╔═════════╦═════════════╦════════════════════════╦═══════════════════════╦═══════════════╦═══════════╦════════════════════╗
║ recid   ║ caseNumber  ║ preEnrollment_minutes  ║ screeningEnA_minutes  ║ ifsp_minutes  ║ servType  ║     servTypes      ║
╠═════════╬═════════════╬════════════════════════╬═══════════════════════╬═══════════════╬═══════════╬════════════════════╣
║ 143039  ║     964699  ║ (null)                 ║                   45  ║           20  ║ -NA-      ║ preEnrollment_type ║
║ 143039  ║     964699  ║ (null)                 ║                   45  ║           20  ║ SC        ║ screening_type     ║
║ 143039  ║     964699  ║ (null)                 ║                   45  ║           20  ║ TCM FF    ║ ifsp_type          ║
╚═════════╩═════════════╩════════════════════════╩═══════════════════════╩═══════════════╩═══════════╩════════════════════╝

从这里应该清楚第二个 UNPIVOT 操作做了什么,为什么它给你它的结果:要从中得到你想要的结果,你 需要旋转。 UNPIVOT 将列转换为行。那不是你要找的。您已经有了想要的行。您想要的是将所有三个 minutes 列放在一个单独的列中,具体取决于 servTypes。有很多方法可以做到这一点,例如通过向 SELECT 列表添加一个表达式,如下所示:

CASE servType
WHEN 'preEnrollment_type' THEN preEnrollment_minutes
WHEN 'screening_type' THEN screeningEnA_minutes
WHEN 'ifsp_type' THEN isfp_minutes
END

或者使用@ander2ed 的方法并完全删除 UNPIVOT,如果您不介意它不会过滤掉 NULLs。

您 link 的文章也涵盖了这个问题:

The only complication here is matching the output phone to the corresponding phone type - for this we need to do some string interrogation to ensure that Phone1 matches to PhoneType1, Phone2 matches to PhoneType2, etc.

做第二个UNPIVOT,然后过滤结果就解决了。您可以通过 linking servTypesminutess 使其工作。在您的特定示例数据中,它们的第一个字符足以识别,并且在两列中相同,因此您可以将 where left(servTypes, 1) = left(minutess, 1) 添加到您的查询中。

这对我来说似乎毫无意义地复杂,我不会推荐它,但这是文章和您的查询之间的区别,这就是您的查询在文章有效时无效的原因。