瑞丽的SQL-SQL Server的表旋转(行列转换)

本文详细介绍了SQL Server中的表旋转技术,包括PIVOT和UNPIVOT运算符的使用方法,以及如何利用CASE表达式实现类似的行列转换效果。通过具体案例展示了如何将表的行转换为列或将列转换为行,以解决数据展示和存储之间的矛盾。

所谓表旋转,就是将表的行转换为列,或是将表的列转换为行,这是从SQL Server 2005開始提供的新技术。因此,如果希望使用此功能,须要将数据库的兼容级别设置为90。表旋转在某些方面也是攻克了表的数据存储和实际须要之间的矛盾。比如。图9-4所看到的的是一个典型的产品销售统计表,这样的格式尽管便于阅读,可是在进行数据表存储的时候却并不easy管理,产品销售数据表通常须要设计成图9-5所看到的的结构。

这样就带来一个问题,用户既希望数据easy管理。又希望能够生成一种能够easy阅读的表格数据。这时候就能够使用表旋转技术。

          

  9-4 产品销售表     

  9-5 数据表结构

9.4.1 PIVOT运算符

PIVOT运算符用于将表的行转换为列,并能同一时候对行运行聚合运算。其语法格式例如以下:

SELECT <非旋转列>,

    [第一个旋转列] AS <列名>,

    [第二个旋转列] AS <列名>,

    ...

    [最后的旋转列] AS <列名>

FROM

    (<SELECT 生成数据的查询>) AS <为源查询结果指定的别名>

 

PIVOT

(

    <聚合函数>(<被聚合的列>)

FOR

[<包括将被转换为列标头的值的列>]

    IN ( [第一个旋转后的列], [第二个旋转后的列],

    ... [最后一个旋转后的列])

) AS <为旋转表指定的别名>

 

<可选的 ORDER BY 子句>;

为了实现行的旋转。源查询获得的结果应当具备三列。才干够实现旋转。第1列是不进行旋转的列,属于标志列;第2列是属性列。也称为透视列,当中的值会被旋转列名;第3列是属性值列,这些值将作为新列的值。

使用以下的语句创建一个演示样例表Orders,内容如表9-7所看到的。

CREATE TABLE Orders

(

   ProductID int NOT NULL,

   OrderDate datetime NOT NULL,

   ShipTo char(20) NOT NULL,

   SubTotal money NOT NULL

);

INSERT INTO Orders

VALUES (1,CAST('20090102' AS datetime), 'Shanghai', 100.00),

       (1, CAST('20090105' AS datetime), 'Shanghai',100.00),

       (1, CAST('20090123' AS datetime),'Jinan',    100.00),

       (2, CAST('20090125' AS datetime),'Shanghai', 100.00),

       (1, CAST('20090205' AS datetime),'Jinan',    100.00),

       (3, CAST('20090213' AS datetime),'Shanghai', 100.00),

       (3, CAST('20090219' AS datetime),'Shanghai', 100.00),

       (4, CAST('20090309' AS datetime),'Beijing',  100.00),

       (1, CAST('20090311' AS datetime),'Dalian',   100.00),

       (2, CAST('20090324' AS datetime),'Shanghai', 100.00),

       (3, CAST('20090326' AS datetime),'Wuhan',    100.00);

9-7                                                                     Orders表的内容

ProductID

OrderDate

ShipTo

SubTotal

1

2009-01-02 00:00:00.000

Shanghai           

100.00

1

2009-01-05 00:00:00.000

Shanghai           

100.00

1

2009-01-23 00:00:00.000

Jinan              

100.00

2

2009-01-25 00:00:00.000

Shanghai           

100.00

1

2009-02-05 00:00:00.000

Jinan              

100.00

3

2009-02-13 00:00:00.000

Shanghai           

100.00

3

2009-02-19 00:00:00.000

Shanghai           

100.00

4

2009-03-09 00:00:00.000

Beijing            

100.00

1

2009-03-11 00:00:00.000

Dalian             

100.00

2

2009-03-24 00:00:00.000

Shanghai           

100.00

3

2009-03-26 00:00:00.000

Wuhan              

100.00

Orders表中包括了3个月的产品销售数据,如今如果要获得像图9-4所看到的的销售表,则对源表的查询首先须要获得上面讲的三列,參考以下的语句:

SELECT ProductID,

       MONTH(OrderDate) AS OrderMonth,

       SubTotal

FROM Orders;

查询结果如表9-8所看到的。当中ProductID为标志列,OrderMonth为属性列,当中的月份要转变为列的名称,SubTotal为属性值列,这些值将成为新列的值。

9-8                                                                   获取到的三列内容

ProductID

OrderMonth

SubTotal

1

1

100.00

1

1

100.00

1

1

100.00

2

1

100.00

1

2

100.00

3

2

100.00

3

2

100.00

4

3

100.00

1

3

100.00

2

3

100.00

3

3

100.00

完整的旋转查询语句例如以下。

查询结果如表9-9所看到的。

SELECT ProductID,

       [1]AS Jan,

       [2]AS Feb,

       [3]AS Mar

FROM (SELECT ProductID, MONTH(OrderDate) ASOrderMonth, SubTotal

      FROMOrders) AS O1

PIVOT

(

  SUM(SubTotal)

   FOROrderMonth IN ([1], [2], [3])

) AS Pvt

ORDER BY ProductID;

9-9                                                                   旋转后输出的内容

ProductID

Jan

Feb

Mar

1

300.00

100.00

100.00

2

100.00

NULL

100.00

3

NULL

200.00

100.00

4

NULL

NULL

100.00

上面的查询语句将按以下的步骤来获取表9-9所看到的的结果集:

·   PIVOT首先按属性值列之外的列(ProductIDOrderMonth)对输入表Sales.Orders进行分组汇总。相似运行以下的语句,得到一个如表9-10所看到的的中间结果集。

SELECT ProductID,

       OrderMonth,

       SUM(SubTotal) AS SubTotal

FROM (SELECT ProductID,MONTH(OrderDate) AS OrderMonth, SubTotal

      FROM Orders) AS O1

GROUP BY ProductID,OrderMonth;

9-10                                                         Orders经分组汇总后的结果

ProductID

OrderMonth

SubTotal

1

1

300.00

1

2

100.00

1

3

100.00

2

1

100.00

2

3

100.00

3

2

200.00

3

3

100.00

4

3

100.00

·   PIVOT依据FOR OrderMonth IN指定的值123。首先在结果集中建立名为123的列。然后从表9-10所看到的的中间结果中取出SubTotal列中取出相符合的值,分别放置到123列中。

此时得到的结果集的别名为pvt(见语句中AS pvt的指定)。结果集的内容如表9-11所看到的。

9-11                                   使用FOR OrderMonth IN ([1], [2], [3])后得到的结果集

ProductID

1

2

3

1

300.00

100.00

100.00

2

100.00

NULL

100.00

3

NULL

200.00

100.00

4

NULL

NULL

100.00

·   最后依据SELECT ProductID, [1] AS Jan,[2] AS Feb, [3] AS Mar FROM的指定。从别名pvt结果集中检索数据,并分别将名为123的列在终于结果集中又一次命名为JanFebMar。得到表9-9所看到的的结果集。

这里须要注意的是FROM的含义。其表示从经PIVOT关系运算符得到的pvt结果集中检索数据,而不是从Orders或派生表O1中检索数据。

SQL Server2005之前。要进行行列转换比較烦琐,你须要考虑源表中行与结果集中行的关系。属性列中的每一个唯一值在结果集中都须要一个列。

像上面表9-7中的Orders表因为包括3个月份的数据,因此在SELECT列表中须要包括3个表达式。分别用于提取3个月份中的数据。

以下语句的查询结果与表9-9同样,请读者自己分析以下的语句。

SELECT ProductID,

      SUM(CASE WHEN OrderMonth = 1 THEN SubTotal END) AS Jan,

      SUM(CASE WHEN OrderMonth = 2 THEN SubTotal END) AS Feb,

      SUM(CASE WHEN OrderMonth = 3 THEN SubTotal END) AS Mar

FROM (SELECT ProductID,

            MONTH(OrderDate) AS OrderMonth,

            SubTotal AS SubTotal

      FROMOrders) AS O1

GROUP BY ProductID;

9.4.2 UNPIVOT运算符

UNPIVOTPIVOT运行差点儿全然相反的操作。将列转换为行。可是,UNPIVOT并不全然是PIVOT的逆操作,因为在运行PIVOT过程中,数据已经被进行了分组汇总。所以使用UNPIVOT并不会重现原始表值表达式的结果。如果表9-9所看到的的结果集存储在一个名为MyPvt的表中,如今须要将列JanFebMar转换到对应于对应产品ID的行值(即返回到表9-10所看到的的格式)。

这意味着必须另外标识两个列。一个用于存储月份,一个用于存储销售额。

为了便于理解,仍旧分别将这两个列命名为OrderMonthSubTotal

以下的语句首先创建MyPvt表,然后将查询数据插入到表中。

CREATE TABLE MyPvt

(

   ProductID int NOT NULL,

   Jan money,

   Feb money,

   Mar money

);

 

INSERT INTO MyPvt(ProductID, Jan, Feb, Mar)

SELECT ProductID,

       [1] AS Jan,

       [2] AS Feb,

       [3] AS Mar

FROM (SELECT ProductID,MONTH(OrderDate) AS OrderMonth, SubTotal

      FROM Orders) AS O1

PIVOT

(

   SUM(SubTotal)

   FOR OrderMonth IN ([1], [2], [3])

) AS Pvt

ORDER BY ProductID;

以下的语句运行UNPIVOT。将得到表9-12所看到的的查询结果。

SELECT ProductID,OrderMonth, SubTotal

FROM MyPvt

UNPIVOT

(

   SubTotal FOR OrderMonth IN (Jan, Feb, Mar)

)AS UnPvt;

9-12                                                          UNPIVOT得到的查询结果

ProductID

OrderMonth

SubTotal

1

Jan

300.00

1

Feb

100.00

1

Mar

100.00

2

Jan

100.00

2

Mar

100.00

3

Feb

200.00

3

Mar

100.00

4

Mar

100.00

上面的语句将按以下的步骤获得输出结果集:

·   首先建立一个暂时结果集的结构,该结构中包括MyPvt表中除IN (Jan, Feb, Mar)之外的列。以及SubTotalFOR OrderMonth中指定的属性值列(SubTotal)和属性列(OrderMonth)。

·   然后将在MyPvt中逐行检索数据,将表的列名称放入OrderMonth列中,将对应的值放入到SubTotal列中。

因为在PIVOT时为列指定了别名。所以在UNPIVOT后。OrderMonth列中的月份使用的是英文简称。而不是表9-10所看到的的格式。

要得到表9-10所看到的的格式,能够在查询语句中使用CASE表达式来解决问题,參考以下的语句:

SELECT ProductID,

      CAST(CASE

           WHEN OrderMonth ='Jan' THEN '1'

           WHEN OrderMonth ='Feb' THEN '2'

           WHEN OrderMonth ='Mar' THEN '3'

           END AS int) AS OrderMonth,

      SubTotal

FROM MyPvt

UNPIVOT

(

   SubTotalFOR OrderMonth IN (Jan, Feb, Mar)

)AS UnPvt;

SQL Server2005之前,则应当使用以下的语句:

SELECT * FROM

  (SELECTProductID,1 AS OrderMonth, Jan AS SubTotal

   FROMMyPvt

   UNION ALL

   SELECTProductID,2 AS OrderMonth, Feb

   FROMMyPvt

   UNION ALL

   SELECT ProductID,3 AS OrderMonth, Mar

   FROMMyPvt) AS O

WHERE SubTotal IS NOT NULL;

9.4.3 CASE表达式9.4节我们介绍了使用PIVOT运算符进行表行列转换的方法,有效攻克了表的数据存储和方便阅读的矛盾问题,本节将提供另外的一种转换方式。这是在做一个考试系统时遇到的问题。以下是是创建演示样例表的语句。表中存放着学生每次各科的考试成绩。

CREATE TABLE exams

(stu_name char(10) NOT NULL,

 exam_datedate NOT NULL,

 exam_subchar(10) NOT NULL,

 exam_scoreint);

INSERT INTO exams

VALUES ('张三', '2009-06-20', '语文', 90),

       ('张三', '2009-06-20', '数学', 95),

       ('张三', '2009-06-20', '英语', 100),

       ('张三', '2009-09-20', '语文', 85),

       ('张三', '2009-09-20', '数学', 90),

       ('张三', '2009-09-20', '英语', 98),

       ('李四', '2009-06-20', '语文', 80),

       ('李四', '2009-06-20', '数学', 85),

       ('李四', '2009-06-20', '英语', 90),

       ('李四', '2009-09-20', '语文', 75),

       ('李四', '2009-09-20', '数学', 80),

       ('李四', '2009-09-20', '英语', 88);

如今要获得如表12-23所看到的的格式,每一个学生按考试日期在表中占一行。

12-23                                                                     转换后结果

 

stu_name

exam_date

语文

数学

英语

张三     

2009-06-20

90

95

100

张三     

2009-09-20

85

90

98

李四     

2009-06-20

80

85

90

李四     

2009-09-20

75

80

88

在很多情况下,都能够使用CASE表达式将表的行转换为列,这是一个很实用的技巧。參考以下的语句:

SELECT stu_name, exam_date,

       CASEWHEN exam_sub = '语文' THENexam_score

            ELSE NULL

       ENDAS 语文,

       CASEWHEN exam_sub = '数学' THENexam_score

           ELSE NULL

       ENDAS 数学,

       CASEWHEN exam_sub = '英语' THENexam_score

           ELSE NULL

       ENDAS 英语

FROM exams;      

上面语句将得到如表12-24所看到的的结果。

12-24                                                     使用CASE表达式得到的结果

 

stu_name

exam_date

语文

数学

英语

张三

2009-06-20

90

NULL

NULL

张三

2009-06-20

NULL

95

NULL

张三

2009-06-20

NULL

NULL

100

张三

2009-09-20

85

NULL

NULL

张三

2009-09-20

NULL

90

NULL

张三

2009-09-20

NULL

NULL

98

李四

2009-06-20

80

NULL

NULL

李四

2009-06-20

NULL

85

NULL

李四

2009-06-20

NULL

NULL

90

李四

2009-09-20

75

NULL

NULL

李四

2009-09-20

NULL

80

NULL

李四

2009-09-20

NULL

NULL

88

由上表能够看出,仅仅要按stu_nameexam_date分组计算最大值,就能够得到表12-23所要求的计算结果。參考以下的语句:

SELECT stu_name, exam_date,

      MAX(CASE WHEN exam_sub = '语文' THEN exam_score

               ELSE NULL

          END) AS 语文,

      MAX(CASE WHEN exam_sub = '数学' THEN exam_score

               ELSE NULL

          END) AS 数学,

      MAX(CASE WHEN exam_sub = '英语' THEN exam_score

               ELSE NULL

           END) AS 英语

FROM exams

GROUP BY stu_name, exam_date

ORDER BY stu_name, exam_date;

 

### Rayleigh-Bénard 对流简介 Rayleigh-Bénard 对流是一种经典的热对流现象,通常发生在被上下两个平行平面边界约束的流体层中。当底部加热时,如果温差超过某一临界值,则会形成稳定的对流单元,这种现象被称为 Rayleigh-Bénard 对流[^1]。 在计算流体力学 (Computational Fluid Dynamics, CFD) 中,研究 Rayleigh-Bénard 对流的方法主要包括数值模拟和理论分析两种途径。这些方法能够帮助理解对流模式、湍流特性以及能量传输效率等问题。 --- ### 数值模拟方法概述 #### 有限差分法 有限差分法是最常用的数值求解技术之一,用于解决描述 Rayleigh-Bénard 对流的动力学方程组(Navier-Stokes 方程)。通过离散化空间域并将偏微分方程转化为代数方程来实现数值近似。这种方法特别适合于二维或三维简单几何形状下的流动问题[^2]。 ```python import numpy as np def finite_difference_laplacian(grid): """ 计算网格上的拉普拉斯算子。 :param grid: 输入温度场矩阵 :return: 输出经过有限差分离散后的结果 """ laplacian = ( -4 * grid[1:-1, 1:-1] + grid[:-2, 1:-1] + grid[2:, 1:-1] + grid[1:-1, :-2] + grid[1:-1, 2:] ) return laplacian ``` 上述代码片段展示了如何利用 Python 实现一个简单的二维有限差分算法以估算温度分布的变化率。 #### 谱方法 谱方法基于傅里叶变换或其他正交基函数展开形式,适用于周期性边界条件的情况。它能提供更高的精度并减少所需的自由度数量,尤其对于高雷诺数条件下复杂的涡旋结构有较好的现效果[^3]。 --- ### 不稳定性机制探讨 Rayleigh-Bénard 对流的核心在于其不稳定性的物理起源——浮力驱动效应。具体来说,下部受热使得该区域内的流体质点密度降低;一旦达到特定阈值以上,原本静止状态便无法维持下去,从而引发宏观尺度上可观测到的整体运动趋势。 这一过程可以用无量纲参数 Ra (Rayleigh Number)示: \[ Ra = \frac{g \beta \Delta T L^3}{\nu \alpha} \] 其中 \( g \) 是重力加速度,\( \beta \) 征体积膨胀系数,\( \Delta T \) 指的是垂直方向平均温差,\( L \) 则代特征长度规模,而 \( \nu \), \( \alpha \) 分别对应动力粘滞性和导热性能指标。 只有当实际系统的 Rayleigh 数超出理论预测临界值 (\( Ra_c \)) 后才会真正启动显著程度的自然循环活动。 --- ### 应用领域举例说明 除了基础科学研究外,Rayleigh-Bénard 对流模型还广泛应用于工程实践当中,比如太阳能集热器优化设计、核反应堆冷却系统评估等领域均可见其身影。通过对不同工况设定下传质规律的研究可以有效提升设备运行效能或者改善工艺流程控制策略等方面取得突破进展。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值