sql 之 rollup, cube, groupset简介

一下为对参考帖子的理解,谢谢!
----------------------------------------
  • rollup:对group进行小计
例如
数据:
CREATE TABLE Sales (EmpId INT, Yr INT, Sales MONEY)
INSERT Sales VALUES(1, 2005, 12000)
INSERT Sales VALUES(1, 2006, 18000)
INSERT Sales VALUES(1, 2007, 25000)
INSERT Sales VALUES(2, 2005, 15000)
INSERT Sales VALUES(2, 2006, 6000)
INSERT Sales VALUES(3, 2006, 20000)
INSERT Sales VALUES(3, 2007, 24000)

语句:
SELECT Yr, SUM(Sales) AS Sales
FROM Sales
GROUP BY Yr

得到结果,注意没有总计:
Yr          Sales
----------- ---------------------
2005        27000.00
2006        44000.00
2007        49000.00
若要得到总计,则:
SELECT Yr, SUM(Sales) AS Sales
FROM Sales
GROUP BY Yr
UNION ALL
SELECT NULL, SUM(Sales) AS Sales
FROM Sales
结果
Yr          Sales
----------- ---------------------
2005        27000.00
2006        44000.00
2007        49000.00
NULL        120000.00
但上面效率不高,sales表会扫描2次;

用WITH ROLLUP,会方便得得到上面的数据:
SELECT
      CASE WHEN GROUPING(Yr) = 0
            THEN CAST (Yr AS CHAR(5))
            ELSE 'ALL'
      END AS Yr,
      SUM(Sales) AS Sales
FROM Sales
GROUP BY Yr WITH ROLLUP
Yr    Sales
----- ---------------------
2005  27000.00
2006  44000.00
2007  49000.00
ALL   120000.00
进一步,对每个雇员进行小结:
SELECT EmpId, Yr, SUM(Sales) AS Sales
FROM Sales
GROUP BY EmpId, Yr WITH ROLLUP
EmpId       Yr          Sales
----------- ----------- ---------------------
1           2005        12000.00
1           2006        18000.00
1           2007        25000.00
1           NULL        55000.00
2           2005        15000.00
2           2006        6000.00
2           NULL        21000.00
3           2006        20000.00
3           2007        24000.00
3           NULL        44000.00
NULL        NULL        120000.00
总结起来,若将上面的结果行列转置:

  • cube
但,若需要按Yr统计,得到下面的数据, 则需要用到cube
SELECT EmpId, Yr, SUM(Sales) AS Sales
FROM Sales
GROUP BY EmpId, Yr WITH CUBE
得到结果
EmpId       Yr          Sales
----------- ----------- ---------------------
1           2005        12000.00
1           2006        18000.00
1           2007        25000.00
1           NULL        55000.00
2           2005        15000.00
2           2006        6000.00
2           NULL        21000.00
3           2006        20000.00
3           2007        24000.00
3           NULL        44000.00
NULL        NULL        120000.00
NULL        2005        27000.00
NULL        2006        44000.00
NULL        2007        49000.00
下面sql语句将上面结果pivot,
SELECT EmpId, [2005], [2006], [2007], [ALL]
FROM
      (
      SELECT
            CASE WHEN GROUPING(EmpId) = 0
                  THEN CAST (EmpId AS CHAR(7))
                  ELSE 'ALL'
            END AS EmpId,
            CASE WHEN GROUPING(Yr) = 0
                  THEN CAST (Yr AS CHAR(7))
                  ELSE 'ALL'
            END AS Yr,
            SUM(Sales) AS Sales
      FROM Sales
      GROUP BY EmpId, Yr WITH CUBE
      ) AS s
PIVOT (SUM(Sales) FOR Yr IN ([2005], [2006], [2007], [ALL])) AS p

EmpId   2005                  2006                  2007                  ALL
------- --------------------- --------------------- --------------------- ---------------------
1       12000.00              18000.00              25000.00              55000.00
2       15000.00              6000.00               NULL                  21000.00
3       NULL                  20000.00              24000.00              44000.00
ALL     27000.00              44000.00              49000.00              120000.00

GROUPSET
rollup和cub会统计所有维度,groupset用来指定维度

PCI乱弹

    PCI总线的特点之一是,PCI设备的IO地址根据机器的情况自动配置的。也即,同一PCI卡插在不同配置的机器上,其IO地址可能是变化的。对于使用者而言,无需配置,非常方便。那PCI规范是怎么做到的呢?
    每个PCI设备有个基址寄存器(BAR),向其写全1后回读,则会得到该PCI设备的IO空间长度。得到了系统中每个设备的IO空间长度,则OS系统软件可以安排每个设备的IO基址,然后写入BAR寄存器,则驱动程序可以获得该设备的IO基址。
    那么怎么寻址到该设备呢?PCI总线中,每个PCI设备有个地址,其信息包括:BUS/SLOT/Function。PCI总线在X86的IO空间中保留了2个PORT,0xCF8(配置地址寄存器)和0xCFC(配置数据寄存器).配置地址寄存器指明了访问哪个设备(BUS/SLOT/Function)的哪个寄存器的第几个字节。
        
PCI配置空间大小:256字节;
PCI配置空间位置:和所在PCI slot有关,PCI初始化时会探测并并配置;
如何探测:对于某个总线,挨个枚举所有设备的配置空间,读取PCI配置头的某些字段(比如Vendor和DeviceID),若返回)0xFFFFFFFF,则slot是空的。

Q: linux如何扫描pci bus?
A: 深度优先遍历,why?因为要决定桥的subordinary bus号

Q: linux如何知道哪个slot被使用了?
A: 读取其配置头部的Vendor和DeviceID字段,返回全1则说明slot是空的

Q: 如何判断一个PCI设备是否为桥?
A: 桥设备的class code为0x060400
配置空间头类型字段会表明它是pci设备或桥

Q: linux如何知道一个BAR是映射到I/O空间还是memory空间?
A: BAR的第0位会标识
pci设备必须知道自己的bus号才能响应配置事务,那它如何知道自己的bus号?


Q: Linux为PCI设备分配的Port空间和MMIO空间是cpu可以直接访问的?还是PCI地址?
A:  从OS的角度来看,使用的是要经过页表翻译的虚拟机地址,
从CPU角度看,使用的存储器域地址,经过HOST主桥翻译成PCI总线地址,通过HOST主桥发起读写事务到PCI设备

Q: posted和noposted有什么区别?什么是delay方式?什么是split方式?
A: posted是异步方式,PCI桥会接管事务,而Non-posted是同步方式,占用事务经过的所有总线带宽。
只有存储器写是Posted方式, IO读写,配置读写,存储器读都是Non-posted方式.
Delay是对nonposted的优化。当PCI桥收到主桥发起Non-posted事务时,向主桥发起retry周期,让主桥择时重新发起相同周期(non-post事务),同时PCI桥向下发其delay reqeust事务,PCI设备接收到dalay request之后,向PCI桥回应Retry。当PCI设备准备好数据后,再次收到delay request之后,发起delay complete周期,传输数据. PCI桥再次接收到NonPosted请求之后,回应数据完成传递给主桥,从而完成数据传输。可以看到retry方式可以释放上级总线。
split方式实际讲non-posted事务转化为post事务。比如Non-posted'读,现将“读请求”用Post方式发送给目标设备,当目标设备准备好数据后,在用Posted方式把数据传递给请求者。角色分为Requestor和Completor。


Q: PCI设备如何标识它自己使用的是哪个中断信号INTx?它和PCI总线的INTx是什么关系?
A: PCI配设备的配置空间中的InterruptPin字段标明设备的中断信号,和PCI总线的对应关系,PCI规范推荐根据PCI device号映射。算法 (device_id + x)mod 4, device_id-0-31, x=0-3, 分别代表A#-D#.
设备号
PCI设备的INTx#信号
PCI总线的INTx#信号
0, 4, 8, 12, 16, 20, 24, 28
INTA#
INTA#
INTB#
INTB#
INTC#
INTC#
INTD#
INTD#
1, 5, 9, 13, 17, 21, 25, 29
INTA#
INTB#
INTB#
INTC#
INTC#
INTD#
INTD#
INTA#
2, 6, 10, 14, 18, 22, 26, 30
INTA#
INTC#
INTB#
INTD#
INTC#
INTA#
INTD#
INTB#
3, 7, 11, 15, 19, 23, 27, 31
INTA#
INTD#
INTB#
INTA#
INTC#
INTB#
INTD#
INTC#

 Q: INTx和数据传输是异步的,如何确认数据已经传输完成?
读刷新,利用PCI总线的传送序规则。读取PCI设备的状态寄存器。

Q:INTx和MSI中断的利弊
INTx 边带信号,和数据传输是异步的。ISR中特别注意。
MSI  占用总线带宽;但是同步,可以表明中断原因。
 
Q: TYPE0配置事务和TYPE1配置事务有什么区别?
A: Type0,主桥或PCI桥访问与其直连的PCI Agent(设备或桥), TYPE1,主桥或PCI桥闯过一个或多个桥访问PCI-Agent(设备或桥)

Hello World!

It's my blog! Keep GOING!!