MySQL讲义第 33 讲——select 查询之静态交叉表查询


文章目录

  • MySQL讲义第 33 讲——select 查询之静态交叉表查询
  • 一、数据准备
  • 二、交叉表的形式
  • 三、静态交叉表的实现
  • 1、根据 user、goods 和 orders 三张表查询每个用户每种类别的商品的消费金额


交叉表查询是将来源于某个表中的字段进行分组,一组列在交叉表左侧,一组列在交叉表上部,并在交叉表行与列交叉处显示表中某个字段的各种计算值。交叉表分为静态交叉表和动态交叉表,其中静态交叉表中的列是固定的,而动态交叉表中的列需要动态生成。

使用交叉表查询来计算和重构数据,可以简化数据分析。交叉表查询计算数据的总和,平均值,计数及其他类型的统计,并将它们分组,一组列在数据表左侧作为交叉表的行字段,另一组列在数据表的顶端作为交叉表的列字段。

一、数据准备

有下面三张表,表结构和数据如下:

mysql> select * from users;
+---------+-----------+-------------+-----------+
| user_id | user_name | phone       | addr      |
+---------+-----------+-------------+-----------+
| 2020101 | 张华      | 15937310588 | Xinxiang  |
| 2020102 | 张毅德    | 13783730566 | Xinxiang  |
| 2020103 | 刘选德    | 13602313277 | Beijing   |
| 2020104 | 赵紫龙    | 13703713731 | Zhengzhou |
| 2020105 | 关云常    | 13637312446 | Xinxiang  |
| 2020106 | 刘蓓      | 15037130526 | Zhengzhou |
| 2020107 | 刘晓璐    | 18237150566 | Zhengzhou |
+---------+-----------+-------------+-----------+
7 rows in set (0.00 sec)

mysql> select * from goods;
+----------+------------+---------+------------+
| goods_id | goods_name | price   | categories |
+----------+------------+---------+------------+
|     1101 | 帽子       |   58.00 | 服装       |
|     1102 | 裤子       |  150.00 | 服装       |
|     1103 | 运动鞋     |  580.00 | 服装       |
|     1104 | 西服       | 1508.00 | 服装       |
|     2201 | 香蕉       |    2.58 | 水果       |
|     2202 | 苹果       |    4.98 | 水果       |
|     2203 | 葡萄       |    6.88 | 水果       |
|     3301 | 笔记本     |   25.00 | 文具       |
|     3302 | 钢笔       |   25.80 | 文具       |
+----------+------------+---------+------------+
9 rows in set (0.00 sec)

mysql> select * from orders;
+----------+---------+----------+----------+---------------------+
| order_id | user_id | goods_id | quantity | order_date          |
+----------+---------+----------+----------+---------------------+
|        1 | 2020101 |     1101 |     1.00 | 2020-10-22 09:05:21 |
|        2 | 2020101 |     1102 |     1.00 | 2020-10-20 00:00:00 |
|        3 | 2020101 |     2201 |     2.50 | 2020-10-21 00:00:00 |
|        4 | 2020101 |     2202 |     4.50 | 2020-10-21 00:00:00 |
|        5 | 2020101 |     2203 |     2.50 | 2020-10-20 00:00:00 |
|        6 | 2020101 |     3301 |     2.00 | 2020-10-20 00:00:00 |
|        7 | 2020102 |     2201 |     3.50 | 2020-10-22 00:00:00 |
|        8 | 2020102 |     2202 |     2.00 | 2020-10-22 00:00:00 |
|        9 | 2020103 |     2202 |     2.00 | 2020-10-19 00:00:00 |
|       10 | 2020103 |     2203 |     1.20 | 2020-10-19 00:00:00 |
|       11 | 2020103 |     3302 |     1.00 | 2020-10-20 00:00:00 |
|       12 | 2020103 |     1101 |     1.00 | 2020-10-19 00:00:00 |
|       13 | 2020103 |     1102 |     1.00 | 2020-10-20 00:00:00 |
|       14 | 2020104 |     1102 |     1.00 | 2020-10-20 00:00:00 |
|       15 | 2020104 |     2201 |     1.80 | 2020-10-19 00:00:00 |
|       16 | 2020104 |     2202 |     3.20 | 2020-10-19 00:00:00 |
|       17 | 2020104 |     3302 |     1.00 | 2020-10-20 00:00:00 |
|       18 | 2020105 |     1103 |     1.00 | 2020-10-21 00:00:00 |
|       19 | 2020105 |     1104 |     1.00 | 2020-10-21 00:00:00 |
|       20 | 2020105 |     2201 |     1.80 | 2020-10-22 00:00:00 |
|       21 | 2020105 |     2202 |     3.20 | 2020-10-22 00:00:00 |
|       22 | 2020105 |     2203 |     2.00 | 2020-10-21 00:00:00 |
|       23 | 2020105 |     3302 |     1.00 | 2020-10-21 00:00:00 |
|       24 | 2020106 |     1102 |     1.00 | 2020-10-22 00:00:00 |
|       25 | 2020106 |     1103 |     1.00 | 2020-10-22 00:00:00 |
|       26 | 2020106 |     1104 |     1.00 | 2020-10-22 00:00:00 |
|       27 | 2020106 |     2201 |     2.00 | 2020-10-21 00:00:00 |
|       28 | 2020106 |     2202 |     3.50 | 2020-10-21 00:00:00 |
|       29 | 2020106 |     2203 |     5.50 | 2020-10-21 00:00:00 |
+----------+---------+----------+----------+---------------------+
29 rows in set (0.00 sec)

二、交叉表的形式

交叉表又称为行列转换,就是把下面的表1 转换为表2(交叉表)。

select stu_name,c_name,score
from stu s,course c,score sc where s.stu_id=sc.stu_id and c.c_id=sc.c_id;
--################################  表1  #############################################
+-----------+-----------------------+-------+
| stu_name  | c_name                | score |
+-----------+-----------------------+-------+
| 王占峰    | 管理学原理            |    88 |
| 王占峰    | 政治经济学            |    75 |
| 王占峰    | 数据库系统原理        |    89 |
| 王占峰    | 企业管理概论          |    95 |
| 刘国强    | 管理学原理            |    65 |
| 刘国强    | 政治经济学            |    79 |
| 刘国强    | 数据库系统原理        |    72 |
| 刘国强    | 企业管理概论          |    91 |
| 王艳艳    | 管理学原理            |    84 |
| 王艳艳    | 政治经济学            |    76 |
| 王艳艳    | 数据库系统原理        |    68 |
| 王艳艳    | 企业管理概论          |    92 |
| 赵牡丹    | 管理学原理            |    64 |
| 赵牡丹    | 政治经济学            |    75 |
| 赵牡丹    | 数据库系统原理        |    81 |
| 赵牡丹    | 企业管理概论          |    90 |
+-----------+----------------------+-------+
--################################  表2  #############################################
+-----------+--------------+---------------+-------------------+------------------+--------+
| stu_name  | 管理学原理    | 政治经济学    | 数据库系统原理      | 企业管理概论      | 总分   |
+-----------+---------------+---------------+------------------+------------------+--------+
| 刘国强    |            65 |            79 |                72 |               91 |    307 |
| 王占峰    |            88 |            75 |                89 |               95 |    347 |
| 王艳艳    |            84 |            76 |                68 |               92 |    320 |
| 赵牡丹    |            64 |            75 |                81 |               90 |    310 |
+-----------+---------------+---------------+-----------------------+--------------+--------+

三、静态交叉表的实现

生成交叉表的基本方法为:
(1)按照交叉表的行字段进行分组(比如上例中的 stu_name)。
(2)对要转换为列的行字段(比如上例中的 c_name)进行统计,在统计函数中使用 IF 函数对数据进行筛选,生成列字段。

例如:

1、根据 user、goods 和 orders 三张表查询每个用户每种类别的商品的消费金额

第一步:根据 u.user_name 和 categories 两个字段分组,统计消费金额

mysql> SELECT
    -> u.user_name as 用户姓名,
    -> categories as 消费类别,
    -> sum(o.quantity * g.price) as 总金额
    -> FROM
    -> users u JOIN orders o ON u.user_id = o.user_id 
    -> JOIN goods g ON o.goods_id = g.goods_id
    -> GROUP BY u.user_name,categories;
+--------------+--------------+-----------+
| 用户姓名     | 消费类别     | 总金额    |
+--------------+--------------+-----------+
| 关云常       | 文具         |   25.8000 |
| 关云常       | 服装         | 2088.0000 |
| 关云常       | 水果         |   34.3400 |
| 刘蓓         | 服装         | 2238.0000 |
| 刘蓓         | 水果         |   60.4300 |
| 刘选德       | 文具         |   25.8000 |
| 刘选德       | 服装         |  208.0000 |
| 刘选德       | 水果         |   18.2160 |
| 张华         | 文具         |   50.0000 |
| 张华         | 服装         |  208.0000 |
| 张华         | 水果         |   46.0600 |
| 张毅德       | 水果         |   18.9900 |
| 赵紫龙       | 文具         |   25.8000 |
| 赵紫龙       | 服装         |  150.0000 |
| 赵紫龙       | 水果         |   20.5800 |
+--------------+--------------+-----------+
15 rows in set (0.00 sec)

第二步:对消费类别字段分别按文具、服装、水果为条件进行筛选,然后转换为列,代码如下:

mysql> SELECT
    -> u.user_name as 用户,
    -> sum(if(categories = '服装',o.quantity * g.price,0)) as 服装,
    -> sum(if(categories = '文具',o.quantity * g.price,0)) as 文具,
    -> sum(if(categories = '水果',o.quantity * g.price,0)) as 水果,
    -> sum(o.quantity * g.price) as 总金额
    -> FROM
    -> users u JOIN orders o ON u.user_id = o.user_id 
    -> JOIN goods g ON o.goods_id = g.goods_id
    -> GROUP BY u.user_name;
+-----------+-----------+---------+---------+-----------+
| 用户      | 服装      | 文具    | 水果    | 总金额    |
+-----------+-----------+---------+---------+-----------+
| 关云常    | 2088.0000 | 25.8000 | 34.3400 | 2148.1400 |
| 刘蓓      | 2238.0000 |  0.0000 | 60.4300 | 2298.4300 |
| 刘选德    |  208.0000 | 25.8000 | 18.2160 |  252.0160 |
| 张华      |  208.0000 | 50.0000 | 46.0600 |  304.0600 |
| 张毅德    |    0.0000 |  0.0000 | 18.9900 |   18.9900 |
| 赵紫龙    |  150.0000 | 25.8000 | 20.5800 |  196.3800 |
+-----------+-----------+---------+---------+-----------+
6 rows in set (0.00 sec)

第三步:添加总计行

mysql> SELECT
    -> IFNULL(u.user_name,'总计') as 用户,
    -> sum(if(categories = '服装',o.quantity * g.price,0)) as 服装,
    -> sum(if(categories = '文具',o.quantity * g.price,0)) as 文具,
    -> sum(if(categories = '水果',o.quantity * g.price,0)) as 水果,
    -> sum(o.quantity * g.price) as 总金额
    -> FROM
    -> users u JOIN orders o ON u.user_id = o.user_id 
    -> JOIN goods g ON o.goods_id = g.goods_id
    -> GROUP BY u.user_name
    -> WITH rollup;
+-----------+-----------+----------+----------+-----------+
| 用户      | 服装      | 文具     | 水果     | 总金额    |
+-----------+-----------+----------+----------+-----------+
| 关云常    | 2088.0000 |  25.8000 |  34.3400 | 2148.1400 |
| 刘蓓      | 2238.0000 |   0.0000 |  60.4300 | 2298.4300 |
| 刘选德    |  208.0000 |  25.8000 |  18.2160 |  252.0160 |
| 张华      |  208.0000 |  50.0000 |  46.0600 |  304.0600 |
| 张毅德    |    0.0000 |   0.0000 |  18.9900 |   18.9900 |
| 赵紫龙    |  150.0000 |  25.8000 |  20.5800 |  196.3800 |
| 总计      | 4892.0000 | 127.4000 | 198.6160 | 5218.0160 |
+-----------+-----------+----------+----------+-----------+
7 rows in set (0.00 sec)
--说明:使用 IFNULL 函数把最后一行第一列的 NULL 替换成 '总计'。