简体   繁体   English

MS SQL中的CrossTab查询/数据透视表?

[英]CrossTab Query / Pivot Table in MS SQL?

I have a table that has the follow data structure: 我有一个具有以下数据结构的表:

 terminal    |  load_time_mns  |  vehicle
_________________________________________
 Terminal 1  |   3             |   AA
 Terminal 2  |  10             |   AF
 Terminal 1  |   1             |   BF
 Terminal 6  |   3             |   QRS
 Terminal 6  |   1.4           |   AA
 Terminal 3  |   2.5           |   OP

I am trying to get an interval breakdown of load time from each terminal.For example, for the above table, I am trying to create a breakdown that looks like the following: 我正在尝试从每个终端获取负载时间的间隔细分,例如,对于上表,我试图创建一个类似于以下内容的细分:

terminal     | [0-1 mns]    |  [1-2 mns]    |    [2-3 mns]    |
_______________________________________________________________
Terminal 1   |    0         |     1         |       1    
_______________________________________________________________
Terminal 2   |    0         |     0         |       0     
_______________________________________________________________
Terminal 3   |    0         |     0         |       1
_______________________________________________________________
Terminal 6   |    0         |     1         |       1        

After a bit of Googling, it looks like I should be focusing on the pivot() function and crosstab queries. 经过一番谷歌搜索之后,看来我应该专注于pivot()函数和交叉表查询。 I am reading up on those two, but am still not quite able to get 我正在阅读这两个内容,但仍然不太能理解

Something like this might help: 这样的事情可能会有所帮助:

Query 1 : 查询1

SELECT
  terminal,
  count(CASE WHEN load_time_mns >= 0 AND load_time_mns < 1 THEN 1 END) [0-1 mns],
  count(CASE WHEN load_time_mns >= 1 AND load_time_mns < 2 THEN 1 END) [1-2 mns],
  count(CASE WHEN load_time_mns >= 2 AND load_time_mns < 3 THEN 1 END) [2-3 mns]
FROM t
GROUP BY terminal

Results : 结果

|   TERMINAL | 0-1 MNS | 1-2 MNS | 2-3 MNS |
|------------|---------|---------|---------|
| Terminal 1 |       0 |       1 |       0 |
| Terminal 2 |       0 |       0 |       0 |
| Terminal 3 |       0 |       0 |       1 |
| Terminal 6 |       0 |       1 |       0 |

Fiddle here . 在这里摆弄。

Note that in your example you did not include 1 in the [0-1] range but you did include 3 in the [0-3] range, which seems not right. 请注意,在您的示例中,您没有在[0-1]范围内包含1 ,但在[0-3]范围内包含3 ,这似乎不正确。

You can use a bit of dynamic SQL to extend this to the complete result set. 您可以使用一些动态SQL将其扩展到完整的结果集。 Create a table called intervals to store the intervals: 创建一个名为interval的表来存储间隔:

Create Table ex (
    terminal varchar(10),
    load_time_mns decimal(10, 2),
    vehicle varchar(3)
);

Insert Into ex values
    ('Terminal 1', 3, 'AA'),
    ('Terminal 2', 10, 'AF'),
    ('Terminal 1', 1, 'BF'),
    ('Terminal 6', 3, 'QRS'),
    ('Terminal 6', 1.4, 'AA'),
    ('Terminal 3', 2.5, 'OP');

Create Table intervals (
    min_mns decimal(10, 2),
    max_mns decimal(10, 2),
    column_name sysname
);

declare @i int = 0
while @i <= 20
begin
    insert into intervals values (
        @i, @i + 1, convert(varchar, @i) + '-' + convert(varchar, @i + 1)
    );
    set @i += 1;
end

while @i <= 420
begin
    insert into intervals values (
        @i, @i + 5, convert(varchar, @i) + '-' + convert(varchar, @i + 5)
    );
    set @i += 5;
end

You can then use a cursor to build up the complete SQL 然后,您可以使用游标建立完整的SQL

declare 
    @sql nvarchar(max) = N'select terminal',
    @lo int, @hi int, @col sysname;

declare pivot_cursor cursor local fast_forward for
select
    min_mns, max_mns, column_name
from
    intervals
order by
    min_mns;

open pivot_cursor;

fetch next from pivot_cursor into @lo, @hi, @col;

while @@fetch_status = 0
begin
    set @sql += ', sum(case when load_time_mns >= ' + convert(varchar, @lo) 
    + ' and load_time_mns < ' + convert(varchar, @hi) 
    + ' then 1 else 0 end) as [' + @col + ']';

    fetch next from pivot_cursor into @lo, @hi, @col;
end

close pivot_cursor;
deallocate pivot_cursor;

Set @sql += ' from ex group by Terminal order by terminal';

exec sp_executesql @sql;

Example SQLFiddle 示例SQLFiddle

DECLARE @t TABLE ( terminal VARCHAR(10), load_time_mns DECIMAL(5,2), vehicle VARCHAR(3))

INSERT INTO @t ( terminal, load_time_mns, vehicle )
VALUES 
('Terminal 1'  ,   3             ,   'AA'),
('Terminal 2'  ,  10             ,   'AF'),
('Terminal 2'  ,  20             ,   'AF'),
('Terminal 1'  ,   1             ,   'BF'),
('Terminal 1'  ,   25             ,   'BF'),
('Terminal 6'  ,   3             ,   'QRS'),
('Terminal 6'  ,   1.4           ,   'AA'),
('Terminal 3'  ,   2.5           ,   'OP')

;WITH intervals AS
(
    SELECT m = 0, n = 1 
    UNION ALL
    SELECT CASE WHEN m < 20 THEN m+1 ELSE m+5 END, CASE WHEN n < 20 THEN n+1 ELSE n+5 END
    FROM intervals
    WHERE n<420
)
SELECT terminal, load_time_mns, vehicle, 
    interval = CAST(m AS VARCHAR(10)) + '-' + CAST(n AS VARCHAR(10)), m
INTO ##tmp
FROM intervals i
LEFT JOIN @t t 
  ON t.load_time_mns >= i.m 
 AND t.load_time_mns < i.n 
OPTION (MAXRECURSION 0)


DECLARE @cols VARCHAR(MAX) = 
    STUFF(CAST((SELECT ',' + QUOTENAME(interval)
           FROM (
            SELECT DISTINCT interval, m
            FROM ##tmp
           ) t
           ORDER BY m
           FOR XML PATH(''), TYPE
          ) AS VARCHAR(MAX)),1,1,'')

DECLARE @sql VARCHAR(MAX) = '
    SELECT terminal, ' + @cols + '
    FROM (
        SELECT terminal, vehicle, interval
        FROM ##tmp
    ) t
    PIVOT (
        COUNT(vehicle)
        FOR interval IN (' + @cols + ')
    ) p
'

EXEC(@sql)

DROP TABLE ##tmp

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

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