关系型数据库,
数据库由若干张表
(Table)组成,这里说的数据Table很像Excel里的表; 正如Excel里的表格,Table也是由 行(rows)
和列(columns)
组成
一个Table存储一个类别的数据,每一行是一条数据,每一列是这种数据的一个属性; Table就像一个二维的表格,列(columns)
是有限固定的,行(rows)
是无限不固定的
SELECT
语句, 通常又称为 查询 (queries),SELECT
可以用来从数据库中取出数据
Select 查询某些属性列(specific columns)的语法
SELECT column(列名), another_column, …
FROM mytable(表名);
查询的结果是一个二维的表格,由行(rows)和列(columns)组成, 看起来像是复制了一遍原有的表(Table),只不过列是我们选定的,而不是所有的列.、
如果我们想取出所有列的数据, 当然可以把所有列名写上,不过更简单的方式用星号 (*
) 来代表所有列.如下:
Select 查询所有列
SELECT *
FROM mytable(表名);
SELECT column, another_column, …
FROM mytable
WHERE condition
AND/OR another_condition
AND/OR …;
注:这里的 condition 都是描述属性列的,
可以用 AND
or OR
这两个关键字来组装多个条件(表示并且,或者) (ie. num_wheels >= 4 AND doors <= 2 这个组合表示 num_wheels属性 大于等于 4 并且 doors 属性小于等于 2).
如果属性是字符串, 我们会用到字符串相关的一些操作符号,其中 LIKE(模糊查询) 和 %(通配符)
字符串用单引号
选取出唯一的结果的语法
SELECT DISTINCT column, another_column, …
FROM mytable
WHERE condition(s);
DISTINCT
语法会直接删除重复的行, GROUP BY
也会返回唯一的行,不过可以对具有相同的 属性值的行做一些统计计算,例如结果排序
在实际的数据表中,数据添加的时候不是完全顺序的,比如我们实际的Dog表,不会是按狗的身高从小到大去添加数据,当数据量成千上万之后,如果结果不做任何排序,结果会看起来很错乱.
为了解决结果排序问题, 我们可以用 ORDER BY col_name
排序的语法来让结果按一个或多个属性列做排序.
SELECT column, another_column, …
FROM mytable
WHERE condition(s)
ORDER BY column ASC/DESC;
ORDER BY col_name
这句话的意思就是让结果按照 col_name 列的具体值做 ASC升序 或 DESC 降序
LIMIT
和 OFFSET
子句通常和ORDER BY
语句一起使用,当我们对整个结果集排序之后,
我们可以 LIMIT
来指定只返回多少行结果 ,用 OFFSET
来指定从哪一行开始返回
SELECT column, another_column, …
FROM mytable
WHERE condition(s)
ORDER BY column ASC/DESC
LIMIT num_limit OFFSET num_offset;
用JOINs进行多表联合查询
关系数据库的范式
数据库范式是数据表设计的规范,在范式规范下,数据库里每个表存储的重复数据降到最少(这有助于数据的一致性维护),同时在数据库范式下,表和表之间不再有很强的数据耦合,可以独立的增长 (ie. 比如汽车引擎的增长和汽车的增长是完全独立的). 范式带来了很多好处,但随着数据表的分离,意味着我们要查询多个数据属性时,需要更复杂的SQL语句
如果一个实体(比如Dog)的属性数据被分散到多个数据表中,我们就需要学习如何通过 JOIN
连表技术来整合这些数据并找到我们想要查询的数据项.
主键(primary key)
, 一般关系数据表中,都会有一个属性列设置为 主键(primary key)
。主键是唯一标识一条数据的,不会重复复(想象你的身份证号码)。一个最常见的主键就是auto-incrementing integer(自增ID,每写入一行数据ID+1, 当然字符串,hash值等只要是每条数据是唯一的也可以设为主键.
借助主键(primary key)
(当然其他唯一性的属性也可以),我们可以把两个表中具有相同 主键ID的数据连接起来(因为一个ID可以简要的识别一条数据,所以连接之后还是表达的同一条数据)(你可以想象一个左右连线游戏)。具体我们用到 JOIN
关键字
SELECT column, another_table_column, …
FROM mytable (主表)
INNER JOIN another_table (要连接的表)
ON mytable.id = another_table.id (想象一下刚才讲的主键连接,两个相同的连成1条)
WHERE condition(s)
ORDER BY column, … ASC/DESC
LIMIT num_limit OFFSET num_offset;
外连接
INNER JOIN
只会保留两个表都存在的数据,这看起来意味着一些数据的丢失,在某些场景下会有问题.
左连接LEFT JOIN
,右连接RIGHT JOIN
和 全连接FULL JOIN
. 这几个 连接方式都会保留不能匹配的行
SELECT column, another_column, …
FROM mytable
INNER/LEFT/RIGHT/FULL JOIN another_table
ON mytable.id = another_table.matching_id
WHERE condition(s)
ORDER BY column, … ASC/DESC
LIMIT num_limit OFFSET num_offset;
和INNER JOIN
语法几乎是一样的
在表A 连接 B, LEFT JOIN
保留A的所有行,不管有没有能匹配上B 反过来 RIGHT JOIN
则保留所有B里的行。
最后FULL JOIN
不管有没有匹配上,同时保留A和B里的所有行
将两个表数据1-1连接,保留A或B的原有行,如果某一行在另一个表不存在,会用 NULL来填充结果数据。所有在用这三个JOIN时,你需要单独处理 NULL
.
在数据库中,NULL
表达的是 "无"的概念,或者说没有东西
需要在编写SQL时考虑到某个属性列可能是 NULL的情况, 这种特殊性会造成编写SQL的复杂性,所以没有必要的情况下,我们应该尽量减少 NULL
的使用,让数据中尽可能少出现 NULL
的情况
如果某个字段你没有填写到数据库,很可能就会出现NULL
。所有一个常见的方式就是为字段设置默认值
,比如 数字的默认值设置为0,字符串设置为 ""字符串. 但是在一些NULL
表示它本来含义的场景,需要注意是否设置默认值还是保持NULL
还有一些情况很难避免 NULL
的出现, 比如之前说的 outer-joining 多表连接,A和B有数据差异时,必须用 NULL
来填充。这种情况,可以用IS NULL
和 IS NOT NULL
来选在某个字段是否等于 NULL
.
SELECT column, another_column, …
FROM mytable
WHERE column IS/IS NOT NULL
AND/OR another_condition
AND/OR …;
在查询中使用表达式
之前我们在SQL中的出现col_name(属性名)的 地方,我们都只是写上col_name自身。其实在SQL中可以用col_name的地方,
都可以用表达式 来指定对属性进行一定的计算或处理
SELECT particle_speed / 2.0 AS half_particle_speed (对结果做了一个除2)
FROM physics_data
WHERE ABS(particle_position) * 10.0 >500
(条件要求这个属性绝对值乘以10大于500);
每一种数据库(mysql,sqlserver等)都有自己的一套函数,包含常用的数字,字符串,时间等处理过程
当我们用表达式对col属性计算时,很多事可以在SQL内完成,这让SQL更加灵活,但表达式如果长了则很难一下子读懂。所以SQL提供了AS
关键字, 来给表达式取一个别名.
AS使用别名
SELECT col_expression AS expr_description, …
FROM mytable;
实际上AS不仅用在表达式别名上,普通的属性列甚至是表(table)都可以取一个别名,这让SQL更容易理解.
属性列和表取别名的例子
SELECT column AS better_column_name, …
FROM a_long_widgets_table_name AS mywidgets
INNER JOIN widget_sales
ON mywidgets.id = widget_sales.widget_id;