PostgreSQL总结(2)特殊数据类型查询 - array

回顾

        前面我们介绍了PostgreSQL的安装和对数据库的一些简单操作。我们知道,对于数据库的大部分操作都是查询操作,PostgreSQL具有丰富的数据类型,有一些特殊的数据类型操作与其他关系型数据库有所区别,这里我们来介绍一下对于这些数据类型的查询。

array查询

        若Table_A和Table_B是多对多的关系,在其他关系型数据库中,我们会再创建一张表来保存Table_A与Table_B之间的关系,因为其他关系型数据库无法使用一条记录来保存这种关系。而在PostgreSQL中,则可以在Table_A和Table_B中分别使用一个数组来保存这种关系,就不需要额外的再创建一张表:在 Table_A 中字段 Column_A_B 是数组类型,Column_A_B 数组中的元素作为 Table_B 的外键;在 Table_B 中字段 Column_B_A 是数组类型,Column_B_A 数组中的元素作为 Table_A 的外键。

        为了更加直观,我们举例说明。有两张表:学生表(student)和课程表(course),一个学生可以选多门课程,一门课程被多个学生选。

-- 学生表
CREATE TABLE student
(
    id bigserial, --主键
    name text, --姓名
    courses bigint[], --所选课程
    CONSTRAINT student_pk PRIMARY KEY(id) 
);

-- 课程表
CREATE TABLE course
(
    id bigserial, --主键
    name text, --课程名称
    e_name text, --课程英文名称
    students bigint[], --选该课程的学生
    CONSTRAINT course_pk PRIMARY KEY(id) 
);

在student表和course表中分别插入几条数据:

-- student插入四条数据
INSERT INTO student
(name, courses)
VALUES
('ZhangSan', array[1,2]),
('LiSi', array[1,3]),
('WangWu', array[2,3]),
('ZhaoLiu', array[1,2,3]);

-- course插入三条数据
INSERT INTO course
(name, e_name, students)
VALUES
('语文', 'Chinese', array[1,2,4]),
('数学', 'Math', array[1,3,4]),
('英语', 'English', array[2,3,4]);

插入结果如下:

=> SELECT * FROM student;
 id |   name   | courses 
----+----------+---------
  1 | ZhangSan | {1,2}
  2 | LiSi     | {1,3}
  3 | WangWu   | {2,3}
  4 | ZhaoLiu  | {1,2,3}
(4 rows)

=> SELECT * FROM course;
 id | name | e_name  | students 
----+------+---------+----------
  1 | 语文 | Chinese | {1,2,4}
  2 | 数学 | Math    | {1,3,4}
  3 | 英语 | English | {2,3,4}
(3 rows)

        目前在student表和course表中都只是保存了其关联的另一张表的id。那么问题来了,如果想查看其关联的表中的其他信息呢?如:想查看”ZhangSan”选了的课程名称有哪些?这就需要使用PostgreSQL中的关键词ANY了(或者使用”&&”)

SELECT s.id sid, c.id cid, s.name sname, s.courses, c.name cname, c.e_name ce_name
FROM student s
LEFT JOIN course c ON c.id = ANY(s.courses)
ORDER BY s.id;

或者:

SELECT s.id sid, c.id cid, s.name sname, s.courses, c.name cname, c.e_name ce_name
FROM student s
LEFT JOIN course c ON array[c.id] && s.courses
ORDER BY s.id;

查询结果如下:

 sid | cid |  sname   | courses | cname | ce_name 
-----+-----+----------+---------+-------+---------
   1 |   1 | ZhangSan | {1,2}   | 语文  | Chinese
   1 |   2 | ZhangSan | {1,2}   | 数学  | Math
   2 |   1 | LiSi     | {1,3}   | 语文  | Chinese
   2 |   3 | LiSi     | {1,3}   | 英语  | English
   3 |   2 | WangWu   | {2,3}   | 数学  | Math
   3 |   3 | WangWu   | {2,3}   | 英语  | English
   4 |   1 | ZhaoLiu  | {1,2,3} | 语文  | Chinese
   4 |   2 | ZhaoLiu  | {1,2,3} | 数学  | Math
   4 |   3 | ZhaoLiu  | {1,2,3} | 英语  | English

        但这个结果好像跟我们预期的还有那么一点点不一样:我们希望的是,关联后的结果条数和student表相同(如分页查询学生所选课程),课程名称也是一个数组并且和课程的id数组对应。在这里,我们做一些改动,查询语句如下:

SELECT s.id sid, s.name sname, s.courses, 
    array(SELECT name FROM course WHERE id = ANY(s.courses)) cname,
    array(SELECT e_name FROM course WHERE id = ANY(s.courses)) ce_name
FROM student s
ORDER BY s.id;

查询结果如下:

 sid |  sname   | courses |      cname       |        ce_name         
-----+----------+---------+------------------+------------------------
   1 | ZhangSan | {1,2}   | {语文,数学}      | {Chinese,Math}
   2 | LiSi     | {1,3}   | {语文,英语}      | {Chinese,English}
   3 | WangWu   | {2,3}   | {数学,英语}      | {Math,English}
   4 | ZhaoLiu  | {1,2,3} | {语文,数学,英语} | {Chinese,Math,English}
(4 rows)

        是不是这样的结果更能让人接受?但是问题又来了:如果course表中有很多个字段,都需要像上面那样合并成一个,那岂不是要有N多个子查询?这样会不会效率很低?关于这个问题,笔者也没有什么太好的解决方案,欢迎大家提出更好的解决方案。目前的处理方式是创建一个视图,将这些信息组合的耗时操作集中处理,不会影响用户。视图的DDL如下:

CREATE VIEW course_info AS
SELECT 
    DISTINCT courses,
    array(SELECT name FROM course WHERE id = ANY(s.courses)) cname,
    array(SELECT e_name FROM course WHERE id = ANY(s.courses)) ce_name
FROM student s;

        那么,在进行关联查询的时候,student表就直接和视图course_info进行关联,查询语句如下:

SELECT s.id sid, s.name sname, s.courses, ci.cname,ci.ce_name
FROM student s
LEFT JOIN course_info ci ON s.courses = ci.courses
ORDER BY s.id;

        我们来看看使用视图作为关联和不使用的情况的执行效率对比:

  • student表与course表关联:
EXPLAIN ANALYZE SELECT s.id sid, s.name sname, s.courses, 
    array(SELECT name FROM course WHERE id = ANY(s.courses)) cname,
    array(SELECT e_name FROM course WHERE id = ANY(s.courses)) ce_name
FROM student s
ORDER BY s.id;

执行计划运行结果如下:


                                                            QUERY PLAN                                                            
----------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=32444.80..32446.80 rows=800 width=72) (actual time=0.114..0.115 rows=4 loops=1)
   Sort Key: s.id
   Sort Method: quicksort  Memory: 26kB
   ->  Seq Scan on student s  (cost=0.00..32406.22 rows=800 width=72) (actual time=0.051..0.101 rows=4 loops=1)
         SubPlan 1
           ->  Bitmap Heap Scan on course  (cost=9.59..20.24 rows=10 width=32) (actual time=0.010..0.011 rows=2 loops=4)
                 Recheck Cond: (id = ANY (s.courses))
                 ->  Bitmap Index Scan on course_pk  (cost=0.00..9.58 rows=10 width=0) (actual time=0.005..0.005 rows=2 loops=4)
                       Index Cond: (id = ANY (s.courses))
         SubPlan 2
           ->  Bitmap Heap Scan on course course_1  (cost=9.59..20.24 rows=10 width=32) (actual time=0.005..0.005 rows=2 loops=4)
                 Recheck Cond: (id = ANY (s.courses))
                 ->  Bitmap Index Scan on course_pk  (cost=0.00..9.58 rows=10 width=0) (actual time=0.002..0.002 rows=2 loops=4)
                       Index Cond: (id = ANY (s.courses))
 Total runtime: 0.168 ms
(15 rows)

  • student表与course_info视图关联:
EXPLAIN ANALYZE SELECT s.id sid, s.name sname, s.courses, ci.cname,ci.ce_name
FROM student s
LEFT JOIN course_info ci ON s.courses = ci.courses
ORDER BY s.id;

执行计划运行结果如下:

                                                               QUERY PLAN                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=40608.35..40610.35 rows=800 width=136) (actual time=0.076..0.076 rows=4 loops=1)
   Sort Key: s.id
   Sort Method: quicksort  Memory: 26kB
   ->  Hash Right Join  (cost=32440.22..40569.78 rows=800 width=136) (actual time=0.066..0.071 rows=4 loops=1)
         Hash Cond: (s_1.courses = s.courses)
         ->  HashAggregate  (cost=32412.22..40511.28 rows=200 width=32) (actual time=0.047..0.047 rows=4 loops=1)
               ->  Seq Scan on student s_1  (cost=0.00..32406.22 rows=800 width=32) (actual time=0.025..0.043 rows=4 loops=1)
                     SubPlan 1
                       ->  Bitmap Heap Scan on course  (cost=9.59..20.24 rows=10 width=32) (actual time=0.005..0.005 rows=2 loops=4)
                             Recheck Cond: (id = ANY (s_1.courses))
                             ->  Bitmap Index Scan on course_pk  (cost=0.00..9.58 rows=10 width=0) (actual time=0.002..0.002 rows=2 loops=4)
                                   Index Cond: (id = ANY (s_1.courses))
                     SubPlan 2
                       ->  Bitmap Heap Scan on course course_1  (cost=9.59..20.24 rows=10 width=32) (actual time=0.002..0.002 rows=2 loops=4)
                             Recheck Cond: (id = ANY (s_1.courses))
                             ->  Bitmap Index Scan on course_pk  (cost=0.00..9.58 rows=10 width=0) (actual time=0.001..0.001 rows=2 loops=4)
                                   Index Cond: (id = ANY (s_1.courses))
         ->  Hash  (cost=18.00..18.00 rows=800 width=72) (actual time=0.008..0.008 rows=4 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 1kB
               ->  Seq Scan on student s  (cost=0.00..18.00 rows=800 width=72) (actual time=0.003..0.004 rows=4 loops=1)
 Total runtime: 0.116 ms

        我们可以看到,student表直接与course表关联查询,执行需要0.168 ms,与course_info视图进行关联查询,执行需要0.116 ms。对于关联信息越多的情况,效率提升越明显。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值