【问题标题】:How to UNPIVOT a table in SQL Server?如何在 SQL Server 中取消透视表?
【发布时间】:2015-09-26 13:53:08
【问题描述】:

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

下面是我的代码和结果截图。 (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 数据,底部是实际表格。

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

我需要

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

还要考虑到我还有更多列可供选择。

这是我使用的参考mssqltips

SQL Fiddle 上面的例子。

【问题讨论】:

  • 如果你添加了一个 SQL Fiddle ,你很有可能会得到一个有用的答案。

标签: sql sql-server pivot unpivot


【解决方案1】:

您似乎认为您的两个UNPIVOT 操作以某种方式联系在一起。它们不是,除了第二个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 ║ screenshotEnA_minutes ║ ifsp_minutes ║ servType ║ servTypes ║
╠═════════╬═════════════╬════════════════════════╬ ═══════════════════════╬═══════════════╬══════════ ═╬════════════════════╣
║ 143039 ║ 964699 ║ (null) ║ 45 ║ 20 ║ -NA- ║ preEnrollment_type ║
║ 143039 ║ 964699 ║ (null) ║ 45 ║ 20 ║ SC ║ 筛选类型 ║
║ 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。

您链接到的文章也涵盖了这个问题:

这里唯一的复杂之处是将输出电话与相应的电话类型匹配 - 为此我们需要进行一些字符串查询以确保 Phone1 与 PhoneType1 匹配,Phone2 与 PhoneType2 匹配,等等。

它通过执行第二个UNPIVOT 来解决它,然后过滤结果。您可以通过链接servTypesminutess 使其工作。在您的特定样本数据中,它们的第一个字符足以识别,并且在两列中是相同的,因此您可以在查询中添加where left(servTypes, 1) = left(minutess, 1)

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

【讨论】:

  • 我添加了case 声明,效果很好!!...感谢您的解释
【解决方案2】:

您可以在此处使用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)

【讨论】:

  • 我喜欢这个查询中的语法,但是它不能在 SQL Server 中运行。它确实在 SQL Fiddle 中运行。 SQL Server 给我以下错误Incorrect syntax near the keyword 'VALUES'.
  • 有趣。您使用的是什么版本的 SQL?它适用于 Microsoft SQL Server 2008 r2。
  • SQL Server Studio 2012 在我的电脑中,但实际的数据库在 SQL Server 2005 中
  • 我明白了。我会怀疑 cross apply (values ()) 直到 SQL Server 2008 才被支持。这很不幸。很高兴你从@hvd 找到答案:)
猜你喜欢
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 2018-03-24
  • 2019-10-19
相关资源
最近更新 更多