標准SQL包含了4種基本的語句類別:
- DDL語句,數據定義語句,主要用來定義數據庫,表名,字段,例如create,drop,alter.
- DML語句,數據操作語句,用來對數據記錄的增刪改查,還用來保證數據的一致性。主要有select,delete,insert,update語句。
- DCL語句,數據控制語句,用於控制不同數據對象訪問級別的語句。定義了數據庫、表、表、用戶的訪問權限和完全級別。常用的語句包括grant、revoke等
- TCL語句,事務控制語句,用來確保事務的特性。
CREATE TABLE建表語句
在介紹建表語句之前,先簡單說明一下創建數據庫的語句。
mysql> create database mytest; #創建數據庫 Query OK, 1 row affected (0.00 sec) mysql> use mytest; #改變當前的數據庫 Database changed mysql> select database(); #查看當前選中的數據庫 +------------+ | database() | +------------+ | mytest | +------------+ 1 row in set (0.00 sec) mysql> select user(); #查看當前登錄的用戶 +----------------+ | user() | +----------------+ | root@localhost | +----------------+ 1 row in set (0.00 sec) mysql> show create database mytest; #查看創建數據庫的語句,默認添加了字符集,會在字符集中介紹 +----------+-------------------------------------------------------------------+ | Database | Create Database | +----------+-------------------------------------------------------------------+ | mytest | CREATE DATABASE `mytest` /*!40100 DEFAULT CHARACTER SET latin1 */ | +----------+-------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select version(); #查看當前數據庫的版本 +------------+ | version() | +------------+ | 5.7.22-log | +------------+ 1 row in set (0.00 sec) mysql>
數據庫創建之后,然后就是建表:
建表語句的作用就是在數據庫創建一張二維表,因此在建表語句要指定每一個字段名(二維表中的列名),還有要指定對填入這些字段的數據的限制(約束條件),同時建表語句還可以指定這張表的字符集,以及之前規划好的索引等。 create table tb1( c1 int auto_increment primary key, c2 varchar(20) ); 創建了tb1表,表中有兩列(兩個字段), auto_increment: 指定字段c1為自增字段,mysql中一個表中只能有一個自增字段,且須為主鍵。 mysql> show create table tb1\G *************************** 1. row *************************** Table: tb1 Create Table: CREATE TABLE `tb1` ( `c1` int(11) NOT NULL AUTO_INCREMENT, `c2` varchar(20) DEFAULT NULL, PRIMARY KEY (`c1`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec) #查看建表語句,mysql會默認選擇表的存儲引擎和字符集 mysql> system ls /data/mysql/mytest/tb1.* /data/mysql/mytest/tb1.frm /data/mysql/mytest/tb1.ibd #在datadir對應的目錄下面會生成對應的表結構文件tb1.frm,和數據文件tb1.ibd。
MySQL支持在建表時指定temporary參數,這樣創建的表是臨時表,臨時表是基於會話級別的表。
#創建臨時表,臨時表使用show tables查不到其存在,但是可以查看表結構,也可以向臨時表插入數據
mysql> create temporary table tb2(id int, info varchar(20)); Query OK, 0 rows affected (0.00 sec) mysql> show tables; +------------------+ | Tables_in_mytest | +------------------+ | tb1 | +------------------+ 1 row in set (0.00 sec) mysql> show create table tb2; +-------+--------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+--------------------------------------------------------------------------------------------------------------------------------------+ | tb2 | CREATE TEMPORARY TABLE `tb2` ( `id` int(11) DEFAULT NULL, `info` varchar(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 | +-------+--------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> insert into tb2 values(1,"a"); Query OK, 1 row affected (0.00 sec)
#臨時表的表結構存在於/tmp/目錄下面
mysql> show variables like "tmpdir";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| tmpdir | /tmp |
+---------------+-------+
1 row in set (0.01 sec)
[root@test3 tmp]# pwd
/tmp
[root@test3 tmp]# ls
#sql2966_99_0.frm
#臨時表的數據文件在MySQL5.7之后由專門的文件存儲
mysql> show variables like "innodb%temp%";
+----------------------------+-----------------------+
| Variable_name | Value |
+----------------------------+-----------------------+
| innodb_temp_data_file_path | ibtmp1:12M:autoextend |
+----------------------------+-----------------------+
1 row in set (0.00 sec)
#臨時表有專門的存儲引擎
mysql> show variables like "default%tmp%";
+----------------------------+--------+
| Variable_name | Value |
+----------------------------+--------+
| default_tmp_storage_engine | InnoDB |
+----------------------------+--------+
1 row in set (0.00 sec)
#臨時表只對當前會話有效,當前會話斷開,臨時表會自動刪除,在其余的會話也看不到臨時表。
mysql> insert into tb2 values(1,"a");
ERROR 1146 (42S02): Table 'mytest.tb2' doesn't exist
mysql>
crate table語句還有很多參數可以使用,這些只是基本的用法,可以查看官方文檔,也可以查看work bench中的介紹。
刪除數據庫和表
#刪除數據庫 mysql> drop database mytesti; Query OK, 0 rows affected (0.00 sec) #刪除表,和表結構一起刪除 mysql> drop table tb1; Query OK, 0 rows affected (0.02 sec) #刪除表中的所有記錄,但是不刪除表結構 mysql> truncate tb4; Query OK, 0 rows affected (0.04 sec) #delete用來刪除表中的數據 mysql> delete table tb2; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'table tb2' at line 1 mysql> delete from tb2 where c = 1; Query OK, 0 rows affected (0.00 sec)
修改表結構
#創建如下表 CREATE TABLE IF NOT EXISTS tb2 ( id INT, NAME VARCHAR (20), email VARCHAR (50) ); #查看表的結構如下: mysql> desc tb2; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | NAME | varchar(20) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) #修改表名 // ALTER TABLE OLD_TB_NAME RENAME [TO] NEW_TB_NAME; mysql> alter table tb2 rename to tb3; Query OK, 0 rows affected (0.00 sec) // TO可以省略 mysql> alter table tb3 rename tb2; Query OK, 0 rows affected (0.00 sec)
#修改字段數據類型(把id字段的int類型修改為varchar類型)
// ALTER TABLE TBNAME MODIFY 字段名 新屬性
ALTER TABLE tb2 MODIFY id VARCHAR(10);
mysql> desc tb2;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | varchar(10) | YES | | NULL | |
| NAME | varchar(20) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
#修改字段名 (把上面的id字段名修改為user_id)
ALTER TABLE TBNAME CHANGE 舊字段名 新字段名 約束條件;
#需要注意的是這種方法不僅可以修改字段名,還可以修改字段的數據類型。
ALTER TABLE tb2 change id user_id varchar(10);
#增加字段
//ALTER TABLE 表名 ADD 新字段名 date FIRST|AFTER 字段A名。 first表示新加的字段在A的前面,after表示在A的后面。
ALTER TABLE tb2 ADD birth date; #默認的新增字段是在最后插入的。
mysql> desc tb2;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| user_id | varchar(10) | YES | | NULL | |
| NAME | varchar(20) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
| birth | date | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
#刪除字段
//ALTER TABLE 表名 DROP 字段名;
ALTER TABLE tb2 DROP user_id;
#字段排序:
ALTER TABLE TBNAME MODIFY 字段1 數據類型 FIRST|AFTER 字段2.
#更改表的存儲引擎:
ALTER TABLE 表名 engine = "存儲引擎名"
#刪除外鍵
ALTER TABLE 表名 FOREIGN KEY 外鍵別名。
insert插入數據
insert用於向表中插入數據。
#默認插入所有的字段 insert into tb2 values(1,"a"); #插入指定的字段 insert into tb2(id) values("3"); #一次插入多個數值 insert into tb2 values(4,"c"),(5,"d"),(6,"e"); mysql> select * from tb2; +------+------+ | id | NAME | +------+------+ | 1 | a | | 3 | NULL | | 4 | c | | 5 | d | | 6 | e | +------+------+ 5 rows in set (0.00 sec) mysql>
update更新數據
update更新語句一般於where條件句聯合使用。
mysql> update tb2 set name = "b" where id =3; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 #如果不使用where條件語句限制,則更新表中所有的行
select查詢
在查詢之前先導入MySQL官方提供的employeeso庫數據。
下載地址:https://github.com/datacharmer/test_db/archive/master.zip
方法:直接下載zip壓縮包,然后直接導入employees.sql文件即可
導入的表,各個表之間的關系如下:

單表查詢
mysql> select * from employees limit 1; +--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | +--------+------------+------------+-----------+--------+------------+ 1 row in set (0.00 sec) mysql> select emp_no, concat(first_name, " ", last_name) as full_name, gender from employees limit 1; +--------+----------------+--------+ | emp_no | full_name | gender | +--------+----------------+--------+ | 10001 | Georgi Facello | M | +--------+----------------+--------+ 1 row in set (0.01 sec) #查詢可以使用*號代替表中所有的字段,也可以使用對應字段的字符,只查詢出對應的要查詢的字段。 #concat函數,就是連接字符串,在這里和as結合,連接了兩個字段,並且重命名為full_name。
MySQL中有許多內嵌的函數可以調用,詳細的函數列表參照:https://dev.mysql.com/doc/refman/5.7/en/string-functions.html 可以使用Google瀏覽器打開,可以翻譯為中文!
單表查詢和一些條件語句結合:
查看表中記錄的數量
mysql> select count(*) from employees; +----------+ | count(*) | +----------+ | 300024 | +----------+ 1 row in set (0.53 sec)
使用order by語句按照某字段排序:
#默認是按照正序排列 mysql> select * from employees order by emp_no limit 3; +--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 10001 | 1953-09-02 | Georgi | Facello | M | 1986-06-26 | | 10002 | 1964-06-02 | Bezalel | Simmel | F | 1985-11-21 | | 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 | +--------+------------+------------+-----------+--------+------------+ 3 rows in set (0.00 sec) #加入關鍵字按照倒序排列 mysql> select * from employees order by emp_no desc limit 3; +--------+------------+------------+-----------+--------+------------+ | emp_no | birth_date | first_name | last_name | gender | hire_date | +--------+------------+------------+-----------+--------+------------+ | 499999 | 1958-05-01 | Sachin | Tsukuda | M | 1997-11-30 | | 499998 | 1956-09-05 | Patricia | Breugel | M | 1993-10-13 | | 499997 | 1961-08-03 | Berhard | Lenart | M | 1986-04-21 | +--------+------------+------------+-----------+--------+------------+ 3 rows in set (0.00 sec)
使用where條件語句:
#查找部門經理表中,員工號為d006的記錄 mysql> select * from dept_manager where dept_no = "d006"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110725 | d006 | 1985-01-01 | 1989-05-06 | | 110765 | d006 | 1989-05-06 | 1991-09-12 | | 110800 | d006 | 1991-09-12 | 1994-06-28 | | 110854 | d006 | 1994-06-28 | 9999-01-01 | +--------+---------+------------+------------+ 4 rows in set (0.06 sec) #where可以使用大於,小於,不等於 SELECT * FROM dept_manager where dept_no > "d006"; SELECT * FROM dept_manager where dept_no < "d004"; SELECT * FROM dept_manager where dept_no <> "d006"; #where語句也可以使用in關鍵字 mysql> select * from dept_manager where dept_no in ("d006", "d001"); +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110022 | d001 | 1985-01-01 | 1991-10-01 | | 110039 | d001 | 1991-10-01 | 9999-01-01 | | 110725 | d006 | 1985-01-01 | 1989-05-06 | | 110765 | d006 | 1989-05-06 | 1991-09-12 | | 110800 | d006 | 1991-09-12 | 1994-06-28 | | 110854 | d006 | 1994-06-28 | 9999-01-01 | +--------+---------+------------+------------+ 6 rows in set (0.00 sec) #使用and關鍵字(與關系) mysql> select * from dept_manager where dept_no = "d006" and to_date = "9999-01-01"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110854 | d006 | 1994-06-28 | 9999-01-01 | +--------+---------+------------+------------+ 1 row in set (0.00 sec) #使用or關鍵字(或關系) mysql> select * from dept_manager where dept_no = "d006" or to_date = "9999-01-01"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110039 | d001 | 1991-10-01 | 9999-01-01 | | 110114 | d002 | 1989-12-17 | 9999-01-01 | | 110228 | d003 | 1992-03-21 | 9999-01-01 | | 110420 | d004 | 1996-08-30 | 9999-01-01 | | 110567 | d005 | 1992-04-25 | 9999-01-01 | | 110725 | d006 | 1985-01-01 | 1989-05-06 | | 110765 | d006 | 1989-05-06 | 1991-09-12 | | 110800 | d006 | 1991-09-12 | 1994-06-28 | | 110854 | d006 | 1994-06-28 | 9999-01-01 | | 111133 | d007 | 1991-03-07 | 9999-01-01 | | 111534 | d008 | 1991-04-08 | 9999-01-01 | | 111939 | d009 | 1996-01-03 | 9999-01-01 | +--------+---------+------------+------------+ 12 rows in set (0.00 sec) #使用between and關鍵字,在兩個數值之間 mysql> select * from dept_manager where dept_no between "d003" and "d004"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110183 | d003 | 1985-01-01 | 1992-03-21 | | 110228 | d003 | 1992-03-21 | 9999-01-01 | | 110303 | d004 | 1985-01-01 | 1988-09-09 | | 110344 | d004 | 1988-09-09 | 1992-08-02 | | 110386 | d004 | 1992-08-02 | 1996-08-30 | | 110420 | d004 | 1996-08-30 | 9999-01-01 | +--------+---------+------------+------------+ 6 rows in set (0.00 sec) #使用like關鍵字,其中“%”代表任意字符,“_”代表一個字符。 mysql> select * from dept_manager where dept_no like "d%4"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110303 | d004 | 1985-01-01 | 1988-09-09 | | 110344 | d004 | 1988-09-09 | 1992-08-02 | | 110386 | d004 | 1992-08-02 | 1996-08-30 | | 110420 | d004 | 1996-08-30 | 9999-01-01 | +--------+---------+------------+------------+ 4 rows in set (0.00 sec) mysql> select * from dept_manager where dept_no like "d0_4"; +--------+---------+------------+------------+ | emp_no | dept_no | from_date | to_date | +--------+---------+------------+------------+ | 110303 | d004 | 1985-01-01 | 1988-09-09 | | 110344 | d004 | 1988-09-09 | 1992-08-02 | | 110386 | d004 | 1992-08-02 | 1996-08-30 | | 110420 | d004 | 1996-08-30 | 9999-01-01 | +--------+---------+------------+------------+ 4 rows in set (0.00 sec) mysql> select * from dept_manager where dept_no like "d_4"; Empty set (0.00 sec) #去重復, mysql> select dept_no from dept_manager limit 5; +---------+ | dept_no | +---------+ | d001 | | d001 | | d002 | | d002 | | d003 | +---------+ 5 rows in set (0.00 sec) mysql> select distinct dept_no from dept_manager limit 5; +---------+ | dept_no | +---------+ | d001 | | d002 | | d003 | | d004 | | d005 | +---------+ 5 rows in set (0.00 sec)
查詢分組:
#一個報錯:
mysql> select emp_no, dept_no from dept_manager group by dept_no; ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'employees.dept_manager.emp_no' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by #解決,更改sql_mode; http://www.ywnds.com/?p=8184
#按照部門分組 mysql> select emp_no, dept_no from dept_manager group by dept_no; +--------+---------+ | emp_no | dept_no | +--------+---------+ | 110022 | d001 | | 110085 | d002 | | 110183 | d003 | | 110303 | d004 | | 110511 | d005 | | 110725 | d006 | | 111035 | d007 | | 111400 | d008 | | 111692 | d009 | +--------+---------+ 9 rows in set (0.10 sec) #求出每個分組的元素數量 mysql> select count(emp_no), dept_no from dept_manager group by dept_no; +---------------+---------+ | count(emp_no) | dept_no | +---------------+---------+ | 2 | d001 | | 2 | d002 | | 2 | d003 | | 4 | d004 | | 2 | d005 | | 4 | d006 | | 2 | d007 | | 2 | d008 | | 4 | d009 | +---------------+---------+ 9 rows in set (0.00 sec) #列出每一個分組的元素 mysql> select group_concat(emp_no), dept_no from dept_manager group by dept_no; +-----------------------------+---------+ | group_concat(emp_no) | dept_no | +-----------------------------+---------+ | 110022,110039 | d001 | | 110085,110114 | d002 | | 110183,110228 | d003 | | 110303,110344,110386,110420 | d004 | | 110511,110567 | d005 | | 110725,110765,110800,110854 | d006 | | 111035,111133 | d007 | | 111400,111534 | d008 | | 111692,111784,111877,111939 | d009 | +-----------------------------+---------+ 9 rows in set (0.00 sec) mysql>
group by聚合函數,加上with rollup函數,會統計一個總的結果在最后一行。
mysql> select count(emp_no), dept_no from dept_manager group by dept_no with rollup; +---------------+---------+ | count(emp_no) | dept_no | +---------------+---------+ | 2 | d001 | | 2 | d002 | | 2 | d003 | | 4 | d004 | | 2 | d005 | | 4 | d006 | | 2 | d007 | | 2 | d008 | | 4 | d009 | | 24 | NULL | +---------------+---------+ 10 rows in set (0.00 sec)
#加上having條件語句
mysql> select count(emp_no), dept_no from dept_manager group by dept_no with rollup having dept_no > "d006";
+---------------+---------+
| count(emp_no) | dept_no |
+---------------+---------+
| 2 | d007 |
| 2 | d008 |
| 4 | d009 |
+---------------+---------+
3 rows in set (0.00 sec)
查詢某個表的數據大小以及索引大小,以及數據和索引的總大小
USE information_schema; select b.TABLE_NAME, b.ddata, b.dindex, sum(b.ddata)+SUM(b.dindex) as total from ( SELECT TABLE_NAME, TRUNCATE (Data_length / 1024 / 1024, 2) AS ddata, TRUNCATE (INDEX_LENGTH / 1024 / 1024, 2) AS dindex FROM TABLES where TABLE_NAME = 't_hk_stock_news') as b; 執行結果如下 +-----------------+--------+--------+--------+ | TABLE_NAME | ddata | dindex | total | +-----------------+--------+--------+--------+ | t_hk_stock_news | 290.00 | 278.00 | 568.00 | +-----------------+--------+--------+--------+ 1 row in set (0.00 sec)
#這里面有個構建的新字段,然后再根據構建的新字段求二者的和。
聯合查詢
聯合查詢分為:內連接和外連接,其中外連接又包含左連接和右連接。
內連接:
需求1:求出經理人員的工號,姓名,性別,部門代號(暫時不考慮部門名稱)。(經理就是dept_manager中的員工)
#使用where條件語句聯合兩張表查詢 SELECT e.emp_no, concat( e.first_name, " ", e.last_name ), e.gender, dp.dept_no FROM employees AS e, dept_manager AS dp WHERE dp.emp_no = e.emp_no; #采用內聯合查詢的方法 SELECT e.emp_no, concat( e.first_name, " ", e.last_name ), e.gender, dp.dept_no FROM employees AS e INNER JOIN dept_manager AS dp ON dp.emp_no = e.emp_no;
由上面這個查詢可以體會一下的連接的含義: 把兩張或多張表中,相同的字段聯合起來的的查詢,當值相等時,就會查詢出其結果。
上面的查詢中,我們再加入一張表,把部門的代號換為部門名稱。
SELECT e.emp_no, concat( e.first_name, " ", e.last_name ), e.gender, dp.dept_no, dep.dept_name FROM employees AS e, dept_manager AS dp, departments AS dep WHERE dp.emp_no = e.emp_no AND dp.dept_no = dep.dept_no; #使用inner聯合查詢的方式 SELECT e.emp_no, concat( e.first_name, " ", e.last_name ), e.gender, dp.dept_no, dep.dept_name FROM employees AS e INNER JOIN dept_manager AS dp ON dp.emp_no = e.emp_no INNER JOIN departments AS dep ON dp.dept_no = dep.dept_no;
外連接: 外連接分為左連接和右連接,這兩個連接的方式是一樣的,不同的是刷選數據的方式。
語法格式如下:
SELECT 字段名 FROM 表名1 LEFT| RIGHT 表名2 ON 表名1.字段名1 = 表名2.字段名2;
有以下的實例,我們來理解以下左查詢和右查詢:
mysql> select * from test1; +------+ | a | +------+ | 1 | | 2 | | 3 | | 4 | +------+ 4 rows in set (0.00 sec) mysql> select * from test2; +------+------+ | c | d | +------+------+ | 1 | a | | 2 | b | | c | d | +------+------+ 3 rows in set (0.00 sec) #左查詢的結果,左邊的數據會全部顯示,對應右表若沒有數據則為NULL mysql> select test1.a, test2.c, test2.d from test1 left join test2 on test1.a = test2.c; +------+------+------+ | a | c | d | +------+------+------+ | 1 | 1 | a | | 2 | 2 | b | | 3 | NULL | NULL | | 4 | NULL | NULL | +------+------+------+ 4 rows in set, 4 warnings (0.00 sec) #右查詢的結果,有表的數據會全部顯示,對應的左表數據若是沒有則為NULL。 mysql> select test1.a, test2.c, test2.d from test1 right join test2 on test1.a = test2.c; +------+------+------+ | a | c | d | +------+------+------+ | 1 | 1 | a | | 2 | 2 | b | | NULL | c | d | +------+------+------+ 3 rows in set, 4 warnings (0.00 sec) mysql>
在上面的employyes庫中表關系圖中,我們來做一個如下檢索。
- 要求求出普通員工的員工號,姓名(用一個字段顯示),性別,最近的薪水,最近的一個部門,和最近的一個title。
(因為在titles,salries,dept_emp表中,同一個工號員工有多條數據,因此我們可以根據時間,選擇最近的員工)。
上面的需求一步一步拆解:先求出普通員工的工號,姓名,性別和部門的代號,這個只涉及兩張表的查詢:
SELECT e.emp_no, concat( e.first_name, " ", e.last_name ) AS full_name, e.gender, dp.dept_no FROM employees AS e LEFT JOIN dept_manager AS dp ON dp.emp_no = e.emp_no WHERE dp.dept_no IS NULL; #這個表查詢處理有30萬條記錄,因此會比較慢,暫時不考慮性能,為了驗證這個結果,可以設置where條件dp.dept_no IS NOT NULL,這樣查出來的結果是經理的個人信息,恰好是24條。
再求出員工的最近的薪水:
看一下薪水表的數據:
mysql> select * from salaries where emp_no = "10002"; +--------+--------+------------+------------+ | emp_no | salary | from_date | to_date | +--------+--------+------------+------------+ | 10002 | 65828 | 1996-08-03 | 1997-08-03 | | 10002 | 65909 | 1997-08-03 | 1998-08-03 | | 10002 | 67534 | 1998-08-03 | 1999-08-03 | | 10002 | 69366 | 1999-08-03 | 2000-08-02 | | 10002 | 71963 | 2000-08-02 | 2001-08-02 | | 10002 | 72527 | 2001-08-02 | 9999-01-01 | +--------+--------+------------+------------+ 6 rows in set (0.00 sec) #我們要查詢得到的是這個表中,salary最近的那個值,也就是時間距現在最近,需要注意的是,時間最近的薪水不一定是最高的! mysql> select emp_no, max(from_date) from salaries group by emp_no limit 4; +--------+----------------+ | emp_no | max(from_date) | +--------+----------------+ | 10001 | 2002-06-22 | | 10002 | 2001-08-02 | | 10003 | 2001-12-01 | | 10004 | 2001-11-27 | +--------+----------------+ 4 rows in set (0.11 sec) #這樣我們得出的是,每個員工的最近的from_date,然后根據這兩個條件就可以求出員工最近的薪水。(這張表用的是復合索引,暫時先不提)
#SQL語句如下
SELECT
a.emp_no, a.salary
from salaries a
WHERE a.from_date = (SELECT max(b.from_date) from salaries b WHERE a.emp_no = b.emp_no GROUP BY b.emp_no);
#我們要的是員工的最近的薪水表,而不是最高的薪水表!
按照上面的方法我們可以求出,員工距離現在最近的部門代號,和title。
title的SQL語句仿照上面寫就可以了,但是員工的部門職稱,需要再聯合一張表查詢,結果如下:
SELECT a.emp_no, a.dept_no, dp.dept_name FROM dept_emp AS a, departments AS dp WHERE #WHERE條件句是一個and語句 from_date = ( SELECT #and語句的第一個條件是子查詢, max(from_date) FROM dept_emp AS b WHERE a.emp_no = b.emp_no GROUP BY emp_no ) AND dp.dept_no = a.dept_no #and語句的第二個條件語句 ORDER BY a.emp_no; #查詢的結果就是每個員工的最新職稱
把求出的三個查詢和最上面的普通員工信息的查詢聯合起來就是我們要得道的SQL查詢:
SELECT e.emp_no, CONCAT(e.first_name, ' ', e.last_name) AS full_name, dep_name.dept_name, c.salary, t.title FROM employees AS e LEFT JOIN #這個查詢求出普通員工的信息 dept_manager AS dp ON e.emp_no = dp.emp_no LEFT JOIN (SELECT #子查詢中求出員工最近的薪水 emp_no, salary FROM salaries a WHERE from_date = (SELECT MAX(from_date) FROM salaries b WHERE a.emp_no = b.emp_no GROUP BY emp_no)) c ON e.emp_no = c.emp_no LEFT JOIN (SELECT #子查詢中求出員工最近的title emp_no, title FROM titles a WHERE from_date = (SELECT MAX(from_date) FROM titles b WHERE a.emp_no = b.emp_no GROUP BY emp_no)) t ON e.emp_no = t.emp_no LEFT JOIN (SELECT #這個子查詢求出員工的最新的職稱信息 a.emp_no, a.dept_no, dp.dept_name FROM dept_emp AS a, departments AS dp WHERE from_date = (SELECT MAX(from_date) FROM dept_emp AS b WHERE a.emp_no = b.emp_no GROUP BY emp_no) AND dp.dept_no = a.dept_no) dep_name ON dep_name.emp_no = e.emp_no WHERE dp.dept_no IS NULL; #過濾出非普通的員工
查詢的結果執行時,可以加上limit函數,不然會很慢,至於優化問題,暫時先不管!
union聯合查詢
有兩張表如下: mysql> select * from tb1; +----+------+ | c1 | c2 | +----+------+ | 1 | zhao | | 2 | qina | | 3 | b | | 4 | c | +----+------+ 4 rows in set (0.00 sec) mysql> select * from tb2; +------+------+ | id | NAME | +------+------+ | 1 | a | | 3 | b | | 4 | c | | 5 | d | | 6 | e | +------+------+ 5 rows in set (0.00 sec) #使用UNION聯合查詢, 去重復,把重復的數值自動去掉 mysql> select * from tb1 -> union -> select * from tb2; +------+------+ | c1 | c2 | +------+------+ | 1 | zhao | | 2 | qina | | 3 | b | | 4 | c | | 1 | a | | 5 | d | | 6 | e | +------+------+ 7 rows in set (0.00 sec) #使用all關鍵字,會把重復的行也查詢出來 mysql> select * from tb1 -> union all -> select * from tb2; +------+------+ | c1 | c2 | +------+------+ | 1 | zhao | | 2 | qina | | 3 | b | | 4 | c | | 1 | a | | 3 | b | | 4 | c | | 5 | d | | 6 | e | +------+------+ 9 rows in set (0.00 sec)
給查詢的結果加上行號:
查詢員工信息時,給查詢的結果加上行號。
mysql> set @rn:=0; Query OK, 0 rows affected (0.00 sec) mysql> select @rn:=@rn+1 as row_num, emp_no, concat("first_name"," ","last_name") as full_name from employees limit 10; +---------+--------+----------------------+ | row_num | emp_no | full_name | +---------+--------+----------------------+ | 1 | 10001 | first_name last_name | | 2 | 10002 | first_name last_name | | 3 | 10003 | first_name last_name | | 4 | 10004 | first_name last_name | | 5 | 10005 | first_name last_name | | 6 | 10006 | first_name last_name | | 7 | 10007 | first_name last_name | | 8 | 10008 | first_name last_name | | 9 | 10009 | first_name last_name | | 10 | 10010 | first_name last_name | +---------+--------+----------------------+ 10 rows in set (0.00 sec) #這樣寫有點不方便就是需要兩個SQL語句,還有一個就是,當第二次查詢數據是,行號不會清零,會累加! mysql> select @rn:=@rn+1 as row_num, emp_no, concat("first_name"," ","last_name") as full_name from employees limit 10; +---------+--------+----------------------+ | row_num | emp_no | full_name | +---------+--------+----------------------+ | 11 | 10001 | first_name last_name | | 12 | 10002 | first_name last_name | | 13 | 10003 | first_name last_name | | 14 | 10004 | first_name last_name | | 15 | 10005 | first_name last_name | | 16 | 10006 | first_name last_name | | 17 | 10007 | first_name last_name | | 18 | 10008 | first_name last_name | | 19 | 10009 | first_name last_name | | 20 | 10010 | first_name last_name | +---------+--------+----------------------+ 10 rows in set (0.00 sec) #盡量使用一條SQL語句,行號每次查詢時會清零: mysql> select @rn:=@rn+1 as row_num, emp_no, concat("first_name"," ","last_name") as full_name from employees, (select @rn:=0) as row_num limit 10; +---------+--------+----------------------+ | row_num | emp_no | full_name | +---------+--------+----------------------+ | 1 | 10001 | first_name last_name | | 2 | 10002 | first_name last_name | | 3 | 10003 | first_name last_name | | 4 | 10004 | first_name last_name | | 5 | 10005 | first_name last_name | | 6 | 10006 | first_name last_name | | 7 | 10007 | first_name last_name | | 8 | 10008 | first_name last_name | | 9 | 10009 | first_name last_name | | 10 | 10010 | first_name last_name | +---------+--------+----------------------+ 10 rows in set (0.00 sec)
MySQL的一個排名問題:
#有如下的數據,按照成績排名: mysql> select * from rank; +------+-------+ | id | score | +------+-------+ | 1 | 10 | | 2 | 20 | | 3 | 20 | | 4 | 30 | | 5 | 40 | | 6 | 40 | | 7 | 20 | +------+-------+ 7 rows in set (0.00 sec) #成績有相同的,按照成績排名 set @prev_value = NULL; set @rank_count = 0; SELECT id, score, CASE WHEN @prev_value = score THEN @rank_count WHEN @prev_value := score THEN @rank_count := @rank_count + 1 end as rank_column from rank ORDER BY score; #如果我們期望有一條SQL語句完成排名,可以仿照上面的方法: mysql> SELECT id, score, -> CASE -> WHEN @prev1_value = score THEN @rank1_count -> WHEN @prev1_value := score THEN @rank1_count := @rank1_count + 1 -> end as rank_column -> from rank, (SELECT @prev1_value := NULL, @rank1_count :=0 ) as rank_column -> ORDER BY score; #注意可以測試一下加ORDER BY與不加ORDER BY的結果,會不一樣的! +------+-------+-------------+ | id | score | rank_column | +------+-------+-------------+ | 1 | 10 | 1 | | 2 | 20 | 2 | | 3 | 20 | 2 | | 7 | 20 | 2 | | 4 | 30 | 3 | | 5 | 40 | 4 | | 6 | 40 | 4 | +------+-------+-------------+ 7 rows in set (0.00 sec) SQL語句如下: SELECT id, score, CASE WHEN @prev1_value = score THEN @rank1_count WHEN @prev1_value := score THEN @rank1_count := @rank1_count + 1 end as rank_column from rank, (SELECT @prev1_value := NULL, @rank1_count :=0 ) as rank_column ORDER BY score;
