SQL Server 2008中将行值作为列返回的SQL查询

3
我有些困难,无法确定如何返回查询结果,其中我想将行值转换为列。
简而言之,在 SQL Server 2008 中,这是我的当前模式示例:

enter image description here

这是我想要查询结果看起来的一个例子:

enter image description here

这里是SQLFiddle.com,可以进行操作 - http://sqlfiddle.com/#!6/6b394/1/0 关于模式的一些有用注释:
  • 该表每天将始终包含两行
  • 一个行为苹果,另一个行为橙子
  • 我正在试图将每对两行合并为一行
  • 为此,我需要将行值转换为它们自己的列,但仅需要复制[NumOffered]、[NumTaken]、[NumAbandoned]、[NumSpoiled]的值 - 不需要复制每个这两行的每个列,如示例所示
如您所见,从所需的最终结果图像中可以看出,两行被合并,您可以看到每个值都有自己的列和相关名称。
我看过几个实现这一目标的例子,但都不太适用于我的需求。我看到了使用分组和 Case 方法的例子。我看到了许多使用 PIVOT 的例子,甚至还有一些在 SQL 中创建自定义函数的例子。我不确定哪种方法对我来说最好。能给我一些见解吗?

在你的“真实”版本中,你只会有两个项目 - 苹果和橙子吗?你确定每个值都会有一个条目吗?也就是说,永远不会出现只有一个苹果条目而没有橙子的情况吗? - Taryn
非常好的问题。据我所知,在真实版本中,只会有两种水果。然而,我想要一个可以容纳多种水果的示例,甚至可能出现一天中可能有多行或只有一行的情况。 - Armin
4个回答

4

有许多不同的方法可以获得结果。多个 JOIN、unpivot/pivot 或带有聚合函数的 CASE 都有优缺点,因此您需要决定哪种方法最适合您的情况。

多个 JOIN - 现在您已经说明每天都会有 2 行 - 苹果和橙子。当多次连接表时,您需要某种列进行连接。似乎该列是 timestamp,但如果您只有一行数据,则会发生什么。那么 INNER JOIN 解决方案提供者 @Becuzz 就不起作用了,因为它只会返回每天都有两个条目的行。 您可以使用多个 JOIN 使用 FULL JOIN,即使每天只有一个条目,也会返回数据:

select 
  [Timestamp] = Coalesce(a.Timestamp, o.Timestamp),
  ApplesNumOffered = a.[NumOffered],
  ApplesNumTaken = a.[NumTaken],
  ApplesNumAbandoned = a.[NumAbandoned],
  ApplesNumSpoiled = a.[NumSpoiled],
  OrangesNumOffered = o.[NumOffered],
  OrangesNumTaken = o.[NumTaken],
  OrangesNumAbandoned = o.[NumAbandoned],
  OrangesNumSpoiled = o.[NumSpoiled]
from
(
  select timestamp, numoffered, NumTaken, numabandoned, numspoiled
  from myTable
  where FruitType = 'Apple'
) a
full join
(
  select timestamp, numoffered, NumTaken, numabandoned, numspoiled
  from myTable
  where FruitType = 'Orange'
) o 
  on a.Timestamp = o.Timestamp
order by [timestamp];

请参见 带演示的SQL Fiddle。多重连接的另一个问题是如果您有超过2个值,您需要为每个值添加一个附加连接。
如果您只有有限数量的值,则建议使用聚合函数和CASE表达式来获取结果:
SELECT 
  [timestamp],
  sum(case when FruitType = 'Apple' then NumOffered else 0 end) AppleNumOffered,
  sum(case when FruitType = 'Apple' then NumTaken else 0 end) AppleNumTaken,
  sum(case when FruitType = 'Apple' then NumAbandoned else 0 end) AppleNumAbandoned,
  sum(case when FruitType = 'Apple' then NumSpoiled else 0 end) AppleNumSpoiled,
  sum(case when FruitType = 'Orange' then NumOffered else 0 end) OrangeNumOffered,
  sum(case when FruitType = 'Orange' then NumTaken else 0 end) OrangeNumTaken,
  sum(case when FruitType = 'Orange' then NumAbandoned else 0 end) OrangeNumAbandoned,
  sum(case when FruitType = 'Orange' then NumSpoiled else 0 end) OrangeNumSpoiled
FROM myTable
group by [timestamp];

请查看带演示的SQL Fiddle。或者像@M.Ali一样使用PIVOT/UNPIVOT。这些方法的问题在于如果你有未知的值——意味着不仅仅是AppleOrange,那么你只能使用动态SQL来获取结果。动态SQL将创建一个需要由引擎执行的SQL字符串:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(FruitType + col) 
                    from
                    (
                      select FruitType
                      from myTable
                    ) d
                    cross apply
                    (
                      select 'NumOffered', 0 union all
                      select 'NumTaken', 1 union all
                      select 'NumAbandoned', 2 union all
                      select 'NumSpoiled', 3
                    ) c (col, so)
                    group by FruitType, Col, so
                    order by FruitType, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT TimeStamp,' + @cols + ' 
            from 
            (
               select TimeStamp,
                new_col = FruitType+col, value
               from myTable
               cross apply
               (
                 select ''NumOffered'', NumOffered union all
                 select ''NumTaken'', NumOffered union all
                 select ''NumAbandoned'', NumOffered union all
                 select ''NumSpoiled'', NumOffered
               ) c (col, value)
            ) x
            pivot 
            (
                sum(value)
                for new_col in (' + @cols + ')
            ) p '

exec sp_executesql @query;

请查看带演示的SQL Fiddle

所有版本均给出以下结果:

|                 timestamp | AppleNumOffered | AppleNumTaken | AppleNumAbandoned | AppleNumSpoiled | OrangeNumOffered | OrangeNumTaken | OrangeNumAbandoned | OrangeNumSpoiled |
|---------------------------|-----------------|---------------|-------------------|-----------------|------------------|----------------|--------------------|------------------|
| January, 01 2015 00:00:00 |              55 |            12 |                 0 |               0 |               12 |              5 |                  0 |                1 |
| January, 02 2015 00:00:00 |              21 |             6 |                 2 |               1 |               60 |             43 |                  0 |                0 |
| January, 03 2015 00:00:00 |              49 |            17 |                 2 |               1 |              109 |             87 |                 12 |                1 |
| January, 04 2015 00:00:00 |               6 |             4 |                 0 |               0 |               53 |             40 |                  0 |                1 |
| January, 05 2015 00:00:00 |              32 |            14 |                 1 |               0 |               41 |             21 |                  5 |                0 |
| January, 06 2015 00:00:00 |              26 |            24 |                 0 |               1 |               97 |             30 |                 10 |                1 |
| January, 07 2015 00:00:00 |              17 |             9 |                 2 |               0 |               37 |             27 |                  0 |                4 |
| January, 08 2015 00:00:00 |              83 |            80 |                 3 |               0 |              117 |            100 |                  5 |                1 |

1
根据您的标准,将两个伴侣行合并并选择适当的字段似乎是最简单的答案。您可以使用PIVOT、UNION和GROUP BY等方法,但这似乎过于复杂。
select apples.Timestamp
        , apples.[NumOffered] as ApplesNumOffered
        , apples.[NumTaken] as ApplesNumTaken
        , apples.[NumAbandoned] as ApplesNumAbandoned
        , apples.[NumSpoiled] as ApplesNumSpoiled
        , oranges.[NumOffered] as OrangesNumOffered
        , oranges.[NumTaken] as OrangesNumTaken
        , oranges.[NumAbandoned] as OrangesNumAbandoned
        , oranges.[NumSpoiled] as OrangesNumSpoiled
from myTable apples
inner join myTable oranges on oranges.Timestamp = apples.Timestamp
where apples.FruitType = 'Apple'
and oranges.FruitType = 'Orange'

-1

查询

;WITH CTE AS
(SELECT [Timestamp]
      ,FruitType + EventType AS Cols
      ,Qty
from myTable t
  UNPIVOT (Qty FOR EventType IN (NumOffered ,NumTaken
                                 ,NumAbandoned,NumSpoiled))up
)
SELECT * FROM CTE
  PIVOT (SUM(Qty) FOR Cols IN (AppleNumOffered,AppleNumTaken
                              ,AppleNumAbandoned,AppleNumSpoiled
                              ,OrangeNumOffered, OrangeNumTaken
                              ,OrangeNumAbandoned,OrangeNumSpoiled))p

结果

╔═════════════════════════╦═════════════════╦═══════════════╦═══════════════════╦═════════════════╦══════════════════╦════════════════╦════════════════════╦══════════════════╗
║        Timestamp        ║ AppleNumOffered ║ AppleNumTaken ║ AppleNumAbandoned ║ AppleNumSpoiled ║ OrangeNumOffered ║ OrangeNumTaken ║ OrangeNumAbandoned ║ OrangeNumSpoiled ║
╠═════════════════════════╬═════════════════╬═══════════════╬═══════════════════╬═════════════════╬══════════════════╬════════════════╬════════════════════╬══════════════════╣
║ 2015-01-01 00:00:00.00055120012501 ║
║ 2015-01-02 00:00:00.00021621604300 ║
║ 2015-01-03 00:00:00.00049172110987121 ║
║ 2015-01-04 00:00:00.0006400534001 ║
║ 2015-01-05 00:00:00.000321410412150 ║
║ 2015-01-06 00:00:00.0002624019730101 ║
║ 2015-01-07 00:00:00.00017920372704 ║
║ 2015-01-08 00:00:00.00083803011710051 ║
╚═════════════════════════╩═════════════════╩═══════════════╩═══════════════════╩═════════════════╩══════════════════╩════════════════╩════════════════════╩══════════════════╝

{{link1:SQL FIDDLE}}


(请注意,这是一个HTML代码段,直接翻译可能不完全准确,建议在上下文中使用)

-2

M.Ali比我先说了。在这些类型的转换中,UNPIVOT和PIVOT是您的好朋友。

每当我需要刷新关于UNPIVOT和PIVOT的记忆时,我都会多次使用这个article


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接