PostgreSQL教程(三):SQL语言

一、引言

本章提供了一个如何使用SQL执行简单操作的概述。本教程的目的只是给你一个介绍。有许多关于SQL的书籍,包括[melt93]和[date97]。你还要知道有些PostgreSQL语言特性是对标准的扩展。

在随后的例子里,我们假设你已经创建了名为mydb的数据库,就像在前面的章节里介绍的一样,并且已经能够启动psql。

本手册的例子也可以在PostgreSQL源代码的目录src/tutorial/中找到(二进制PostgreSQL发布中可能没有编译这些文件)。要使用这些文件,首先进入该目录然后运行make:

$ cd ..../src/tutorial$ make 

这样就创建了那些脚本并编译了包含用户定义函数和类型的 C 文件。接下来,要开始本教程,按照下面说的做:

$ cd ..../tutorial
$ psql -s mydb
...
mydb=> \i basics.sql

\i命令从指定的文件中读取命令。psql-s选项把你置于单步模式,它在向服务器发送每个语句之前暂停。 在本节使用的命令都在文件basics.sql中。 


二、概念

PostgreSQL是一种关系型数据库管理系统(RDBMS)。这意味着它是一种用于管理存储在关系中的数据的系统。关系实际上是表的数学术语。今天,把数据存储在表里的概念已经快成了固有的常识了,但是还有其它的一些方法用于组织数据库。在类Unix操作系统上的文件和目录就形成了一种层次数据库的例子。更现代的发展是面向对象数据库。

每个表都是一个命名的行集合。一个给定表的每一行由同一组的命名列组成,而且每一列都有一个特定的数据类型。虽然列在每行里的顺序是固定的,但一定要记住SQL并不对行在表中的顺序做任何保证(但你可以为了显示的目的对它们进行显示地排序)。

表被分组成数据库,一个由单个PostgreSQL服务器实例管理的数据库集合组成一个数据库集簇。


三、创建一个新表

你可以通过指定表的名字和所有列的名字及其类型来创建表:

CREATE TABLE weather (city            varchar(80),temp_lo         int,           -- 最低温度temp_hi         int,           -- 最高温度prcp            real,          -- 湿度date            date
);

你可以在psql输入这些命令以及换行符。psql可以识别该命令直到分号才结束。

你可以在 SQL 命令中自由使用空白(即空格、制表符和换行符)。 这就意味着你可以用和上面不同的对齐方式键入命令,或者将命令全部放在一行中。两个划线("--")引入注释。 任何跟在它后面直到行尾的东西都会被忽略。SQL 是对关键字和标识符大小写不敏感的语言,只有在标识符用双引号包围时才能保留它们的大小写(上例没有这么做)。

varchar(80)指定了一个可以存储最长 80 个字符的任意字符串的数据类型。int是普通的整数类型。real是一种用于存储单精度浮点数的类型。date类型应该可以自解释(没错,类型为date的列名字也是date。 这么做可能比较方便或者容易让人混淆 — 你自己选择)。

PostgreSQL支持标准的SQL类型int、smallint、real、double precision、char(N)、varchar(N)、date、time、timestamp和interval,还支持其他的通用功能的类型和丰富的几何类型。PostgreSQL中可以定制任意数量的用户定义数据类型。因而类型名并不是语法关键字,除了SQL标准要求支持的特例外。

第二个例子将保存城市和它们相关的地理位置:

CREATE TABLE cities (name            varchar(80),location        point
);

类型point就是一种PostgreSQL特有数据类型的例子。

最后,我们还要提到如果你不再需要某个表,或者你想以不同的形式重建它,那么你可以用下面的命令删除它:

DROP TABLE tablename;

四、在表中增加行

INSERT语句用于向表中添加行:

INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');

请注意所有数据类型都使用了相当明了的输入格式。那些不是简单数字值的常量通常必需用单引号(')包围,就象在例子里一样。date类型实际上对可接收的格式相当灵活,不过在本教程里,我们应该坚持使用这种清晰的格式。

point类型要求一个座标对作为输入,如下:

INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');

到目前为止使用的语法要求你记住列的顺序。一个可选的语法允许你明确地列出列:

INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');

如果你需要,你可以用另外一个顺序列出列或者是忽略某些列, 比如说,我们不知道降水量:

INSERT INTO weather (date, city, temp_hi, temp_lo)VALUES ('1994-11-29', 'Hayward', 54, 37);

许多开发人员认为明确列出列要比依赖隐含的顺序是更好的风格。

请输入上面显示的所有命令,这样你在随后的各节中才有可用的数据。

你还可以使用COPY从文本文件中装载大量数据。这种方式通常更快,因为COPY命令就是为这类应用优化的, 只是比 INSERT少一些灵活性。比如:

COPY weather FROM '/home/user/weather.txt';

这里源文件的文件名必须在运行后端进程的机器上是可用的, 而不是在客户端上,因为后端进程将直接读取该文件。你可以在COPY中读到更多有关COPY命令的信息。


五、查询一个表

要从一个表中检索数据就是查询这个表。SQL的SELECT语句就是做好在这个用途的。该语句分为选择列表(列出要返回的列)、表列表(列出从中检索数据的表)以及可选的条件(指定任意的限制)。比如,要检索weather表的所有行,键入:

SELECT * FROM weather;

这里*是“所有列”的缩写。因此相同的结果应该这样获得:

SELECT city, temp_lo, temp_hi, prcp, date FROM weather;

而输出应该是:

     city              | temp_lo | temp_hi | prcp  |    date    
--------------------+-----------+------------+-------+------------
 San Francisco |      46    |      50     | 0.25  | 1994-11-27
 San Francisco |      43    |      57     |    0    | 1994-11-29
 Hayward          |      37    |      54     |          | 1994-11-29
(3 rows)
 

你可以在选择列表中写任意表达式,为不仅仅是列的列表。比如,你可以:

SELECT city, (temp_hi + temp_lo) / 2 AS temp_avg, date FROM weather;

这样应该得到:

     city              | temp_avg |    date    
--------------------+--------------+------------
 San Francisco |       48       | 1994-11-27
 San Francisco |       50       | 1994-11-29
 Hayward          |       45       | 1994-11-29
(3 rows)
 

请注意这里的AS子句是如何给输出列重新命名的(AS子句是可选的)。

一个查询可以使用WHERE子句“修饰”,它指定需要哪些行。WHERE子句包含一个布尔(真值)表达式,只有哪些使布尔表达式为真的行才会被返回。在条件中可以使用常用的布尔操作符(AND、OR和NOT)。比如,下面的查询检索旧金山的下雨天的天气:
 

SELECT * FROM weather WHERE city='San Francisco' AND prcp > 0.0;

注意:字段的值不能使用双引号,要使用单引号,否则pgsql会将双引号包含的值认为是字段名。

结果:

你可以要求返回的查询结果是排好序的:
 

SELECT * FROM weather ORDER BY city;

在这个例子里 ,排序的顺序并未完全被指定,因此你可能看到属于旧金山的行被随机地排序。但是如果你使用下面的语句,那么就总是会得到上面的结果:

SELECT * FROM weather ORDER BY city, temp_lo;

你可以要求在查询的结果中消除重复的行:

SELECT DISTINCT city FROM weather;

再次声明,结果行的顺序可能变化。你可以组合使用DISTINCTORDER BY来保证获取一致的结果:

SELECT DISTINCT city FROM weather ORDER BY city;


Notes

【1】虽然SELECT * 对于即席查询很实用,但我们普遍认为在生产中这是很糟糕的风格,因为给表增加一个列就改变了结果。

【2】在一些数据库系统里,包括老版本的PostgreSQL,DISTINCT的实现自动对行进行排序,因此ORDER BY是多余的。但是这一点并不是SQL标准的要求,并且目前的PostgreSQL并不保证DISTINCT会导致行被排序。


六、在表之间连接

到目前为止,我们的查询一次只访问一个表。查询可以一次访问多个表,或者用这种方式访问一个表而同时处理该表的多个行。一个同时访问同一个或者不同表的多个行的查询叫连接查询。举例来说,比如你想列出所有天气记录以及相关的城市位置。要实现这个目标,我们需要拿weather表每行的city列和cities表所有行的name列进行比较,并选取哪些在该值上相匹配的行对。


Notes

这里只是一个概念上的模型。连接通常以比实际比较每个可能的行对更高效的方式执行,但这些是用户看不到的


这个任务可以用下面的查询来实现:

SELECT * FROM weather,cities WHERE city=name;

观察结果集的两个方面:

  • 没有城市Hayward的结果行。这是因为在cities表里面没有Hayward的匹配行,所以连接忽略 weather表里的不匹配行。我们稍后将看到如何修补它。

  • 有两个列包含城市名字。这是正确的, 因为weathercities表的列被串接在一起。不过,实际上我们不想要这些, 因此你将可能希望明确列出输出列而不是使用*:        

 SELECT city, temp_lo, temp_hi, prcp, date, location FROM weather,cities WHERE city=name;

练习:看看上面这个查询省略WHERE子句的语义是什么

因为这些列的名字都不一样,所以规划器自动地找出它们属于哪个表。

如果在两个表里有重名的列,你需要限定列名来说明你究竟想要哪一个,如:

SELECT weather.city, weather.temp_lo, weather.temp_hi,weather.prcp, weather.date, cities.locationFROM weather, citiesWHERE cities.name = weather.city;

人们广泛认为在一个链接查询中限定所有列名是一种好的风格,这样即使未来向其中一个表里添加重名列也不会导致查询失效。

到目前为止,这种类型的连接查询也可以用下面这样的形式写出来:
 

SELECT * FROM weather INNER JOIN cities ON (weather.city = cities.name);注意:上述括号加不加均可

这个语法并不象上文的那个那么常用,我们在这里写出来是为了让你更容易了解后面的主题。

现在我们将看看如何能把Hayward记录找回来。我们想让查询干的事是扫描weather表, 并且对每一行都找出匹配的cities表行。如果我们没有找到匹配的行,那么我们需要一些"空值"代替cities表的列。 这种类型的查询叫外连接 (我们在此之前看到的连接都是内连接)。这样的命令看起来象这样:

 SELECT * FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name);

这个查询是一个左外连接, 因为在连接操作符左部的表中的行在输出中至少要出现一次, 而在右部的表的行只有在能找到匹配的左部表行是才被输出。 如果输出的左部表的行没有对应匹配的右部表的行,那么右部表行的列将填充空值(null)。

练习:还有右外连接和全外连接,试着找出来它们能干什么。

我们也可以把一个表和自己连接起来。这叫做自连接。 比如,假设我们想找出那些在其它天气记录的温度范围之外的天气记录。这样我们就需要拿 weather表里每行的temp_lo和temp_hi列与weather表里其它行的temp_lo和temp_hi列进行比较。我们可以用下面的查询实现这个目标:

SELECT W1.city, W1.temp_lo AS low, W1.temp_hi AS high,W2.city, W2.temp_lo AS low, W2.temp_hi AS highFROM weather W1, weather W2WHERE W1.temp_lo < W2.temp_loAND W1.temp_hi > W2.temp_hi;

在这里我们把weather表重新标记为W1和W2以区分连接的左部和右部。你还可以用这样的别名在其它查询里节约一些敲键,比如:

SELECT *FROM weather w, cities cWHERE w.city = c.name;

你以后会经常碰到这样的缩写的。


七、聚集函数

和大多数其它关系数据库产品一样,PostgreSQL支持聚集函数。 一个聚集函数从多个输入行中计算出一个结果。 比如,我们有在一个行集合上计算count(计数)、sum(和)、avg(均值)、max(最大值)和min(最小值)的函数。

比如,我们可以用下面的语句找出所有记录中最低温度中的最高温度:

SELECT max(temp_lo) FROM weather;

如果我们想知道该读数发生在哪个城市,我们可以用:

SELECT city FROM weather WHERE temp_lo = max(temp_lo);   错误

不过这个方法不能运转,因为聚集max不能被用于WHERE子句中(存在这个限制是因为WHERE子句决定哪些行可以被聚集计算包括;因此显然它必需在聚集函数之前被计算)。 不过,我们通常都可以用其它方法实现我们的目的;这里我们就可以使用子查询

 SELECT city FROM weather WHERE temp_lo = (SELECT max(temp_lo) FROM weather);

这样做是 OK 的,因为子查询是一次独立的计算,它独立于外层的查询计算出自己的聚集。

聚集同样也常用于和GROUP BY子句组合。比如,我们可以获取每个城市观测到的最低温度的最高值:

SELECT city, max(temp_lo) FROM weather GROUP BY city;

 

这样给我们每个城市一个输出。每个聚集结果都是在匹配该城市的表行上面计算的。我们可以用HAVING 过滤这些被分组的行:

SELECT city, max(temp_lo) FROM weather GROUP BY city HAVING max(temp_lo) < 40;

这样就只给出那些所有temp_lo值曾都低于 40的城市。最后,如果我们只关心那些名字以“S”开头的城市,我们可以用:

 SELECT city, max(temp_lo) FROM weather WHERE city LIKE 'S%' GROUP BY city HAVING max(temp_lo) < 50;

LIKE操作符进行模式匹配,在第 9.7 节里有解释。

理解聚集和SQL的WHERE以及HAVING子句之间的关系对我们非常重要。WHEREHAVING的基本区别如下WHERE在分组和聚集计算之前选取输入行(因此,它控制哪些行进入聚集计算), 而HAVING在分组和聚集之后选取分组行。因此,WHERE子句不能包含聚集函数; 因为试图用聚集函数判断哪些行应输入给聚集运算是没有意义的。相反,HAVING子句总是包含聚集函数(严格说来,你可以写不使用聚集的HAVING子句, 但这样做很少有用。同样的条件用在WHERE阶段会更有效)。

在前面的例子里,我们可以在WHERE里应用城市名称限制,因为它不需要聚集。这样比放在HAVING里更加高效,因为可以避免那些未通过 WHERE检查的行参与到分组和聚集计算中。


八、更新

你可以用UPDATE命令更新现有的行。假设你发现所有 11 月 28 日以后的温度读数都低了两度,那么你就可以用下面的方式改正数据:

 UPDATE weather SET temp_hi = temp_hi -2, temp_lo = temp_lo -2 WHERE date > '1994-11-28';

看看数据的新状态:

SELECT * FROM weather;

九、删除

数据行可以用DELETE命令从表中删除。假设你对Hayward的天气不再感兴趣,那么你可以用下面的方法把那些行从表中删除:

DELETE FROM weather WHERE city='Hayward';

所有属于Hayward的天气记录都被删除。

我们用下面形式的语句的时候一定要小心

DELETE FROM tablename;

如果没有一个限制,DELETE将从指定表中删除所有行,把它清空。做这些之前系统不会请求你确认!!!!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690693.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第24讲投票管理实现

投票管理实现 后端&#xff1a; package com.java1234.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.java1234.entity.*; import com.java1234.service.…

数据结构之线性表插入与删除运算

线性表 线性表的定义 线性表&#xff0c;或称表&#xff0c;是一种非常灵便的结构&#xff0c;可以根据需要改变表的长度&#xff0c;也可以在表中任何位置对元素进行访问、插入或删除等操作。另外&#xff0c;还可以将多个表连接成一个表&#xff0c;或把一个表拆分成多个表…

IO进程线程day3作业

1、使用 fread 和 fwrite 完成文件拷贝 代码&#xff1a; #include<myhead.h> int main(int argc, const char *argv[]) {//判断终端输入if(argc!3){printf("inputs file error\n");printf("usage:./a.out srcfile destfile\n");return -1;}//定义…

【C->Cpp】由C迈向Cpp(3)

正文开始&#xff1a; 目录 &#xff08;一&#xff09;函数重载 &#xff08;1&#xff09;函数重载 &#xff08;2&#xff09;函数重载实现原理 &#xff08;二&#xff09; 引用 &#xff08;1&#xff09;引用 &#xff08;2&#xff09;语法 i &#xff0c;别名&am…

使用openeuler 22.03替代CentOS 7.9,建立虚拟机详细步骤

进入浏览器搜索网址下载openeuler 22.03镜像文件 https://mirrors.huaweicloud.com/openeuler/openEuler-22.03-LTS-SP3/ISO/x86_64/openEuler-22.03-LTS-SP3-x86_64-dvd.iso 打开VMware Workstation新建一个虚拟机&#xff1a; 自定义虚拟机位置 加入下载好的openeuler镜像文件…

嵌入式学习第十八天(目录IO)

目录IO: 1. mkdir int mkdir(const char *pathname, mode_t mode); 功能&#xff1a;创建目录文件 参数&#xff1a; pathname&#xff1a;文件路径 mode&#xff1a;文件的权限 rwx rwx rwx 111 111 111 0 7 7 7 r&#xff1a;目录中是否能够查看文件 w&#xff1a;目…

AMD FPGA设计优化宝典笔记(5)低频全局复位与高扇出

亚军老师的这本书《AMD FPGA设计优化宝典》&#xff0c;他主要讲了两个东西&#xff1a; 第一个东西是代码的良好风格&#xff1b; 第二个是设计收敛等的本质。 这个书的结构是一个总论&#xff0c;加上另外的9个优化&#xff0c;包含的有&#xff1a;时钟网络、组合逻辑、触发…

面向对象 设计原则

0 引言 单一职责原则&#xff1a;类应该只有一个改变的理由&#xff1b; 开放-封闭原则&#xff1a;类应该对扩展开放&#xff0c;对修改关闭&#xff1b; 迪米特原则&#xff1a;只和朋友交谈&#xff1b; 里氏替换原则&#xff1a;子类可以扩展父类的功能&#xff0c;但不能…

RocketMQ—RocketMQ消息重复消费问题

RocketMQ—RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题&#xff1a; 生产者多次投递消息&#xff1a;如果生产者发送消息时&#xff0c;连接有延迟&#xff0c;MQ还没收到消息&#xff0c;生产者又发送了一次消息&#xff1b; 消费者方扩容…

Docker命令实战

文章目录 一、Docker常用命令-图谱二、基础实战命令2.1、查找镜像2.2、启动容器2.3、修改容器内容2.3.1、进入容器内部修改2.3.2、挂载数据到外部修改 2.4、提交改变2.5、镜像传输--将镜像保存成压缩包2.6、两台主机间压缩文件的传输拷贝2.7、推送阿里云个人远程镜像仓库2.8、其…

SQL110 插入记录(一)(插入和interval关键字的用法)

代码 insert into exam_record(uid,exam_id,start_time,submit_time,score) values(1001,9001,2021-09-01 22:11:12,2021-09-01 22:11:12interval 50 minute,90), (1002,9002,2021-09-04 07:01:02,null,null)知识点 interval关键字的用法 INTERVAL关键字一般使用格式为&#x…

一篇博客教会你使用node多版本管理

文章目录 nvm 简介nvm 安装nvm 使用配置国内镜像显示可以安装的 nodejs安装 nodejs显示已安装的 nodejs切换 nodejs nvm 简介 nvm&#xff08;Node Version Manager&#xff09;是 node.js 的版本管理器&#xff0c;可以让我们轻松地在不同的 node.js 版本之间进行切换。 今天…

c语言(指针进阶)

指针 一.什么是字符指针二.使用指针数组模拟二维数组三.函数指针 一.什么是字符指针 字符指针&#xff1a;指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。即将字符串的首地址赋值给字符指针&#xff0c;可让字符指针…

树和堆的精讲

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

告别你的朝九晚五的工作。

告别你的朝九晚五的工作。 以下是6个网站&#xff0c;你可以从任何地方获得100美元到2000美元的报酬&#xff1a; &#xff08;第3个网站最正规&#xff09; 1. Honeygain 分享未使用的互联网带宽换取现金。 功能包括推荐系统、JumpTask模式、成就和每日幸运抽奖。 非常适…

机构如何搭建一个在线课程教学平台?

随着数字化教育的兴起&#xff0c;越来越多的教育机构开始考虑建立自己的在线课程教学平台。这一趋势不仅顺应了时代的发展&#xff0c;而且为教育行业带来了诸多便利和优势。构建一个在线教学平台可以帮助机构拓宽服务范围、提升教学质量、增强学生体验&#xff0c;并且能够有…

optuna,一个好用的Python机器学习自动化超参数优化库

🏷️个人主页:鼠鼠我捏,要死了捏的主页 🏷️付费专栏:Python专栏 🏷️个人学习笔记,若有缺误,欢迎评论区指正 前言 超参数优化是机器学习中的重要问题,它涉及在训练模型时选择最优的超参数组合,以提高模型的性能和泛化能力。Optuna是一个用于自动化超参数优化的…

美容小程序:让预约更简单,服务更贴心

在当今繁忙的生活节奏中&#xff0c;美容预约常常令人感到繁琐和疲惫。为了解决这个问题&#xff0c;许多美容院和SPA中心已经开始采用美容小程序来简化预约流程&#xff0c;并提供更加贴心的服务。在这篇文章中&#xff0c;我们将引导您了解如何制作一个美容小程序&#xff0c…

2024 年 2 月 TIOBE 指数:最流行的 10 种编程语言

Go 进入了 TIOBE 指数的前 10 名&#xff0c;这是谷歌编程语言有史以来的最高位置。 在 2024 年 2 月的 TIOBE 软件最受欢迎的编程语言列表中&#xff0c;Python、C 和 C 保持了它们的领先地位&#xff08;图 A&#xff09;。TIOBE 的专有积分系统考虑了根据多种大型搜索引擎&…

Tailscale实现内网穿透、异地组网、远程访问

文章目录 Tailscale简介主要功能适用场景使用Tailscale的优势如何开始使用Tailscale总结参考资料注册登录Tailscale账号并下载客户端禁用秘钥过期简单使用设备添加 - 组网Linux安装Tailscale,实现设备添加Tailscale 中的 DERP 简介什么是 DERP?DERP 的优势DERP 的工作原理DER…