MySQL筆記

MySQL學習筆記

[toc]

登錄和退出MySQL服務器

# 登錄MySQL
$ mysql -u root -p12345612

# 退出MySQL數據庫服務器
exit;

基本語法

-- 顯示所有數據庫
show databases;

-- 創(chuàng)建數據庫
CREATE DATABASE test;

-- 切換數據庫
use test;

-- 刪除數據庫
drop database test;

-- 顯示數據庫中的所有表
show tables;

-- 創(chuàng)建數據表
CREATE TABLE pet (
    name VARCHAR(20),
    owner VARCHAR(20),
    species VARCHAR(20),
    sex CHAR(1),
    birth DATE,
    death DATE
);

-- 查看數據表結構
-- describe pet;
desc pet;

-- 查詢表
SELECT * from pet;

-- 插入數據
INSERT INTO pet VALUES ('puffball', 'Diane', 'hamster', 'f', '1990-03-30', NULL);

-- 修改數據
UPDATE pet SET name = 'squirrel' where owner = 'Diane';

-- 刪除數據
DELETE FROM pet where name = 'squirrel';

-- 刪除表
DROP TABLE myorder;

正則表達式

-- 使用正則表達式
-- .表示匹配任意一個字符
SELECT prod_name 
FROM products 
WHERE prod_name REGEXP '.000'
ORDER BY prod_name;

-- like和regexp之間的區(qū)別:regexp只要列值中出現即匹配,like嚴格匹配整個列值

-- OR匹配
SELECT prod_name 
FROM products 
WHERE prod_name REGEXP '1000|2000'
ORDER BY prod_name;

-- []多字符匹配,相當于OR
SELECT prod_name 
FROM products 
WHERE prod_name REGEXP '[123] Tom'
ORDER BY prod_name;

-- \\對特殊字符轉義

字段拼接

-- concat()函數拼接多個列
select concat(vend_name,'(',vend_country,')')
from venders
order by vend_name

-- trim()函數刪除空格,rtrim()刪除右邊空格,ltrim刪除左邊空格

-- as關鍵字賦別名
select concat(vend_name,'(',vend_country,')') as vend_title
from venders
order by vend_name

分組

-- group by實現分組
-- group by必須在where語句之后,order by語句之前
SELECT vend_id,COUNT(*)AS num_prods 
FROM products 
GROUP BY vend_id WITH ROLLUP;

-- having和where的區(qū)別
-- WHERE在數據分組前進行過濾,HAVING在數據分組后進行過濾

全文搜索

-- 通過fulltext()建立索引
-- 不要在導入數據時建立索引
CREATE TABLE productnotes
(   
    note_id int NOT NULL AUTO_INCREMENT, 
    prod_id char(10) NOT NULL, 
    note_date datetime NOT NULL,
    note_text textNULL, 
    PRIMARY KEY(note_id), 
    FULLTEXT(note_text)
)ENGINE=MyISAM;

-- march()和against()進行全文搜索
-- march()指定搜索列,against()指定搜索字符
SELECT note_text 
FROM productnotes 
WHERE Match(note_text) Against('rabbit');

-- 查詢拓展
-- 除了匹配到含有anvils字段的行外,還能根據返回的行做進一步的檢索可能相關的行
SELECT note_text 
FROM productnotes 
WHERE Match(note_text) Against('anvils'WITH QUERY EXPANSION);

-- 布爾搜索
-- 布爾搜索無需建立fulltext()索引

SELECT note_text 
FROM productnotes 
WHERE Match(note_text) Against('+rabbit +bait'IN BOOLEAN MODE);
-- 這個搜索匹配包含詞rabbit和bait的行

SELECT note_text 
FROM productnotes
WHERE Match(note_text) Against('rabbit bait'IN BOOLEAN MODE);
-- 沒有指定操作符册赛,這個搜索匹配包含rabbit和bait中的至少一個詞的行

SELECT note_text 
FROM productnotes
WHERE Match(note_text) Against('"rabbit bait"'IN BOOLEAN MODE);
-- 這個搜索匹配短語rabbit bait而不是匹配兩個詞rabbit和bait

SELECT note_text 
FROM productnotes 
WHERE Match(note_text)Against('>rabbit <carrot'IN BOOLEAN MODE);
-- 匹配rabbit和carrot,增加前者的等級围俘,降低后者的等級惠昔。

-- 注意
-- 3個或3個字符以下的短詞不建立索引,mysql自帶非用詞(stopword)列表,這些詞在索引時忽略
-- 頻率超過50%的詞視為非用詞
-- 不支持中文
-- 僅在MyISAM數據庫引擎中支持全文本搜索

視圖

-- 視圖是虛擬表,對數據查詢的簡化
-- 視圖用CREATE VIEW viewname as語句來創(chuàng)建
-- 使用SHOW CREATE VIEW viewname缤苫;來查看創(chuàng)建視圖的語句赁温。
-- 用DROP刪除視圖坛怪,其語法為DROP VIEW viewname;
-- 更新視圖時,可以先用DROP再用CREATE股囊,也可以直接用CREATE OR REPLACE VIEW

存儲過程

-- 執(zhí)行存儲過程
-- @用于指定變量
CALL productpricing(@pricelow,
                    @pricehigh,
                    @priceaverage);

-- 創(chuàng)建存儲過程
CREATE PROCEDURE productpricing()
BEGIN 
SELECT Avg(prod_price) AS priceaverage 
FROM products; 
END;

-- 刪除存儲過程
DROP PROCEDURE productpricing;

-- 參數
-- IN(傳遞給存儲過程)酝陈、OUT(從存儲過程傳出,如這里所用)和INOUT(對存儲過程傳入和傳出)類型的參數
CREATE PROCEDURE productpricing(
    OUT pl DECIMAL(8,2), 
    OUT ph DECIMAL(8,2), 
    OUT pa DECIMAL(8,2)
)
BEGIN 
SELECT Min(prod_price)
INTO p1
FROM products; 
SELECT Max(prod_price)
INTO ph 
FROM products; 
SELECT Avg(prod_price)
INTO pa 
FROM products; 
END;

-- 檢查存儲過程
SHOW PROCEDURE STATUS LIKE ordertotal;

游標

CREATE PROCEDURE processorders()
BEGIN
-- 聲明局部變量 
    DECLARE done BOOLEAN DEFAULT O;
    DECLARE o INT;
-- 聲明游標 
    DECLARE ordernumbers CURSOR 
    FOR 
    SELECT order_num FROM orders;
-- 聲明CONTINUE HANDLER
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
-- 啟動游標
    OPEN ordernumbers;
-- 遍歷所有行 
    REPEAT
-- 獲取數據 
        FETCH ordernumbers INTO o;
-- 結束遍歷 
    UNTIL done END REPEAT;
-- 關閉游標 
    CLOSE ordernumbers; 
END;

觸發(fā)器

-- 創(chuàng)建觸發(fā)器
-- 每個表最多支持6個觸發(fā)器(每條INSERT毁涉、UPDATE和DELETE的之前和之后)
CREATE TRIGGER neWproduct AFTER INSERT ON products 
FOR EACH ROW SELECT 'Product added';
-- 文本'Product added'將對每個插入的行顯示一次

-- 刪除觸發(fā)器
DROP TRIGGER newproduct;

-- MySQL生成新的訂單號保存至order_num
CREATE TRIGGER neworder AFTER INSERT ON orders 
FOR EACH ROW SELECT NEW.order_num;

-- 在刪除表之前做備份
CREATE TRIGGER deleteorder BEFORE DELETE ON orders 
FOR EACH ROW 
BEGIN
INSERT INTO archive_orders(order_num, order_date, cust_id)
VALUES(OLD. order_num, OLD. order_date, OLD. cust_id); 
END;

字符集和校對

-- 顯示所有可用的字符集以及每個字符集的描述和默認校對
SHOW CHARACTER SET;

-- 顯示所有可用的校對,以及它們適用的字符集
SHOW COLLATION;

-- 對整個表和特定列指定字符集和校對
CREATE TABLE mytable
(
    columnn1 INT, 
    columnn2 VARCHAR(10),
    column3 VARCHAR(10) CHARACTER SET latinl COLLATE latinl_general_ci
)DEFAULT CHARACTER SET hebrew 
COLLATE hebrew_general_ci;

-- 指定排序的校對順序
SELECT * 
FROM cuStomers 
ORDER BY lastname,firstname COLLATE latinl_general_cs;

建表約束

主鍵約束

-- 主鍵約束
-- 使某個字段不重復且不得為空锈死,確保表內所有數據的唯一性贫堰。
CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(20)
);

-- 聯合主鍵
-- 聯合主鍵中的每個字段都不能為空,并且加起來不能和已設置的聯合主鍵重復瓮钥。
CREATE TABLE user (
    id INT,
    name VARCHAR(20),
    password VARCHAR(20),
    PRIMARY KEY(id, name)
);

-- 自增約束
-- 自增約束的主鍵由系統(tǒng)自動遞增分配燎孟。
CREATE TABLE user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20)
);

-- 添加主鍵約束
-- 如果忘記設置主鍵功戚,還可以通過SQL語句設置(兩種方式):
ALTER TABLE user ADD PRIMARY KEY(id);
ALTER TABLE user MODIFY id INT PRIMARY KEY;

-- 刪除主鍵
ALTER TABLE user drop PRIMARY KEY;

唯一主鍵

-- 建表時創(chuàng)建唯一主鍵
CREATE TABLE user (
    id INT,
    name VARCHAR(20),
    UNIQUE(name)
);

-- 添加唯一主鍵
-- 如果建表時沒有設置唯一建,還可以通過SQL語句設置(兩種方式):
ALTER TABLE user ADD UNIQUE(name);
ALTER TABLE user MODIFY name VARCHAR(20) UNIQUE;

-- 刪除唯一主鍵
ALTER TABLE user DROP INDEX name;

非空約束

-- 建表時添加非空約束
-- 約束某個字段不能為空
CREATE TABLE user (
    id INT,
    name VARCHAR(20) NOT NULL
);

-- 移除非空約束
ALTER TABLE user MODIFY name VARCHAR(20);

默認約束

-- 建表時添加默認約束
-- 約束某個字段的默認值
CREATE TABLE user2 (
    id INT,
    name VARCHAR(20),
    age INT DEFAULT 10
);

-- 移除非空約束
ALTER TABLE user MODIFY age INT;

外鍵約束

-- 班級
CREATE TABLE classes (
    id INT PRIMARY KEY,
    name VARCHAR(20)
);

-- 學生表
CREATE TABLE students (
    id INT PRIMARY KEY,
    name VARCHAR(20),
    -- 這里的 class_id 要和 classes 中的 id 字段相關聯
    class_id INT,
    -- 表示 class_id 的值必須來自于 classes 中的 id 字段值
    FOREIGN KEY(class_id) REFERENCES classes(id)
);

-- 1. 主表(父表)classes 中沒有的數據值偎行,在副表(子表)students 中,是不可以使用的;
-- 2. 主表中的記錄被副表引用時蛤袒,主表不可以被刪除熄云。

數據庫的三大設計范式

1NF

只要字段值還可以繼續(xù)拆分,就不滿足第一范式妙真。

范式設計得越詳細缴允,對某些實際操作可能會更好,但并非都有好處珍德,需要對項目的實際情況進行設定练般。

2NF

在滿足第一范式的前提下,其他列都必須完全依賴于主鍵列锈候。如果出現不完全依賴薄料,只可能發(fā)生在聯合主鍵的情況下:

-- 訂單表
CREATE TABLE myorder (
    product_id INT,
    customer_id INT,
    product_name VARCHAR(20),
    customer_name VARCHAR(20),
    PRIMARY KEY (product_id, customer_id)
);

實際上,在這張訂單表中泵琳,product_name 只依賴于 product_id 摄职,customer_name 只依賴于 customer_id 。也就是說虑稼,product_namecustomer_id 是沒用關系的琳钉,customer_nameproduct_id 也是沒有關系的。

這就不滿足第二范式:其他列都必須完全依賴于主鍵列蛛倦!

CREATE TABLE myorder (
    order_id INT PRIMARY KEY,
    product_id INT,
    customer_id INT
);

CREATE TABLE product (
    id INT PRIMARY KEY,
    name VARCHAR(20)
);

CREATE TABLE customer (
    id INT PRIMARY KEY,
    name VARCHAR(20)
);

拆分之后歌懒,myorder 表中的 product_idcustomer_id 完全依賴于 order_id 主鍵,而 productcustomer 表中的其他字段又完全依賴于主鍵溯壶。滿足了第二范式的設計及皂!

3NF

在滿足第二范式的前提下,除了主鍵列之外且改,其他列之間不能有傳遞依賴關系验烧。

CREATE TABLE myorder (
    order_id INT PRIMARY KEY,
    product_id INT,
    customer_id INT,
    customer_phone VARCHAR(15)
);

表中的 customer_phone 有可能依賴于 order_idcustomer_id 兩列又跛,也就不滿足了第三范式的設計:其他列之間不能有傳遞依賴關系碍拆。

CREATE TABLE myorder (
    order_id INT PRIMARY KEY,
    product_id INT,
    customer_id INT
);

CREATE TABLE customer (
    id INT PRIMARY KEY,
    name VARCHAR(20),
    phone VARCHAR(15)
);

修改后就不存在其他列之間的傳遞依賴關系,其他列都只依賴于主鍵列慨蓝,滿足了第三范式的設計感混!

事務管理

在 MySQL 中,事務其實是一個最小的不可分割的工作單元礼烈。事務能夠保證一個業(yè)務的完整性弧满。

比如我們的銀行轉賬:

-- a -> -100
UPDATE user set money = money - 100 WHERE name = 'a';

-- b -> +100
UPDATE user set money = money + 100 WHERE name = 'b';

在實際項目中,假設只有一條 SQL 語句執(zhí)行成功此熬,而另外一條執(zhí)行失敗了庭呜,就會出現數據前后不一致滑进。

因此,在執(zhí)行多條有關聯 SQL 語句時募谎,事務可能會要求這些 SQL 語句要么同時執(zhí)行成功扶关,要么就都執(zhí)行失敗。

如何控制事務 - COMMIT / ROLLBACK

在 MySQL 中近哟,事務的自動提交狀態(tài)默認是開啟的驮审。

-- 查詢事務的自動提交狀態(tài)
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
|            1 |
+--------------+

自動提交的作用:當我們執(zhí)行一條 SQL 語句的時候,其產生的效果就會立即體現出來吉执,且不能回滾疯淫。

什么是回滾?舉個例子:

CREATE DATABASE bank;

USE bank;

CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(20),
    money INT
);

INSERT INTO user VALUES (1, 'a', 1000);

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

可以看到戳玫,在執(zhí)行插入語句后數據立刻生效熙掺,原因是 MySQL 中的事務自動將它提交到了數據庫中。那么所謂回滾的意思就是咕宿,撤銷執(zhí)行過的所有 SQL 語句币绩,使其回滾到最后一次提交數據時的狀態(tài)。

在 MySQL 中使用 ROLLBACK 執(zhí)行回滾:

-- 回滾到最后一次提交
ROLLBACK;

-- 可設置保留點回滾
SAVEPOINT delete1;
ROLLBACK TO deletel;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

由于所有執(zhí)行過的 SQL 語句都已經被提交過了府阀,所以數據并沒有發(fā)生回滾缆镣。那如何讓數據可以發(fā)生回滾?

-- 關閉自動提交
SET AUTOCOMMIT = 0;

-- 查詢自動提交狀態(tài)
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
|            0 |
+--------------+

將自動提交關閉后试浙,測試數據回滾:

INSERT INTO user VALUES (2, 'b', 1000);

-- 關閉 AUTOCOMMIT 后董瞻,數據的變化是在一張?zhí)摂M的臨時數據表中展示,
-- 發(fā)生變化的數據并沒有真正插入到數據表中田巴。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

-- 數據表中的真實數據其實還是:
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

-- 由于數據還沒有真正提交钠糊,可以使用回滾
ROLLBACK;

-- 再次查詢
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

那如何將虛擬的數據真正提交到數據庫中?使用 COMMIT :

INSERT INTO user VALUES (2, 'b', 1000);
-- 手動提交數據(持久性)壹哺,
-- 將數據真正提交到數據庫中抄伍,執(zhí)行后不能再回滾提交過的數據。
COMMIT;

-- 提交后測試回滾
ROLLBACK;

-- 再次查詢(回滾無效了)
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

總結

  1. 自動提交

    • 查看自動提交狀態(tài):SELECT @@AUTOCOMMIT 管宵;

    • 設置自動提交狀態(tài):SET AUTOCOMMIT = 0 截珍。

  2. 手動提交

    @@AUTOCOMMIT = 0 時,使用 COMMIT 命令提交事務箩朴。

  3. 事務回滾

    @@AUTOCOMMIT = 0 時笛臣,使用 ROLLBACK 命令回滾事務。

事務的實際應用隧饼,讓我們再回到銀行轉賬項目:

-- 轉賬
UPDATE user set money = money - 100 WHERE name = 'a';

-- 到賬
UPDATE user set money = money + 100 WHERE name = 'b';

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

這時假設在轉賬時發(fā)生了意外,就可以使用 ROLLBACK 回滾到最后一次提交的狀態(tài):

-- 假設轉賬發(fā)生了意外静陈,需要回滾燕雁。
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

這時我們又回到了發(fā)生意外之前的狀態(tài)诞丽,也就是說,事務給我們提供了一個可以反悔的機會拐格。假設數據沒有發(fā)生意外僧免,這時可以手動將數據真正提交到數據表中:COMMIT

手動開啟事務 - BEGIN / START TRANSACTION

事務的默認提交被開啟 ( @@AUTOCOMMIT = 1 ) 后捏浊,此時就不能使用事務回滾了懂衩。但是我們還可以手動開啟一個事務處理事件,使其可以發(fā)生回滾:

-- 使用 BEGIN 或者 START TRANSACTION 手動開啟一個事務
-- START TRANSACTION;
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';

-- 由于手動開啟的事務沒有開啟自動提交金踪,
-- 此時發(fā)生變化的數據仍然是被保存在一張臨時表中浊洞。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

-- 測試回滾
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

仍然使用 COMMIT 提交數據,提交后無法再發(fā)生本次事務的回滾胡岔。

BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

-- 提交數據
COMMIT;

-- 測試回滾(無效法希,因為表的數據已經被提交)
ROLLBACK;

事務的 ACID 特征與使用

事務的四大特征:

  • A 原子性:事務是最小的單位,不可以再分割靶瘸;
  • C 一致性:要求同一事務中的 SQL 語句苫亦,必須保證同時成功或者失敗怨咪;
  • I 隔離性:事務1 和 事務2 之間是具有隔離性的屋剑;
  • D 持久性:事務一旦結束 ( COMMIT ) ,就不可以再返回了 ( ROLLBACK ) 诗眨。

事務的隔離性

事務的隔離性可分為四種 ( 性能從低到高 )

  1. READ UNCOMMITTED ( 讀取未提交 )

    如果有多個事務唉匾,那么任意事務都可以看見其他事務的未提交數據

  2. READ COMMITTED ( 讀取已提交 )

    只能讀取到其他事務已經提交的數據辽话。

  3. REPEATABLE READ ( 可被重復讀 )

    如果有多個連接都開啟了事務肄鸽,那么事務之間不能共享數據記錄,否則只能共享已提交的記錄油啤。

  4. SERIALIZABLE ( 串行化 )

    所有的事務都會按照固定順序執(zhí)行典徘,執(zhí)行完一個事務后再繼續(xù)執(zhí)行下一個事務的寫入操作

查看當前數據庫的默認隔離級別:

-- MySQL 8.x, GLOBAL 表示系統(tǒng)級別益咬,不加表示會話級別逮诲。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
SELECT @@TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ                | -- MySQL的默認隔離級別,可以重復讀幽告。
+--------------------------------+

-- MySQL 5.x
SELECT @@GLOBAL.TX_ISOLATION;
SELECT @@TX_ISOLATION;

修改隔離級別:

-- 設置系統(tǒng)隔離級別梅鹦,LEVEL 后面表示要設置的隔離級別 (READ UNCOMMITTED)。
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 查詢系統(tǒng)隔離級別冗锁,發(fā)現已經被修改齐唆。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-UNCOMMITTED               |
+--------------------------------+

臟讀

測試 READ UNCOMMITTED ( 讀取未提交 ) 的隔離性:

INSERT INTO user VALUES (3, '小明', 1000);
INSERT INTO user VALUES (4, '淘寶店', 1000);

SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
+----+-----------+-------+

-- 開啟一個事務操作數據
-- 假設小明在淘寶店買了一雙800塊錢的鞋子:
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘寶店';

-- 然后淘寶店在另一方查詢結果,發(fā)現錢已到賬冻河。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘寶店    |  1800 |
+----+-----------+-------+

由于小明的轉賬是在新開啟的事務上進行操作的箍邮,而該操作的結果是可以被其他事務(另一方的淘寶店)看見的茉帅,因此淘寶店的查詢結果是正確的,淘寶店確認到賬锭弊。但就在這時堪澎,如果小明在它所處的事務上又執(zhí)行了 ROLLBACK 命令,會發(fā)生什么味滞?

-- 小明所處的事務
ROLLBACK;

-- 此時無論對方是誰樱蛤,如果再去查詢結果就會發(fā)現:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
+----+-----------+-------+

這就是所謂的臟讀,一個事務讀取到另外一個事務還未提交的數據剑鞍。這在實際開發(fā)中是不允許出現的昨凡。

讀取已提交

把隔離級別設置為 READ COMMITTED

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-COMMITTED                 |
+--------------------------------+

這樣,再有新的事務連接進來時攒暇,它們就只能查詢到已經提交過的事務數據了土匀。但是對于當前事務來說,它們看到的還是未提交的數據形用,例如:

-- 正在操作數據事務(當前事務)
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘寶店';

-- 雖然隔離級別被設置為了 READ COMMITTED就轧,但在當前事務中,
-- 它看到的仍然是數據表中臨時改變數據田度,而不是真正提交過的數據妒御。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘寶店    |  1800 |
+----+-----------+-------+


-- 假設此時在遠程開啟了一個新事務,連接到數據庫镇饺。
$ mysql -u root -p12345612

-- 此時遠程連接查詢到的數據只能是已經提交過的
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
+----+-----------+-------+

但是這樣還有問題乎莉,那就是假設一個事務在操作數據時,其他事務干擾了這個事務的數據奸笤。例如:

-- 小張在查詢數據的時候發(fā)現:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘寶店    |  1800 |
+----+-----------+-------+

-- 在小張求表的 money 平均值之前惋啃,小王做了一個操作:
START TRANSACTION;
INSERT INTO user VALUES (5, 'c', 100);
COMMIT;

-- 此時表的真實數據是:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
|  5 | c         |   100 |
+----+-----------+-------+

-- 這時小張再求平均值的時候,就會出現計算不相符合的情況:
SELECT AVG(money) FROM user;
+------------+
| AVG(money) |
+------------+
|  820.0000  |
+------------+

雖然 READ COMMITTED 讓我們只能讀取到其他事務已經提交的數據监右,但還是會出現問題边灭,就是在讀取同一個表的數據時,可能會發(fā)生前后不一致的情況健盒。這被稱為不可重復讀現象 ( READ COMMITTED ) 绒瘦。

幻讀

將隔離級別設置為 REPEATABLE READ ( 可被重復讀取 ) :

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+

測試 REPEATABLE READ ,假設在兩個不同的連接上分別執(zhí)行 START TRANSACTION :

-- 小張 - 成都
START TRANSACTION;
INSERT INTO user VALUES (6, 'd', 1000);

-- 小王 - 北京
START TRANSACTION;

-- 小張 - 成都
COMMIT;

當前事務開啟后扣癣,沒提交之前惰帽,查詢不到,提交后可以被查詢到父虑。但是该酗,在提交之前其他事務被開啟了,那么在這條事務線上士嚎,就不會查詢到當前有操作事務的連接垂涯。相當于開辟出一條單獨的線程烁焙。

無論小張是否執(zhí)行過 COMMIT ,在小王這邊耕赘,都不會查詢到小張的事務記錄,而是只會查詢到自己所處事務的記錄:

SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
|  5 | c         |   100 |
+----+-----------+-------+

這是因為小王在此之前開啟了一個新的事務 ( START TRANSACTION ) 膳殷,那么在他的這條新事務的線上操骡,跟其他事務是沒有聯系的,也就是說赚窃,此時如果其他事務正在操作數據册招,它是不知道的。

然而事實是勒极,在真實的數據表中是掰,小張已經插入了一條數據。但是小王此時并不知道辱匿,也插入了同一條數據键痛,會發(fā)生什么呢?

INSERT INTO user VALUES (6, 'd', 1000);
-- ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'

報錯了匾七,操作被告知已存在主鍵為 6 的字段絮短。這種現象也被稱為幻讀,一個事務提交的數據昨忆,不能被其他事務讀取到丁频。

串行化

顧名思義,就是所有事務的寫入操作全都是串行化的邑贴。什么意思席里?把隔離級別修改成 SERIALIZABLE :

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| SERIALIZABLE                   |
+--------------------------------+

還是拿小張和小王來舉例:

-- 小張 - 成都
START TRANSACTION;

-- 小王 - 北京
START TRANSACTION;

-- 開啟事務之前先查詢表,準備操作數據拢驾。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘寶店    |  1000 |
|  5 | c         |   100 |
|  6 | d         |  1000 |
+----+-----------+-------+

-- 發(fā)現沒有 7 號王小花奖磁,于是插入一條數據:
INSERT INTO user VALUES (7, '王小花', 1000);

此時會發(fā)生什么呢?由于現在的隔離級別是 SERIALIZABLE ( 串行化 ) 独旷,串行化的意思就是:假設把所有的事務都放在一個串行的隊列中署穗,那么所有的事務都會按照固定順序執(zhí)行,執(zhí)行完一個事務后再繼續(xù)執(zhí)行下一個事務的寫入操作 ( 這意味著隊列中同時只能執(zhí)行一個事務的寫入操作 ) 嵌洼。

根據這個解釋案疲,小王在插入數據時,會出現等待狀態(tài)麻养,直到小張執(zhí)行 COMMIT 結束它所處的事務褐啡,或者出現等待超時。

用戶管理

-- 查看用戶
use mysql;
select user from user;

-- 新建用戶并設置密碼
create user username IDENTIFIED BY 'password';

-- 用戶重命名
rename user username1 to username2;

-- 刪除用戶
drop user username;

-- 查看用戶權限
show grants for username;

-- 授權給用戶
-- 授予用戶username在database上的select和delete權限
grnat select,delete on database.* to username;

-- 撤銷權限
-- 撤銷用戶username在database上的select權限
revoke select on database.* from username;

-- 密碼
-- 不指定用戶時默認為當前用戶
SET PASSWORD FOR username = Password('new password');

GRANT和REVOKE可在幾個層次上控制訪問權限:

  • 整個服務器鳖昌,使用GRANT ALL和REVOKEALL备畦;
  • 整個數據庫低飒,使用ON database.*;
  • 特定的表懂盐,使用oN database.table褥赊;
  • 特定的列;
  • 特定的存儲過程莉恼。

查詢練習

準備數據

-- 創(chuàng)建數據庫
CREATE DATABASE select_test;
-- 切換數據庫
USE select_test;

-- 創(chuàng)建學生表
CREATE TABLE student (
    no VARCHAR(20) PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    sex VARCHAR(10) NOT NULL,
    birthday DATE, -- 生日
    class VARCHAR(20) -- 所在班級
);

-- 創(chuàng)建教師表
CREATE TABLE teacher (
    no VARCHAR(20) PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    sex VARCHAR(10) NOT NULL,
    birthday DATE,
    profession VARCHAR(20) NOT NULL, -- 職稱
    department VARCHAR(20) NOT NULL -- 部門
);

-- 創(chuàng)建課程表
CREATE TABLE course (
    no VARCHAR(20) PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    t_no VARCHAR(20) NOT NULL, -- 教師編號
    -- 表示該 tno 來自于 teacher 表中的 no 字段值
    FOREIGN KEY(t_no) REFERENCES teacher(no) 
);

-- 成績表
CREATE TABLE score (
    s_no VARCHAR(20) NOT NULL, -- 學生編號
    c_no VARCHAR(20) NOT NULL, -- 課程號
    degree DECIMAL, -- 成績
    -- 表示該 s_no, c_no 分別來自于 student, course 表中的 no 字段值
    FOREIGN KEY(s_no) REFERENCES student(no),   
    FOREIGN KEY(c_no) REFERENCES course(no),
    -- 設置 s_no, c_no 為聯合主鍵
    PRIMARY KEY(s_no, c_no)
);

-- 查看所有表
SHOW TABLES;

-- 添加學生表數據
INSERT INTO student VALUES('101', '曾華', '男', '1977-09-01', '95033');
INSERT INTO student VALUES('102', '匡明', '男', '1975-10-02', '95031');
INSERT INTO student VALUES('103', '王麗', '女', '1976-01-23', '95033');
INSERT INTO student VALUES('104', '李軍', '男', '1976-02-20', '95033');
INSERT INTO student VALUES('105', '王芳', '女', '1975-02-10', '95031');
INSERT INTO student VALUES('106', '陸軍', '男', '1974-06-03', '95031');
INSERT INTO student VALUES('107', '王尼瑪', '男', '1976-02-20', '95033');
INSERT INTO student VALUES('108', '張全蛋', '男', '1975-02-10', '95031');
INSERT INTO student VALUES('109', '趙鐵柱', '男', '1974-06-03', '95031');

-- 添加教師表數據
INSERT INTO teacher VALUES('804', '李誠', '男', '1958-12-02', '副教授', '計算機系');
INSERT INTO teacher VALUES('856', '張旭', '男', '1969-03-12', '講師', '電子工程系');
INSERT INTO teacher VALUES('825', '王萍', '女', '1972-05-05', '助教', '計算機系');
INSERT INTO teacher VALUES('831', '劉冰', '女', '1977-08-14', '助教', '電子工程系');

-- 添加課程表數據
INSERT INTO course VALUES('3-105', '計算機導論', '825');
INSERT INTO course VALUES('3-245', '操作系統(tǒng)', '804');
INSERT INTO course VALUES('6-166', '數字電路', '856');
INSERT INTO course VALUES('9-888', '高等數學', '831');

-- 添加添加成績表數據
INSERT INTO score VALUES('103', '3-105', '92');
INSERT INTO score VALUES('103', '3-245', '86');
INSERT INTO score VALUES('103', '6-166', '85');
INSERT INTO score VALUES('105', '3-105', '88');
INSERT INTO score VALUES('105', '3-245', '75');
INSERT INTO score VALUES('105', '6-166', '79');
INSERT INTO score VALUES('109', '3-105', '76');
INSERT INTO score VALUES('109', '3-245', '68');
INSERT INTO score VALUES('109', '6-166', '81');

-- 查看表結構
SELECT * FROM course;
SELECT * FROM score;
SELECT * FROM student;
SELECT * FROM teacher;

1 到 10

-- 查詢 student 表的所有行
SELECT * FROM student;

-- 查詢 student 表中的 name拌喉、sex 和 class 字段的所有行
SELECT name, sex, class FROM student;

-- 查詢 teacher 表中不重復的 department 列
-- department: 去重查詢
SELECT DISTINCT department FROM teacher;

-- 查詢 score 表中成績在60-80之間的所有行(區(qū)間查詢和運算符查詢)
-- BETWEEN xx AND xx: 查詢區(qū)間, AND 表示 "并且"
SELECT * FROM score WHERE degree BETWEEN 60 AND 80;
SELECT * FROM score WHERE degree > 60 AND degree < 80;

-- 查詢 score 表中成績?yōu)?85, 86 或 88 的行
-- IN: 查詢規(guī)定中的多個值
SELECT * FROM score WHERE degree IN (85, 86, 88);

-- 查詢 student 表中 '95031' 班或性別為 '女' 的所有行
-- or: 表示或者關系
SELECT * FROM student WHERE class = '95031' or sex = '女';

-- 以 class 降序的方式查詢 student 表的所有行
-- DESC: 降序,從高到低
-- ASC(默認): 升序俐银,從低到高
SELECT * FROM student ORDER BY class DESC;
SELECT * FROM student ORDER BY class ASC;

-- 以 c_no 升序尿背、degree 降序查詢 score 表的所有行
SELECT * FROM score ORDER BY c_no ASC, degree DESC;

-- 查詢 "95031" 班的學生人數
-- COUNT: 統(tǒng)計
SELECT COUNT(*) FROM student WHERE class = '95031';

-- 查詢 score 表中的最高分的學生學號和課程編號(子查詢或排序查詢)。
-- (SELECT MAX(degree) FROM score): 子查詢捶惜,算出最高分
SELECT s_no, c_no FROM score WHERE degree = (SELECT MAX(degree) FROM score);

--  排序查詢
-- LIMIT r, n: 表示從第r行開始田藐,查詢n條數據
SELECT s_no, c_no, degree FROM score ORDER BY degree DESC LIMIT 0, 1;

分組計算平均成績

查詢每門課的平均成績。

-- AVG: 平均值
SELECT AVG(degree) FROM score WHERE c_no = '3-105';
SELECT AVG(degree) FROM score WHERE c_no = '3-245';
SELECT AVG(degree) FROM score WHERE c_no = '6-166';

-- GROUP BY: 分組查詢
SELECT c_no, AVG(degree) FROM score GROUP BY c_no;

分組條件與模糊查詢

查詢 score 表中至少有 2 名學生選修吱七,并以 3 開頭的課程的平均分數汽久。

SELECT * FROM score;
-- c_no 課程編號
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
| 103  | 3-245 |     86 |
| 103  | 6-166 |     85 |
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

分析表發(fā)現,至少有 2 名學生選修的課程是 3-105 陪捷、3-245 回窘、6-166 ,以 3 開頭的課程是 3-105 市袖、3-245 啡直。也就是說,我們要查詢所有 3-1053-245degree 平均分苍碟。

-- 首先把 c_no, AVG(degree) 通過分組查詢出來
SELECT c_no, AVG(degree) FROM score GROUP BY c_no
+-------+-------------+
| c_no  | AVG(degree) |
+-------+-------------+
| 3-105 |     85.3333 |
| 3-245 |     76.3333 |
| 6-166 |     81.6667 |
+-------+-------------+

-- 再查詢出至少有 2 名學生選修的課程
-- HAVING: 表示持有
HAVING COUNT(c_no) >= 2

-- 并且是以 3 開頭的課程
-- LIKE 表示模糊查詢酒觅,"%" 是一個通配符,匹配 "3" 后面的任意字符微峰。
AND c_no LIKE '3%';

-- 把前面的SQL語句拼接起來舷丹,
-- 后面加上一個 COUNT(*),表示將每個分組的個數也查詢出來蜓肆。
SELECT c_no, AVG(degree), COUNT(*) FROM score GROUP BY c_no
HAVING COUNT(c_no) >= 2 AND c_no LIKE '3%';
+-------+-------------+----------+
| c_no  | AVG(degree) | COUNT(*) |
+-------+-------------+----------+
| 3-105 |     85.3333 |        3 |
| 3-245 |     76.3333 |        3 |
+-------+-------------+----------+

多表查詢 - 1

查詢所有學生的 name颜凯,以及該學生在 score 表中對應的 c_nodegree

SELECT no, name FROM student;
+-----+-----------+
| no  | name      |
+-----+-----------+
| 101 | 曾華      |
| 102 | 匡明      |
| 103 | 王麗      |
| 104 | 李軍      |
| 105 | 王芳      |
| 106 | 陸軍      |
| 107 | 王尼瑪    |
| 108 | 張全蛋    |
| 109 | 趙鐵柱    |
+-----+-----------+

SELECT s_no, c_no, degree FROM score;
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
| 103  | 3-245 |     86 |
| 103  | 6-166 |     85 |
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

通過分析可以發(fā)現仗扬,只要把 score 表中的 s_no 字段值替換成 student 表中對應的 name 字段值就可以了症概,如何做呢?

-- FROM...: 表示從 student, score 表中查詢
-- WHERE 的條件表示為早芭,只有在 student.no 和 score.s_no 相等時才顯示出來彼城。
SELECT name, c_no, degree FROM student, score 
WHERE student.no = score.s_no;
+-----------+-------+--------+
| name      | c_no  | degree |
+-----------+-------+--------+
| 王麗      | 3-105 |     92 |
| 王麗      | 3-245 |     86 |
| 王麗      | 6-166 |     85 |
| 王芳      | 3-105 |     88 |
| 王芳      | 3-245 |     75 |
| 王芳      | 6-166 |     79 |
| 趙鐵柱    | 3-105 |     76 |
| 趙鐵柱    | 3-245 |     68 |
| 趙鐵柱    | 6-166 |     81 |
+-----------+-------+--------+

多表查詢 - 2

查詢所有學生的 no 、課程名稱 ( course 表中的 name ) 和成績 ( score 表中的 degree ) 列。

只有 score 關聯學生的 no 募壕,因此只要查詢 score 表调炬,就能找出所有和學生相關的 nodegree

SELECT s_no, c_no, degree FROM score;
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
| 103  | 3-245 |     86 |
| 103  | 6-166 |     85 |
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

然后查詢 course 表:

+-------+-----------------+
| no    | name            |
+-------+-----------------+
| 3-105 | 計算機導論      |
| 3-245 | 操作系統(tǒng)        |
| 6-166 | 數字電路        |
| 9-888 | 高等數學        |
+-------+-----------------+

只要把 score 表中的 c_no 替換成 course 表中對應的 name 字段值就可以了。

-- 增加一個查詢字段 name舱馅,分別從 score缰泡、course 這兩個表中查詢。
-- as 表示取一個該字段的別名代嗤。
SELECT s_no, name as c_name, degree FROM score, course
WHERE score.c_no = course.no;
+------+-----------------+--------+
| s_no | c_name          | degree |
+------+-----------------+--------+
| 103  | 計算機導論      |     92 |
| 105  | 計算機導論      |     88 |
| 109  | 計算機導論      |     76 |
| 103  | 操作系統(tǒng)        |     86 |
| 105  | 操作系統(tǒng)        |     75 |
| 109  | 操作系統(tǒng)        |     68 |
| 103  | 數字電路        |     85 |
| 105  | 數字電路        |     79 |
| 109  | 數字電路        |     81 |
+------+-----------------+--------+

三表關聯查詢

查詢所有學生的 name 匀谣、課程名 ( course 表中的 name ) 和 degree

只有 score 表中關聯學生的學號和課堂號资溃,我們只要圍繞著 score 這張表查詢就好了。

SELECT * FROM score;
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
| 103  | 3-245 |     86 |
| 103  | 6-166 |     85 |
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

只要把 s_noc_no 替換成 studentsrouse 表中對應的 name 字段值就好了烈炭。

首先把 s_no 替換成 student 表中的 name 字段:

SELECT name, c_no, degree FROM student, score WHERE student.no = score.s_no;
+-----------+-------+--------+
| name      | c_no  | degree |
+-----------+-------+--------+
| 王麗      | 3-105 |     92 |
| 王麗      | 3-245 |     86 |
| 王麗      | 6-166 |     85 |
| 王芳      | 3-105 |     88 |
| 王芳      | 3-245 |     75 |
| 王芳      | 6-166 |     79 |
| 趙鐵柱    | 3-105 |     76 |
| 趙鐵柱    | 3-245 |     68 |
| 趙鐵柱    | 6-166 |     81 |
+-----------+-------+--------+

再把 c_no 替換成 course 表中的 name 字段:

-- 課程表
SELECT no, name FROM course;
+-------+-----------------+
| no    | name            |
+-------+-----------------+
| 3-105 | 計算機導論      |
| 3-245 | 操作系統(tǒng)        |
| 6-166 | 數字電路        |
| 9-888 | 高等數學        |
+-------+-----------------+

-- 由于字段名存在重復溶锭,使用 "表名.字段名 as 別名" 代替喳坠。
SELECT student.name as s_name, course.name as c_name, degree 
FROM student, score, course
WHERE student.NO = score.s_no
AND score.c_no = course.no;

子查詢加分組求平均分

查詢 95031 班學生每門課程的平均成績凡纳。

score 表中根據 student 表的學生編號篩選出學生的課堂號和成績:

-- IN (..): 將篩選出的學生號當做 s_no 的條件查詢
SELECT s_no, c_no, degree FROM score
WHERE s_no IN (SELECT no FROM student WHERE class = '95031');
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

這時只要將 c_no 分組一下就能得出 95031 班學生每門課的平均成績:

SELECT c_no, AVG(degree) FROM score
WHERE s_no IN (SELECT no FROM student WHERE class = '95031')
GROUP BY c_no;
+-------+-------------+
| c_no  | AVG(degree) |
+-------+-------------+
| 3-105 |     82.0000 |
| 3-245 |     71.5000 |
| 6-166 |     80.0000 |
+-------+-------------+

子查詢 - 1

查詢在 3-105 課程中触趴,所有成績高于 109 號同學的記錄倡缠。

首先篩選出課堂號為 3-105 海渊,在找出所有成績高于 109 號同學的的行全景。

SELECT * FROM score 
WHERE c_no = '3-105'
AND degree > (SELECT degree FROM score WHERE s_no = '109' AND c_no = '3-105');

子查詢 - 2

查詢所有成績高于 109 號同學的 3-105 課程成績記錄赋元。

-- 不限制課程號九妈,只要成績大于109號同學的3-105課程成績就可以丽蝎。
SELECT * FROM score
WHERE degree > (SELECT degree FROM score WHERE s_no = '109' AND c_no = '3-105');

YEAR 函數與帶 IN 關鍵字查詢

查詢所有和 101 猎拨、108 號學生同年出生的 noname 屠阻、birthday 列红省。

-- YEAR(..): 取出日期中的年份
SELECT no, name, birthday FROM student
WHERE YEAR(birthday) IN (SELECT YEAR(birthday) FROM student WHERE no IN (101, 108));

多層嵌套子查詢

查詢 '張旭' 教師任課的學生成績表。

首先找到教師編號:

SELECT NO FROM teacher WHERE NAME = '張旭'

通過 sourse 表找到該教師課程號:

SELECT NO FROM course WHERE t_no = ( SELECT NO FROM teacher WHERE NAME = '張旭' );

通過篩選出的課程號查詢成績表:

SELECT * FROM score WHERE c_no = (
    SELECT no FROM course WHERE t_no = ( 
        SELECT no FROM teacher WHERE NAME = '張旭' 
    )
);

多表查詢

查詢某選修課程多于5個同學的教師姓名国觉。

首先在 teacher 表中吧恃,根據 no 字段來判斷該教師的同一門課程是否有至少5名學員選修:

-- 查詢 teacher 表
SELECT no, name FROM teacher;
+-----+--------+
| no  | name   |
+-----+--------+
| 804 | 李誠   |
| 825 | 王萍   |
| 831 | 劉冰   |
| 856 | 張旭   |
+-----+--------+

SELECT name FROM teacher WHERE no IN (
    -- 在這里找到對應的條件
);

查看和教師編號有有關的表的信息:

SELECT * FROM course;
-- t_no: 教師編號
+-------+-----------------+------+
| no    | name            | t_no |
+-------+-----------------+------+
| 3-105 | 計算機導論      | 825  |
| 3-245 | 操作系統(tǒng)        | 804  |
| 6-166 | 數字電路        | 856  |
| 9-888 | 高等數學        | 831  |
+-------+-----------------+------+

我們已經找到和教師編號有關的字段就在 course 表中,但是還無法知道哪門課程至少有5名學生選修麻诀,所以還需要根據 score 表來查詢:

-- 在此之前向 score 插入一些數據痕寓,以便豐富查詢條件。
INSERT INTO score VALUES ('101', '3-105', '90');
INSERT INTO score VALUES ('102', '3-105', '91');
INSERT INTO score VALUES ('104', '3-105', '89');

-- 查詢 score 表
SELECT * FROM score;
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 101  | 3-105 |     90 |
| 102  | 3-105 |     91 |
| 103  | 3-105 |     92 |
| 103  | 3-245 |     86 |
| 103  | 6-166 |     85 |
| 104  | 3-105 |     89 |
| 105  | 3-105 |     88 |
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

-- 在 score 表中將 c_no 作為分組蝇闭,并且限制 c_no 持有至少 5 條數據呻率。
SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5;
+-------+
| c_no  |
+-------+
| 3-105 |
+-------+

根據篩選出來的課程號,找出在某課程中丁眼,擁有至少5名學員的教師編號:

SELECT t_no FROM course WHERE no IN (
    SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5
);
+------+
| t_no |
+------+
| 825  |
+------+

teacher 表中筷凤,根據篩選出來的教師編號找到教師姓名:

SELECT name FROM teacher WHERE no IN (
    -- 最終條件
    SELECT t_no FROM course WHERE no IN (
        SELECT c_no FROM score GROUP BY c_no HAVING COUNT(*) > 5
    )
);

子查詢 - 3

查詢 “計算機系” 課程的成績表。

思路是,先找出 course 表中所有 計算機系 課程的編號藐守,然后根據這個編號查詢 score 表挪丢。

-- 通過 teacher 表查詢所有 `計算機系` 的教師編號
SELECT no, name, department FROM teacher WHERE department = '計算機系'
+-----+--------+--------------+
| no  | name   | department   |
+-----+--------+--------------+
| 804 | 李誠   | 計算機系     |
| 825 | 王萍   | 計算機系     |
+-----+--------+--------------+

-- 通過 course 表查詢該教師的課程編號
SELECT no FROM course WHERE t_no IN (
    SELECT no FROM teacher WHERE department = '計算機系'
);
+-------+
| no    |
+-------+
| 3-245 |
| 3-105 |
+-------+

-- 根據篩選出來的課程號查詢成績表
SELECT * FROM score WHERE c_no IN (
    SELECT no FROM course WHERE t_no IN (
        SELECT no FROM teacher WHERE department = '計算機系'
    )
);
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-245 |     86 |
| 105  | 3-245 |     75 |
| 109  | 3-245 |     68 |
| 101  | 3-105 |     90 |
| 102  | 3-105 |     91 |
| 103  | 3-105 |     92 |
| 104  | 3-105 |     89 |
| 105  | 3-105 |     88 |
| 109  | 3-105 |     76 |
+------+-------+--------+

UNION 和 NOTIN 的使用

查詢 計算機系電子工程系 中的不同職稱的教師。

-- NOT: 代表邏輯非
SELECT * FROM teacher WHERE department = '計算機系' AND profession NOT IN (
    SELECT profession FROM teacher WHERE department = '電子工程系'
)
-- 合并兩個集
UNION
SELECT * FROM teacher WHERE department = '電子工程系' AND profession NOT IN (
    SELECT profession FROM teacher WHERE department = '計算機系'
);

ANY 表示至少一個 - DESC ( 降序 )

查詢課程 3-105 且成績 <u>至少</u> 高于 3-245score 表卢厂。

SELECT * FROM score WHERE c_no = '3-105';
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 101  | 3-105 |     90 |
| 102  | 3-105 |     91 |
| 103  | 3-105 |     92 |
| 104  | 3-105 |     89 |
| 105  | 3-105 |     88 |
| 109  | 3-105 |     76 |
+------+-------+--------+

SELECT * FROM score WHERE c_no = '3-245';
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-245 |     86 |
| 105  | 3-245 |     75 |
| 109  | 3-245 |     68 |
+------+-------+--------+

-- ANY: 符合SQL語句中的任意條件乾蓬。
-- 也就是說,在 3-105 成績中慎恒,只要有一個大于從 3-245 篩選出來的任意行就符合條件任内,
-- 最后根據降序查詢結果。
SELECT * FROM score WHERE c_no = '3-105' AND degree > ANY(
    SELECT degree FROM score WHERE c_no = '3-245'
) ORDER BY degree DESC;
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
| 102  | 3-105 |     91 |
| 101  | 3-105 |     90 |
| 104  | 3-105 |     89 |
| 105  | 3-105 |     88 |
| 109  | 3-105 |     76 |
+------+-------+--------+

表示所有的 ALL

查詢課程 3-105 且成績高于 3-245score 表融柬。

-- 只需對上一道題稍作修改死嗦。
-- ALL: 符合SQL語句中的所有條件。
-- 也就是說粒氧,在 3-105 每一行成績中越除,都要大于從 3-245 篩選出來全部行才算符合條件。
SELECT * FROM score WHERE c_no = '3-105' AND degree > ALL(
    SELECT degree FROM score WHERE c_no = '3-245'
);
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 101  | 3-105 |     90 |
| 102  | 3-105 |     91 |
| 103  | 3-105 |     92 |
| 104  | 3-105 |     89 |
| 105  | 3-105 |     88 |
+------+-------+--------+

復制表的數據作為條件查詢

查詢某課程成績比該課程平均成績低的 score 表外盯。

-- 查詢平均分
SELECT c_no, AVG(degree) FROM score GROUP BY c_no;
+-------+-------------+
| c_no  | AVG(degree) |
+-------+-------------+
| 3-105 |     87.6667 |
| 3-245 |     76.3333 |
| 6-166 |     81.6667 |
+-------+-------------+

-- 查詢 score 表
SELECT degree FROM score;
+--------+
| degree |
+--------+
|     90 |
|     91 |
|     92 |
|     86 |
|     85 |
|     89 |
|     88 |
|     75 |
|     79 |
|     76 |
|     68 |
|     81 |
+--------+

-- 將表 b 作用于表 a 中查詢數據
-- score a (b): 將表聲明為 a (b)摘盆,
-- 如此就能用 a.c_no = b.c_no 作為條件執(zhí)行查詢了。
SELECT * FROM score a WHERE degree < (
    (SELECT AVG(degree) FROM score b WHERE a.c_no = b.c_no)
);
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 105  | 3-245 |     75 |
| 105  | 6-166 |     79 |
| 109  | 3-105 |     76 |
| 109  | 3-245 |     68 |
| 109  | 6-166 |     81 |
+------+-------+--------+

子查詢 - 4

查詢所有任課 ( 在 course 表里有課程 ) 教師的 namedepartment 饱苟。

SELECT name, department FROM teacher WHERE no IN (SELECT t_no FROM course);
+--------+-----------------+
| name   | department      |
+--------+-----------------+
| 李誠   | 計算機系        |
| 王萍   | 計算機系        |
| 劉冰   | 電子工程系      |
| 張旭   | 電子工程系      |
+--------+-----------------+

條件加組篩選

查詢 student 表中至少有 2 名男生的 class 孩擂。

-- 查看學生表信息
SELECT * FROM student;
+-----+-----------+-----+------------+-------+
| no  | name      | sex | birthday   | class |
+-----+-----------+-----+------------+-------+
| 101 | 曾華      | 男  | 1977-09-01 | 95033 |
| 102 | 匡明      | 男  | 1975-10-02 | 95031 |
| 103 | 王麗      | 女  | 1976-01-23 | 95033 |
| 104 | 李軍      | 男  | 1976-02-20 | 95033 |
| 105 | 王芳      | 女  | 1975-02-10 | 95031 |
| 106 | 陸軍      | 男  | 1974-06-03 | 95031 |
| 107 | 王尼瑪    | 男  | 1976-02-20 | 95033 |
| 108 | 張全蛋    | 男  | 1975-02-10 | 95031 |
| 109 | 趙鐵柱    | 男  | 1974-06-03 | 95031 |
| 110 | 張飛      | 男  | 1974-06-03 | 95038 |
+-----+-----------+-----+------------+-------+

-- 只查詢性別為男,然后按 class 分組箱熬,并限制 class 行大于 1类垦。
SELECT class FROM student WHERE sex = '男' GROUP BY class HAVING COUNT(*) > 1;
+-------+
| class |
+-------+
| 95033 |
| 95031 |
+-------+

NOTLIKE 模糊查詢取反

查詢 student 表中不姓 "王" 的同學記錄。

-- NOT: 取反
-- LIKE: 模糊查詢
mysql> SELECT * FROM student WHERE name NOT LIKE '王%';
+-----+-----------+-----+------------+-------+
| no  | name      | sex | birthday   | class |
+-----+-----------+-----+------------+-------+
| 101 | 曾華      | 男  | 1977-09-01 | 95033 |
| 102 | 匡明      | 男  | 1975-10-02 | 95031 |
| 104 | 李軍      | 男  | 1976-02-20 | 95033 |
| 106 | 陸軍      | 男  | 1974-06-03 | 95031 |
| 108 | 張全蛋    | 男  | 1975-02-10 | 95031 |
| 109 | 趙鐵柱    | 男  | 1974-06-03 | 95031 |
| 110 | 張飛      | 男  | 1974-06-03 | 95038 |
+-----+-----------+-----+------------+-------+

YEAR 與 NOW 函數

查詢 student 表中每個學生的姓名和年齡坦弟。

-- 使用函數 YEAR(NOW()) 計算出當前年份护锤,減去出生年份后得出年齡。
SELECT name, YEAR(NOW()) - YEAR(birthday) as age FROM student;
+-----------+------+
| name      | age  |
+-----------+------+
| 曾華      |   42 |
| 匡明      |   44 |
| 王麗      |   43 |
| 李軍      |   43 |
| 王芳      |   44 |
| 陸軍      |   45 |
| 王尼瑪    |   43 |
| 張全蛋    |   44 |
| 趙鐵柱    |   45 |
| 張飛      |   45 |
+-----------+------+

MAX 與 MIN 函數

查詢 student 表中最大和最小的 birthday 值酿傍。

SELECT MAX(birthday), MIN(birthday) FROM student;
+---------------+---------------+
| MAX(birthday) | MIN(birthday) |
+---------------+---------------+
| 1977-09-01    | 1974-06-03    |
+---------------+---------------+

多段排序

classbirthday 從大到小的順序查詢 student 表烙懦。

SELECT * FROM student ORDER BY class DESC, birthday;
+-----+-----------+-----+------------+-------+
| no  | name      | sex | birthday   | class |
+-----+-----------+-----+------------+-------+
| 110 | 張飛      | 男  | 1974-06-03 | 95038 |
| 103 | 王麗      | 女  | 1976-01-23 | 95033 |
| 104 | 李軍      | 男  | 1976-02-20 | 95033 |
| 107 | 王尼瑪    | 男  | 1976-02-20 | 95033 |
| 101 | 曾華      | 男  | 1977-09-01 | 95033 |
| 106 | 陸軍      | 男  | 1974-06-03 | 95031 |
| 109 | 趙鐵柱    | 男  | 1974-06-03 | 95031 |
| 105 | 王芳      | 女  | 1975-02-10 | 95031 |
| 108 | 張全蛋    | 男  | 1975-02-10 | 95031 |
| 102 | 匡明      | 男  | 1975-10-02 | 95031 |
+-----+-----------+-----+------------+-------+

子查詢 - 5

查詢 "男" 教師及其所上的課程。

SELECT * FROM course WHERE t_no in (SELECT no FROM teacher WHERE sex = '男');
+-------+--------------+------+
| no    | name         | t_no |
+-------+--------------+------+
| 3-245 | 操作系統(tǒng)     | 804  |
| 6-166 | 數字電路     | 856  |
+-------+--------------+------+

MAX 函數與子查詢

查詢最高分同學的 score 表赤炒。

-- 找出最高成績(該查詢只能有一個結果)
SELECT MAX(degree) FROM score;

-- 根據上面的條件篩選出所有最高成績表氯析,
-- 該查詢可能有多個結果,假設 degree 值多次符合條件莺褒。
SELECT * FROM score WHERE degree = (SELECT MAX(degree) FROM score);
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 103  | 3-105 |     92 |
+------+-------+--------+

子查詢 - 6

查詢和 "李軍" 同性別的所有同學 name 掩缓。

-- 首先將李軍的性別作為條件取出來
SELECT sex FROM student WHERE name = '李軍';
+-----+
| sex |
+-----+
| 男  |
+-----+

-- 根據性別查詢 name 和 sex
SELECT name, sex FROM student WHERE sex = (
    SELECT sex FROM student WHERE name = '李軍'
);
+-----------+-----+
| name      | sex |
+-----------+-----+
| 曾華      | 男  |
| 匡明      | 男  |
| 李軍      | 男  |
| 陸軍      | 男  |
| 王尼瑪    | 男  |
| 張全蛋    | 男  |
| 趙鐵柱    | 男  |
| 張飛      | 男  |
+-----------+-----+

子查詢 - 7

查詢和 "李軍" 同性別且同班的同學 name

SELECT name, sex, class FROM student WHERE sex = (
    SELECT sex FROM student WHERE name = '李軍'
) AND class = (
    SELECT class FROM student WHERE name = '李軍'
);
+-----------+-----+-------+
| name      | sex | class |
+-----------+-----+-------+
| 曾華      | 男  | 95033 |
| 李軍      | 男  | 95033 |
| 王尼瑪    | 男  | 95033 |
+-----------+-----+-------+

子查詢 - 8

查詢所有選修 "計算機導論" 課程的 "男" 同學成績表遵岩。

需要的 "計算機導論" 和性別為 "男" 的編號可以在 coursestudent 表中找到你辣。

SELECT * FROM score WHERE c_no = (
    SELECT no FROM course WHERE name = '計算機導論'
) AND s_no IN (
    SELECT no FROM student WHERE sex = '男'
);
+------+-------+--------+
| s_no | c_no  | degree |
+------+-------+--------+
| 101  | 3-105 |     90 |
| 102  | 3-105 |     91 |
| 104  | 3-105 |     89 |
| 109  | 3-105 |     76 |
+------+-------+--------+

按等級查詢

建立一個 grade 表代表學生的成績等級巡通,并插入數據:

CREATE TABLE grade (
    low INT(3),
    upp INT(3),
    grade char(1)
);

INSERT INTO grade VALUES (90, 100, 'A');
INSERT INTO grade VALUES (80, 89, 'B');
INSERT INTO grade VALUES (70, 79, 'C');
INSERT INTO grade VALUES (60, 69, 'D');
INSERT INTO grade VALUES (0, 59, 'E');

SELECT * FROM grade;
+------+------+-------+
| low  | upp  | grade |
+------+------+-------+
|   90 |  100 | A     |
|   80 |   89 | B     |
|   70 |   79 | C     |
|   60 |   69 | D     |
|    0 |   59 | E     |
+------+------+-------+

查詢所有學生的 s_noc_nograde 列舍哄。

思路是宴凉,使用區(qū)間 ( BETWEEN ) 查詢,判斷學生的成績 ( degree ) 在 grade 表的 lowupp 之間表悬。

SELECT s_no, c_no, grade FROM score, grade 
WHERE degree BETWEEN low AND upp;
+------+-------+-------+
| s_no | c_no  | grade |
+------+-------+-------+
| 101  | 3-105 | A     |
| 102  | 3-105 | A     |
| 103  | 3-105 | A     |
| 103  | 3-245 | B     |
| 103  | 6-166 | B     |
| 104  | 3-105 | B     |
| 105  | 3-105 | B     |
| 105  | 3-245 | C     |
| 105  | 6-166 | C     |
| 109  | 3-105 | C     |
| 109  | 3-245 | D     |
| 109  | 6-166 | B     |
+------+-------+-------+

連接查詢

準備用于測試連接查詢的數據:

CREATE DATABASE testJoin;

CREATE TABLE person (
    id INT,
    name VARCHAR(20),
    cardId INT
);

CREATE TABLE card (
    id INT,
    name VARCHAR(20)
);

INSERT INTO card VALUES (1, '飯卡'), (2, '建行卡'), (3, '農行卡'), (4, '工商卡'), (5, '郵政卡');
SELECT * FROM card;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | 飯卡      |
|    2 | 建行卡    |
|    3 | 農行卡    |
|    4 | 工商卡    |
|    5 | 郵政卡    |
+------+-----------+

INSERT INTO person VALUES (1, '張三', 1), (2, '李四', 3), (3, '王五', 6);
SELECT * FROM person;
+------+--------+--------+
| id   | name   | cardId |
+------+--------+--------+
|    1 | 張三   |      1 |
|    2 | 李四   |      3 |
|    3 | 王五   |      6 |
+------+--------+--------+

分析兩張表發(fā)現弥锄,person 表并沒有為 cardId 字段設置一個在 card 表中對應的 id 外鍵。如果設置了的話蟆沫,personcardId 字段值為 6 的行就插不進去籽暇,因為該 cardId 值在 card 表中并沒有。

內連接

要查詢這兩張表中有關系的數據饭庞,可以使用 INNER JOIN ( 內連接 ) 將它們連接在一起戒悠。

-- INNER JOIN: 表示為內連接,將兩張表拼接在一起舟山。
-- on: 表示要執(zhí)行某個條件救崔。
SELECT * FROM person INNER JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id   | name   | cardId | id   | name      |
+------+--------+--------+------+-----------+
|    1 | 張三   |      1 |    1 | 飯卡      |
|    2 | 李四   |      3 |    3 | 農行卡    |
+------+--------+--------+------+-----------+

-- 將 INNER 關鍵字省略掉,結果也是一樣的捏顺。
-- SELECT * FROM person JOIN card on person.cardId = card.id;

注意:card 的整張表被連接到了右邊。

左外連接

完整顯示左邊的表 ( person ) 纬黎,右邊的表如果符合條件就顯示幅骄,不符合則補 NULL

-- LEFT JOIN 也叫做 LEFT OUTER JOIN本今,用這兩種方式的查詢結果是一樣的拆座。
SELECT * FROM person LEFT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id   | name   | cardId | id   | name      |
+------+--------+--------+------+-----------+
|    1 | 張三   |      1 |    1 | 飯卡      |
|    2 | 李四   |      3 |    3 | 農行卡    |
|    3 | 王五   |      6 | NULL | NULL      |
+------+--------+--------+------+-----------+

右外鏈接

完整顯示右邊的表 ( card ) ,左邊的表如果符合條件就顯示冠息,不符合則補 NULL 挪凑。

SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id   | name   | cardId | id   | name      |
+------+--------+--------+------+-----------+
|    1 | 張三   |      1 |    1 | 飯卡      |
|    2 | 李四   |      3 |    3 | 農行卡    |
| NULL | NULL   |   NULL |    2 | 建行卡    |
| NULL | NULL   |   NULL |    4 | 工商卡    |
| NULL | NULL   |   NULL |    5 | 郵政卡    |
+------+--------+--------+------+-----------+

全外鏈接

完整顯示兩張表的全部數據。

-- MySQL 不支持這種語法的全外連接
-- SELECT * FROM person FULL JOIN card on person.cardId = card.id;
-- 出現錯誤:
-- ERROR 1054 (42S22): Unknown column 'person.cardId' in 'on clause'

-- MySQL全連接語法逛艰,使用 UNION 將兩張表合并在一起躏碳。
SELECT * FROM person LEFT JOIN card on person.cardId = card.id
UNION
SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id   | name   | cardId | id   | name      |
+------+--------+--------+------+-----------+
|    1 | 張三   |      1 |    1 | 飯卡      |
|    2 | 李四   |      3 |    3 | 農行卡    |
|    3 | 王五   |      6 | NULL | NULL      |
| NULL | NULL   |   NULL |    2 | 建行卡    |
| NULL | NULL   |   NULL |    4 | 工商卡    |
| NULL | NULL   |   NULL |    5 | 郵政卡    |
+------+--------+--------+------+-----------+
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市散怖,隨后出現的幾起案子菇绵,更是在濱河造成了極大的恐慌,老刑警劉巖镇眷,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咬最,死亡現場離奇詭異,居然都是意外死亡欠动,警方通過查閱死者的電腦和手機永乌,發(fā)現死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人翅雏,你說我怎么就攤上這事圈驼。” “怎么了枚荣?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵碗脊,是天一觀的道長。 經常有香客問我橄妆,道長衙伶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任害碾,我火速辦了婚禮矢劲,結果婚禮上,老公的妹妹穿的比我還像新娘慌随。我一直安慰自己芬沉,他們只是感情好,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布阁猜。 她就那樣靜靜地躺著丸逸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剃袍。 梳的紋絲不亂的頭發(fā)上黄刚,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音民效,去河邊找鬼憔维。 笑死,一個胖子當著我的面吹牛畏邢,可吹牛的內容都是我干的业扒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼舒萎,長吁一口氣:“原來是場噩夢啊……” “哼程储!你這毒婦竟也來了?” 一聲冷哼從身側響起臂寝,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤虱肄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后交煞,有當地人在樹林里發(fā)現了一具尸體咏窿,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年素征,在試婚紗的時候發(fā)現自己被綠了集嵌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萝挤。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖根欧,靈堂內的尸體忽然破棺而出怜珍,到底是詐尸還是另有隱情,我是刑警寧澤凤粗,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布酥泛,位于F島的核電站,受9級特大地震影響嫌拣,放射性物質發(fā)生泄漏柔袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一异逐、第九天 我趴在偏房一處隱蔽的房頂上張望捶索。 院中可真熱鬧,春花似錦灰瞻、人聲如沸腥例。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽燎竖。三九已至,卻和暖如春要销,著一層夾襖步出監(jiān)牢的瞬間底瓣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工蕉陋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拨扶。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓凳鬓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親患民。 傳聞我的和親對象是個殘疾皇子缩举,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348