sql

一些常用的SQL语句

Posted by kevin on November 13, 2019

preface

SQL 学过一点,但是没有怎么用,因此用的时候经常又要去看一遍教程,不如直接把经常会用到的语句给记录下来,下次直接看这一篇就行了。

本篇基于 Mysql 5.6.38,Windows 10 平台,参考廖雪峰 SQL 教程 and 菜鸟教程

操作数据库

列出所有的数据库

mysql> SHOW DATABASES;
+--------------------+ 
| Database           |
+--------------------+
| information_schema | 
| ctf                |
| mysql              |
| performance_schema | 
| sql                |
+--------------------+
5 rows in set (0.00 sec)

创建一个名叫 sql 的新数据库

mysql> CREATE DATABASE `sql`;
Query OK, 1 row affected (0.00 sec) 

MySQL 中的数据库,表,字段名,一般要用 `` 包围,以区分普通字符。但有些场合不包也不会报错,因此下面有些地方我就没有用。MySQL 关键字一般用大写,但是用小写也没啥事2333

删除名叫 sql 的数据库

mysql> DROP DATABASE `sql`;
Query OK, 0 rows affected (0.00 sec) 

对数据库进行操作前要切换为当前数据库

mysql> USE `ctf`; 
Database changed 

操作表

列出当前数据库中所有表

mysql> SHOW TABLES;
+---------------+ 
| Tables_in_ctf |
+---------------+
| first         |
+---------------+ 
1 row in set (0.00 sec)

创建一个新表

mysql> CREATE TABLE IF NOT EXISTS `my_tbl`( 
    ->    `my_id` INT UNSIGNED AUTO_INCREMENT, 
    ->    `my_title` VARCHAR(100) NOT NULL,
    ->    `my_author` VARCHAR(40) NOT NULL, 
    ->    `submission_date` DATE,
    ->    PRIMARY KEY ( `my_id` )
    -> )ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.27 sec) 

删除一个表

mysql> DROP TABLE `my_tbl`; 
Query OK, 0 rows affected (0.41 sec) 

查看某个表的结构

mysql> DESC first;
+--------+--------------+------+-----+---------+-------+ 
| Field  | Type         | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| name   | varchar(255) | NO   |     | NULL    |       | 
| age    | int(10)      | NO   |     | NULL    |       | 
| gender | varchar(2)   | NO   |     | NULL    |       |
+--------+--------------+------+-----+---------+-------+
3 rows in set (0.01 sec) 

查看创建某个表的SQL语句

mysql> SHOW CREATE TABLE first;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table
                                            |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| first | CREATE TABLE `first` ( 
  `name` varchar(255) NOT NULL,
  `age` int(10) NOT NULL, 
  `gender` varchar(2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

用 ALTER 对表进行修改

给表新增一列 birth 字段

mysql> ALTER TABLE `first` ADD COLUMN birth VARCHAR(10) NOT NULL; 
Query OK, 0 rows affected (2.32 sec) 
Records: 0  Duplicates: 0  Warnings: 0

修改 birth 名为 birthday,类型改为 VARCHAR(20)

mysql> ALTER TABLE `first` CHANGE COLUMN `birth` `birthday` VARCHAR(20) NOT NULL; 
Query OK, 4 rows affected (2.42 sec) 
Records: 4  Duplicates: 0  Warnings: 0

删除 birthday 列

mysql> ALTER TABLE `first` DROP COLUMN `birthday`; 
Query OK, 0 rows affected (2.20 sec) 
Records: 0  Duplicates: 0  Warnings: 0

操作数据

INSERT 插入

通用语法

INSERT INTO table_name ( field1, field2,...fieldN )
                       VALUES
                       ( value1, value2,...valueN );

上面这样的话就插入了一条记录,也可以一次性插入好多条记录:

mysql> INSERT INTO first (name, gender, age) 
    -> VALUES 
    -> ('Susan', 'f', 21),
    -> ('Rose', 'f', 20);
Query OK, 2 rows affected (0.89 sec) 
Records: 2  Duplicates: 0  Warnings: 0

DELETE 删除

通用语法

DELETE FROM table_name [WHERE Clause]

不用 where 条件的话整个表都会被删除,可以在 where 中指定任何条件,比如下面这段代码就删除了 age 为 12 的记录:

mysql> DELETE FROM first WHERE age=12;
Query OK, 1 row affected (0.05 sec) 

UPDATE 修改

通用语法

UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]

可以同时更新多个字段,一般是配合 where 使用,更新指定行的数据,下面我们更新 yuhui 的年龄:

mysql> UPDATE first SET age=18 WHERE name='yuhui';
Query OK, 1 row affected (1.69 sec) 
Rows matched: 1  Changed: 1  Warnings: 0

查询一下,已经被更新了:

mysql> SELECT * FROM first WHERE name='yuhui';     
+-------+-----+--------+ 
| name  | age | gender |
+-------+-----+--------+
| yuhui |  18 | f      |
+-------+-----+--------+
1 row in set (0.00 sec) 

SELECT 查询

通用语法

SELECT column_name1,column_name2
FROM table_name1, table_name2
[WHERE Clause]
[LIMIT N][ OFFSET M]

查询语句可以在多个表中查询,并且可以查询多个字段,返回的是一条或多条记录,可以用 LIMIT 属性来设定返回的记录数,用 OFFSET 指定 SELECT 语句开始查询的数据偏移量,默认情况下是 0,如果想查询所有字段信息的话,可以用 * 来代替 column_name

mysql> SELECT `name`, `age` FROM `first` WHERE  `gender`='f';
+-------+-----+ 
| name  | age |
+-------+-----+
| chan  |  17 |
| yuhui |  18 |
| kaiyi |  20 | 
+-------+-----+
3 rows in set (0.00 sec)

limit 关键字

limit 可以限制输出记录的条数,通用语法如下

select _column,_column from _table [where Clause] [limit N][offset M]

其中,N 是指返回的记录的条数,M 是从第几条记录开始查询,默认是 0,上面的语句又可以写成下面这样表示返回 N 条记录,偏移量为 M

select _column,_column from _table [where Clause] [limit M, N]
MariaDB [security]> select * from users limit 1 offset 0;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
+----+----------+----------+
MariaDB [security]> select * from users limit 0, 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
+----+----------+----------+

和系统有关的函数

MySQL 中有些函数可以获取系统以及当前数据表的一些信息,比如 user() 获取当前登录用户,database() 获取当前数据库名,version() 获取当前数据库版本,@@version_compile_os 获取当前操作系统名,@@datadir 获取数据库所在的路径


MariaDB [security]> select database();
+------------+
| database() |
+------------+
| security   |
+------------+

MariaDB [security]> select version();
+-------------------+
| version()         |
+-------------------+
| 10.3.20-MariaDB-1 |
+-------------------+

MariaDB [security]> select user();
+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+

MariaDB [security]> select @@version_compile_os;
+----------------------+
| @@version_compile_os |
+----------------------+
| debian-linux-gnu     |
+----------------------+

MariaDB [security]> select @@datadir;
+-----------------+
| @@datadir       |
+-----------------+
| /var/lib/mysql/ |
+-----------------+

order by

查询的结果根据某字段进行排序,常用格式如下

SELECT field1, field2,...fieldN FROM table_name1, table_name2...
ORDER BY field1 [ASC [DESC][默认 ASC]], [field2...] [ASC [DESC][默认 ASC]]

其中呢我们可以通过 ASC 和 DESC 来指定升序还是降序,默认情况下是升序,而且也可以用数字来代替字段名,如 1 代表第一个字段,2 代表第二个字段

MariaDB [dvwa]> select first_name, last_name from users order by first_name;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin      | admin     |
| Bob        | Smith     |
| Gordon     | Brown     |
| Hack       | Me        |
| Pablo      | Picasso   |
+------------+-----------+

MariaDB [dvwa]> select first_name, last_name from users order by 1;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin      | admin     |
| Bob        | Smith     |
| Gordon     | Brown     |
| Hack       | Me        |
| Pablo      | Picasso   |
+------------+-----------+

union 联合查询

在 sql 注入中 union 注入是非常常见的一种,union 查询一般语法如下

SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT]
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];

也就是将多个表中查询出来的结果放在一起,但是要注意,前后查询的字段数一定要一致,否则就会报错,如果前后查询的数据有重复的话,MySQL 默认是将重复的数据给删除,这和 DISTINCT 关键字的功能一样,如果想保留相同记录的话可以用 union ALL ,返回所有的结果集

MariaDB [dvwa]> select first_name, last_name from users union select username from security.users;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
# 前后查询字段数不一样所以报错

MariaDB [dvwa]> select first_name from users union select username from security.users;
+------------+
| first_name |
+------------+
| admin      |
| Gordon     |
| Hack       |
| Pablo      |
| Bob        |
| admin1     |
+------------+

MariaDB [dvwa]> select first_name from users union ALL select username from security.users;
+------------+
| first_name |
+------------+
| admin      |
| Gordon     |
| Hack       |
| Pablo      |
| Bob        |
| admin      |
| admin1     |
+------------+

sql 注入时经常让前面一个查询的结果是假的,这样就可以只输出后面一句查询的结果,防止无效的信息干扰

MariaDB [dvwa]> select first_name  from users where 1=2 union select username from security.users;
+------------+
| first_name |
+------------+
| admin      |
| admin1     |
+------------+

group by 分组

分组一般和聚合函数一起用,聚合函数就是 SUM(), AVG(), COUNT() 等用于统计的一些函数,总的来说就是用在一些有重复记录的情况,比如表的名字叫做九年级,字段有班级和名字,要想统计每个班有多少学生就可以按照班级这个字段来进行分组

我们用一个表来说明一下分组是怎么使的

mysql> select * from first;
+--------+-----+--------+ 
| name   | age | gender |
+--------+-----+--------+
| kevin  |  18 | m      |
| chan   |  17 | f      | 
| ddddhm |  20 | fk     |
| yuhui  |  18 | f      |
| kaiyi  |  20 | f      |
+--------+-----+--------+ 

这里有些重复的字段记录,比如 gender 为 f 的有三个,我们就按照 gender 来分个组

mysql> select gender, age, name from first group by gender; 
+--------+-----+--------+ 
| gender | age | name   |
+--------+-----+--------+
| f      |  17 | chan   |
| fk     |  20 | ddddhm |
| m      |  18 | kevin  | 
+--------+-----+--------+

欸?怎么回事,怎么就三条记录了,其实分组之后我们可以看作单元格合并,也就是相同的 f 列的数据都合在了一个大的单元格里面,但是 select 总不能够取出两条记录来吧,所以他只取一条记录,不是说了吗,分组一般是用来统计的,所以不太关心具体的数据是啥,只关心有几条记录,用 count 来看一下

mysql> select gender, count(*) from first group by gender;      
+--------+----------+ 
| gender | count(*) |
+--------+----------+
| f      |        3 |
| fk     |        1 |
| m      |        1 |
+--------+----------+ 

这就好了, gender 为 f 的有三条记录,其余的记录为 1,这就是分组的一个简单实践