简体   繁体   English

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

[英]SQL query for returning row values as columns - SQL Server 2008

I'm having trouble figuring out how to return a query result where I have row values that I want to turn into columns. 我无法弄清楚如何返回查询结果,其中我有行值,我想变成列。

In short, here is an example of my current schema in SQL Server 2008: 简而言之,这是我在SQL Server 2008中的当前架构的示例:

在此输入图像描述

And here is an example of what I would like the query result to look like: 以下是我希望查询结果如下所示的示例:

在此输入图像描述

Here is the SQLFiddle.com to play with - http://sqlfiddle.com/#!6/6b394/1/0 这是可以使用的SQLFiddle.com - http://sqlfiddle.com/#!6/6b394/1/0

Some helpful notes about the schema: 有关架构的一些有用说明:

  • The table will always contain two rows for each day 该表每天总是包含两行
  • One row for Apples, and one row for Oranges 苹果一行,橘子一行
  • I am trying to combine each pair of two rows into one row 我试图将每对两行合并为一行
  • To do that I need to convert the row values into their own columns, but only the values for [NumOffered], [NumTaken], [NumAbandoned], [NumSpoiled] - NOT every column for each of these two rows needs to be duplicated, as you can see from the example 要做到这一点,我需要将行值转换为它们自己的列,但只有[NumOffered],[NumTaken],[NumAbandoned],[NumSpoiled]的值 - 不是每列中的每一列都需要重复,正如您从示例中看到的那样

As you can see, from the example desired end result image, the two rows are combined, and you can see each value has its own column with a relevant name. 如您所见,从示例所需的最终结果图像中,将两行组合在一起,您可以看到每个值都有自己的具有相关名称的列。

I've seen several examples of how this is possible, but not quite applicable for my purposes. 我已经看到了几个可能的例子,但不太适用于我的目的。 I've seen examples using Grouping and Case methods. 我见过使用分组和案例方法的例子。 I've seen many uses of PIVOT, and even some custom function creation in SQL. 我已经看到了PIVOT的许多用途,甚至在SQL中创建了一些自定义函数。 I'm not sure which is the best approach for me. 我不确定哪种方法最适合我。 Can I get some insight on this? 我可以对此有所了解吗?

There are many different ways that you can get the result. 您可以通过许多不同的方式获得结果。 Multiple JOINs, unpivot/pivot or CASE with aggregate. 多个JOIN,unpivot / pivot或带聚合的CASE。

They all have pros and cons, so you'll need to decide what will work best for your situation. 它们都有利有弊,所以你需要决定哪种方法最适合你的情况。

Multiple Joins - now you've stated that you will always have 2 rows for each day - one for apple and orange. 多个连接 - 现在您已经声明每天总共有2行 - 一个用于苹果和橙色。 When joining on the table multiple times your need some sort of column to join on. 多次加入桌面时,您需要某种列加入。 It appears the column is timestamp but what happens if you have a day that you only get one row. 该列似乎是timestamp但如果您有一天只能获得一行,会发生什么。 Then the INNER JOIN solution provided by @Becuzz won't work because it will only return the rows with both entries per day. 然后@Becuzz提供的INNER JOIN解决方案将无法工作,因为它只会返回每天包含两个条目的行。 LeYou could use multiple JOINs using a FULL JOIN which will return the data even if there is only one entry per day: LeYou可以使用FULL JOIN使用多个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];

See SQL Fiddle with Demo . 请参阅SQL Fiddle with Demo Another issue with multiple joins is what if you have more than 2 values, you'll need an additional join for each value. 多个连接的另一个问题是,如果您有两个以上的值,则每个值都需要一个额外的连接。

If you have a limited number of values then I'd suggest using an aggregate function and a CASE expression to get the result: 如果您的值有限,那么我建议使用聚合函数和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];

See SQL Fiddle with Demo . 请参阅SQL Fiddle with Demo Or even using PIVOT/UNPIVOT like @M.Ali has. 甚至使用像@ M.Ali这样的PIVOT / UNPIVOT The problem with these are what if you have unknown values - meaning more than just Apple and Orange . 如果你有未知的价值 - 这意味着不仅仅是AppleOrange You are left with using dynamic SQL to get the result. 您将使用动态SQL来获取结果。 Dynamic SQL will create a string of sql that needs to be execute by the engine: 动态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;

See SQL Fiddle with Demo 请参阅SQL Fiddle with Demo

All versions give the result: 所有版本都给出了结果:

|                 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 |

Given your criteria, joining the two companion rows together and selecting out the appropriate fields seems like the simplest answer. 根据您的标准,将两个伴随行连接在一起并选择适当的字段似乎是最简单的答案。 You could go the route of PIVOTs, UNIONs and GROUP BYs, etc., but that seems like overkill. 你可以去PIVOTs,UNIONs和GROUP BYs等路线,但这看起来有点过分。

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'

Query 询问

;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

Result 结果

╔═════════════════════════╦═════════════════╦═══════════════╦═══════════════════╦═════════════════╦══════════════════╦════════════════╦════════════════════╦══════════════════╗
║        Timestamp        ║ AppleNumOffered ║ AppleNumTaken ║ AppleNumAbandoned ║ AppleNumSpoiled ║ OrangeNumOffered ║ OrangeNumTaken ║ OrangeNumAbandoned ║ OrangeNumSpoiled ║
╠═════════════════════════╬═════════════════╬═══════════════╬═══════════════════╬═════════════════╬══════════════════╬════════════════╬════════════════════╬══════════════════╣
║ 2015-01-01 00:00:00.000 ║              55 ║            12 ║                 0 ║               0 ║               12 ║              5 ║                  0 ║                1 ║
║ 2015-01-02 00:00:00.000 ║              21 ║             6 ║                 2 ║               1 ║               60 ║             43 ║                  0 ║                0 ║
║ 2015-01-03 00:00:00.000 ║              49 ║            17 ║                 2 ║               1 ║              109 ║             87 ║                 12 ║                1 ║
║ 2015-01-04 00:00:00.000 ║               6 ║             4 ║                 0 ║               0 ║               53 ║             40 ║                  0 ║                1 ║
║ 2015-01-05 00:00:00.000 ║              32 ║            14 ║                 1 ║               0 ║               41 ║             21 ║                  5 ║                0 ║
║ 2015-01-06 00:00:00.000 ║              26 ║            24 ║                 0 ║               1 ║               97 ║             30 ║                 10 ║                1 ║
║ 2015-01-07 00:00:00.000 ║              17 ║             9 ║                 2 ║               0 ║               37 ║             27 ║                  0 ║                4 ║
║ 2015-01-08 00:00:00.000 ║              83 ║            80 ║                 3 ║               0 ║              117 ║            100 ║                  5 ║                1 ║
╚═════════════════════════╩═════════════════╩═══════════════╩═══════════════════╩═════════════════╩══════════════════╩════════════════╩════════════════════╩══════════════════╝

SQL FIDDLE

M.Ali beat me too it. M.Ali也打败了我。 UNPIVOT and PIVOT are your friends with these types of transformations. UNPIVOT和PIVOT是您与这些类型的转换的朋友。

I've used this article multiple times when refreshing my memory with UNPIVOT and PIVOT. 我用UNPIVOT和PIVOT刷新记忆时多次使用过这篇文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM