SQL语句参考

-- TOC --

本文总结一些日常使用到的SQL语句的使用细节,在编码时随时给自己参考。

REPLACE

从应用程序的角度看,SQL语言中的REPLACE语句是非常有实用价值的,因为很多时候,一张table里的数据,你不知道哪一个会先到,哪一个后到,也有可能相同位置的数据会来很多次。

此时用replace语句是最明智的选择,代码会比较简单。replace into的含义,即如果row存在就update,如果row不存在就insert。

能不能用replace,要看table的设计,因为区分row需要primary key。如果是明确更新某一行,有primary key在手,可以replace,没有的话,就要先查询primary key,然后再replace。有的时候,查询后用update,语义可能更清晰一些。有的时候,没有primary key,但是有一个数据是unique的,直接update后跟where更简单吧。

需要注意的是,table需要至少有一个有unique约束的column来给replace into语句定位。否则,就是无脑insert。在有unique约束的table中insert,可能出Error,而用replace,就不会出Error,直接干!

sqlite> create table t3(id integer primary key, sn int, content text);
sqlite> insert into t3(id,sn,content) values (null,1,'a');
sqlite> insert into t3(id,sn,content) values (null,2,'b');
sqlite> insert into t3(id,sn,content) values (null,3,'c');
sqlite> select * from t3;
id  sn  content
--  --  -------
1   1   a
2   2   b
3   3   c
sqlite> replace into t3(id,sn,content) values (1,10,'aa');
sqlite> replace into t3(id,sn,content) values (2,20,'bb');
sqlite> replace into t3(id,sn,content) values (3,30,'cc');
sqlite> select * from t3;
id  sn  content
--  --  -------
1   10  aa
2   20  bb
3   30  cc

UPDATE

SQL语言中的UPDATE语句,用来更新某row的数据,或者同时更新很多row的数据。所以,replace into语句是不能完全代替update的。

update语句用来更新所有满足SQL表达式的row!这一点很重要...

下面的case,update语句不带where条件,直接将所有row更新:

sqlite> select * from t3;
id  sn  content
--  --  -------
1   10  aa
2   20  bb
3   30  cc
sqlite> update t3 set sn=12345;
sqlite> select * from t3;
id  sn     content
--  -----  -------
1   12345  aa
2   12345  bb
3   12345  cc

给update设一个where条件,更新的范围就会缩小到一部分row,或者精确定位到某一条row:

sqlite> insert into t3(id,sn,content) values (null,321,'g');
sqlite> insert into t3(id,sn,content) values (null,321,'g');
sqlite> insert into t3(id,sn,content) values (null,321,'g');
sqlite> insert into t3(id,sn,content) values (null,321,'g');
sqlite> insert into t3(id,sn,content) values (null,321,'g');
sqlite> select * from t3;
id  sn     content
--  -----  -------
1   12345  aa
2   12345  bb
3   12345  cc
4   321    g
5   321    g
6   321    g
7   321    g
8   321    g
sqlite> update t3 set content='xinlin' where sn=321;
sqlite> select * from t3;
id  sn     content
--  -----  -------
1   12345  aa
2   12345  bb
3   12345  cc
4   321    xinlin
5   321    xinlin
6   321    xinlin
7   321    xinlin
8   321    xinlin

ALTER TABLE

SQLite3数据库只支持很有限的alter table语句,即只能做rename和add column。

rename table

sqlite> .table
jsond  pnum   ssd    timed
sqlite> .mode column
sqlite> select * from pnum;
pid  pnum          tid
---  ------------  ---
1    GM2020090501  1
2    GM2020090401  2
3    GM2020090601  3
4    GM2020090801  7
5    GM2020090901  8
6    GM2020091101  9
7    GM2020091001  10
8    GM2020090701  11
9    GM2020091301  12
10   GM2020091701  13
11   GM2020091601  14
12   GM2020091901  15
13   GM2020091201  16
14   GM2020091401  18
15   GM2020091801  19
16   GM2020092001  20
17   GM2020091501  21
sqlite> alter table pnum rename to abcd;
sqlite> .table
abcd   jsond  ssd    timed
sqlite> select * from abcd;
pid  pnum          tid
---  ------------  ---
1    GM2020090501  1
2    GM2020090401  2
3    GM2020090601  3
4    GM2020090801  7
5    GM2020090901  8
6    GM2020091101  9
7    GM2020091001  10
8    GM2020090701  11
9    GM2020091301  12
10   GM2020091701  13
11   GM2020091601  14
12   GM2020091901  15
13   GM2020091201  16
14   GM2020091401  18
15   GM2020091801  19
16   GM2020092001  20
17   GM2020091501  21

以上测试,将表pnum修改为abcd,里面的数据保持不变。

add column

sqlite> .table
abcd   jsond  ssd    timed
sqlite> select * from abcd;
pid  pnum          tid
---  ------------  ---
1    GM2020090501  1
2    GM2020090401  2
3    GM2020090601  3
4    GM2020090801  7
5    GM2020090901  8
6    GM2020091101  9
7    GM2020091001  10
8    GM2020090701  11
9    GM2020091301  12
10   GM2020091701  13
11   GM2020091601  14
12   GM2020091901  15
13   GM2020091201  16
14   GM2020091401  18
15   GM2020091801  19
16   GM2020092001  20
17   GM2020091501  21
sqlite> alter table abcd add a7 text;
sqlite> select * from abcd;
pid  pnum          tid  a7
---  ------------  ---  --
1    GM2020090501  1
2    GM2020090401  2
3    GM2020090601  3
4    GM2020090801  7
5    GM2020090901  8
6    GM2020091101  9
7    GM2020091001  10
8    GM2020090701  11
9    GM2020091301  12
10   GM2020091701  13
11   GM2020091601  14
12   GM2020091901  15
13   GM2020091201  16
14   GM2020091401  18
15   GM2020091801  19
16   GM2020092001  20
17   GM2020091501  21
sqlite> alter table abcd add a text unique;
Error: Cannot add a UNIQUE column

以上测试,给表abcd增加a7列,增加后,这一列默认全部为null。增加a8列的时候出错,SQLite3不允许直接增加有unique属性的列。

修改table的column定义

SQLite并不能直接修改table的column,alter table语句只能修改table的name或者增加column。以下记录一个成功的变相修改table column的操作流程。

在数据库系统的维护过程中,总是免不了要对table做各种修改,对数据倒来倒去...

JOIN

cross join

又称为笛卡尔乘积(Carterian Product),cross join简单粗暴,两个表格分别有a列和b列,分别有m行和n行,cross join之后得到的表格,就是a+b列,m*n行。

sqlite> create table aa(id integer primary key, sn int, content text);
sqlite> insert into aa(id,sn,content) values (null,1,'a');
sqlite> insert into aa(id,sn,content) values (null,2,'b');
sqlite> insert into aa(id,sn,content) values (null,3,'c');
sqlite> select * from aa;
1|1|a
2|2|b
3|3|c
sqlite> .mode column
sqlite> .header on
sqlite> create table bb(id int unique not null, seq int);
sqlite> insert into bb(id,seq) values (4,4);
sqlite> insert into bb(id,seq) values (5,5);
sqlite> select * from bb;
id  seq
--  ---
4   4
5   5
sqlite> select * from aa cross join bb;
id  sn  content  id  seq
--  --  -------  --  ---
1   1   a        4   4
1   1   a        5   5
2   2   b        4   4
2   2   b        5   5
3   3   c        4   4
3   3   c        5   5
sqlite>
sqlite> select * from aa join bb;
id  sn  content  id  seq
--  --  -------  --  ---
1   1   a        4   4
1   1   a        5   5
2   2   b        4   4
2   2   b        5   5
3   3   c        4   4
3   3   c        5   5

使用cross join需要注意,这个操作有可能会产生一个非常非常大的表格!你要知道你在做什么。。

inner join

INNER JOIN不带条件的时候,就是CROSS JOIN,如上示例。

两个表格分别a列和b列,m行和n行,inner join后的到的table,列数小于等于a+b,行数小于等于m*n。因为 inner join 有条件,因此得到的表格会比无条件的cross join小。

sqlite> select * from aa;
id  sn  content
--  --  -------
1   1   a
2   2   b
3   3   c
sqlite> select * from bb;
id  seq
--  ---
4   4
5   5
sqlite> insert into bb(id,seq) values (1,7);
sqlite> insert into bb(id,seq) values (2,8);
sqlite> select * from aa join bb on aa.id=bb.id;
id  sn  content  id  seq
--  --  -------  --  ---
1   1   a        1   7
2   2   b        2   8

当aa.id=bb.id的row,才进入结果table。

使用 ON 来表示条件,是最通用的,但是表达式写起来稍微有点长,而且结果table会存在相同的列。

如果两个表的列名称相同,可以用USING来表达条件:

sqlite> select * from aa join bb on aa.id=bb.id;
id  sn  content  id  seq
--  --  -------  --  ---
1   1   a        1   7
2   2   b        2   8
sqlite> select * from aa join bb using(id);
id  sn  content  seq
--  --  -------  ---
1   1   a        7
2   2   b        8

sql表达式简单了写,而且结果table中的重复列也没有了。使用 USING 的前提是,列的名称要相同。

sqlite> select * from aa join bb using(id);
id  sn  content  seq
--  --  -------  ---
1   1   a        7
2   2   b        8
sqlite> select * from aa natural join bb;
id  sn  content  seq
--  --  -------  ---
1   1   a        7
2   2   b        8

NATURAL JOIN 会自动的去查看相同的列名,然后对他们进行 inner join 操作。

NATURAL JOIN 这个操作有一个危险点:因为它是自动去查看相同列名,如果两个表中没有相同的列名,它也会自动将 join 降级为 cross join。

sqlite> select * from sqlite_master;
type   name                   tbl_name  rootpage  sql
-----  ---------------------  --------  --------  -------------------------------------------------------------
table  aa                     aa        2         CREATE TABLE aa(id integer primary key, sn int, content text)
table  bb                     bb        3         CREATE TABLE bb(id int unique not null, seq int)
index  sqlite_autoindex_bb_1  bb        4
sqlite>
sqlite> create table cc(cat int, dog int);
sqlite> insert into cc(cat,dog) values (1,1);
sqlite> insert into cc(cat,dog) values (1,1);
sqlite> insert into cc(cat,dog) values (1,1);
sqlite> insert into cc(cat,dog) values (1,1);
sqlite> insert into cc(cat,dog) values (1,1);
sqlite> select * from aa natural join cc;
id  sn  content  cat  dog
--  --  -------  ---  ---
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
2   2   b        1    1
2   2   b        1    1
2   2   b        1    1
2   2   b        1    1
2   2   b        1    1
3   3   c        1    1
3   3   c        1    1
3   3   c        1    1
3   3   c        1    1
3   3   c        1    1
sqlite> select * from aa join cc using(id);
Error: cannot join using column id - column not present in both tables
sqlite> select * from aa join cc on aa.id=cc.cat;
id  sn  content  cat  dog
--  --  -------  ---  ---
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1

而使用 ON 或者 USING,都没有这样的危险!

outer join

SQL语言定义了3中OUTER JOIN,分别是 LEFT, RIGHT和FULL,SQLite只支持 LEFT OUTER JOIN。

OUTER JOIN 是 INNER JOIN 的扩展。在执行完 INNER JOIN 的动作后,再讲LEFT table(左边的那个)中没有出现在结果table中的row,填入结果table,多出来的列(右边table中的列)填NULL。这就是OUTER JOIN。

sqlite> select * from aa outer join cc on aa.id=cc.cat;
Error: RIGHT and FULL OUTER JOINs are not currently supported
sqlite>
sqlite> select * from aa left outer join cc on aa.id=cc.cat;
id  sn  content  cat  dog
--  --  -------  ---  ---
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
1   1   a        1    1
2   2   b
3   3   c

其实,在SQL语言表达式中,将右两个table互换,就是RIGHT OUTER JOIN了。

从 OUTER JOIN 的特点分析,LEFT table 中的所有row,都会一定出现的结果table中!

本文链接:https://cs.pynote.net/sf/sqlite/202203205/

-- EOF --

-- MORE --