學習筆記 2021.12.9cont2
2021.12.9
子查詢
相關子查詢
如果子查詢的執行依賴於外部查詢,通常情況下都是因為子查詢中的表用到了外部的表,並進行了條件關聯,因此每執行一次外部查詢,子查詢都要重新計算一次,這樣的子查詢就稱之為關聯子查詢
。
相關子查詢按照一行接一行的順序執行,主查詢的每一行都執行一次子查詢。
典型的運用場景:查詢員工中工資大於本部門平均工資的員工的last_name,salary和其department_id
SELECT last_name,salary,department_id FROM employees e1 WHERE salary >( SELECT AVG(salary) FROM employees e2 WHERE e2.department_id = e1.`department_id` );
由例子的自己的理解:
相關子查詢的關鍵在於內查詢需要用到外面的表中的資料。這裡的處理也可以向上面有種情況一樣,將平均值分部門的結果作為一張表來實現子查詢。
由此可以簡單的結合前面的順序總結一下子查詢的位置安排
exist和not exist
- 關聯子查詢通常也會和 EXISTS操作符一起來使用,用來檢查在子查詢中是否存在滿足條件的行。
- 如果在子查詢中不存在滿足條件的行:
- 條件返回 FALSE
- 繼續在子查詢中查詢
- 如果在子查詢中存在滿足條件的行:
- 不在子查詢中繼續查詢
- 條件返回 TRUE
- NOT EXISTS關鍵字表示如果不存在某種條件,則返回TRUE,否則返回FALSE。
例:查詢departments表中,不存在於employees表中的部門的department_id和department_name
SELECT d.department_id,d.department_name
FROM departments d
WHERE NOT EXISTS
(
SELECT *
FROM employees e
WHERE d.`department_id`=e.`department_id`
);
理解這個關鍵字就理解成子查詢中的一種特殊的使用場景就可以了,其實通過其他方式實現也是很好理解的。
思考:
問題:誰的工資比Abel的高?
解答:
#方式1:自連線 SELECT e2.last_name,e2.salary FROM employees e1,employees e2 WHERE e1.last_name = 'Abel' AND e1.`salary` < e2.`salary`
#方式2:子查詢
SELECT last_name,salary
FROM employees
WHERE salary > (
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
問題:以上兩種方式有好壞之分嗎?
解答:自連線方式好!
題目中可以使用子查詢,也可以使用自連線。一般情況建議你使用自連線,因為在許多 DBMS 的處理過程中,對於自連線的處理速度要比子查詢快得多。
可以這樣理解:子查詢實際上是通過未知表進行查詢後的條件判斷,而自連線是通過已知的自身資料表進行條件判斷,因此在大部分 DBMS 中都對自連線處理進行了優化。
略去了這一章的習題練習,
但從邏輯層面來說這裡的練習題應該是最有難道的,如果有需求,則在這裡來刷題和鍛鍊思維。
建立和管理表
儲存資料是處理資料的第一步
。只有正確地把資料儲存起來,我們才能進行有效的處理和分析。否則,只能是一團亂麻,無從下手。
那麼,怎樣才能把使用者各種經營相關的、紛繁複雜的資料,有序、高效地儲存起來呢? 在 MySQL 中,一個完整的資料儲存過程總共有 4 步,分別是建立資料庫、確認欄位、建立資料表、插入資料。
我們要先建立一個數據庫,而不是直接建立資料表呢?
因為從系統架構的層次上看,MySQL 資料庫系統從大到小依次是資料庫伺服器
、資料庫
、資料表
、資料表的行與列
。
資料庫名的規範要求:
- 資料庫名、表名不得超過30個字元,變數名限制為29個
- 必須只能包含 A–Z, a–z, 0–9, _共63個字元
- 資料庫名、表名、欄位名等物件名中間不要包含空格
- 同一個MySQL軟體中,資料庫不能同名;同一個庫中,表不能重名;同一個表中,欄位不能重名
- 必須保證你的欄位沒有和保留字、資料庫系統或常用方法衝突。如果堅持使用,請在SQL語句中使用`(著重號)引起來
- 保持欄位名和型別的一致性:在命名欄位併為其指定資料型別的時候一定要保證一致性,假如資料型別在一個表裡是整數,那在另一個表裡可就別變成字元型了
建立和管理資料庫
建立資料庫
- 方式1:建立資料庫
CREATE DATABASE 資料庫名;
- 方式2:建立資料庫並指定字符集
CREATE DATABASE 資料庫名 CHARACTER SET 字符集;
- 方式3:判斷資料庫是否已經存在,不存在則建立資料庫(
推薦
)
CREATE DATABASE IF NOT EXISTS 資料庫名;
如果MySQL中已經存在相關的資料庫,則忽略建立語句,不再建立資料庫。
示例:
CREATE DATABASE IF NOT EXISTS mytest1;
SHOW DATABASES;
資料庫的使用:
- 檢視當前所有的資料庫
SHOW DATABASES; #有一個S,代表多個數據庫
- 檢視當前正在使用的資料庫
SELECT DATABASE(); #使用的一個 mysql 中的全域性函式
- 檢視指定庫下所有的表
SHOW TABLES FROM 資料庫名;
- 檢視資料庫的建立資訊
SHOW CREATE DATABASE 資料庫名;
或者:
SHOW CREATE DATABASE 資料庫名\G
- 使用/切換資料庫
USE 資料庫名;
資料庫的刪除:
- 方式1:刪除指定的資料庫
DROP DATABASE 資料庫名;
- 方式2:刪除指定的資料庫(
推薦
)
DROP DATABASE IF EXISTS 資料庫名;
要刪除的不存在則默默推出,不會報錯。
建立表
常見的變數型別
型別 | 型別舉例 |
---|---|
整數型別 | TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT |
浮點型別 | FLOAT、DOUBLE |
定點數型別 | DECIMAL |
位型別 | BIT |
日期時間型別 | YEAR、TIME、DATE、DATETIME、TIMESTAMP |
文字字串型別 | CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT |
列舉型別 | ENUM |
集合型別 | SET |
二進位制字串型別 | BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB |
JSON型別 | JSON物件、JSON陣列 |
空間資料型別 | 單值:GEOMETRY、POINT、LINESTRING、POLYGON; 集合:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION |
具體的介紹有需要再去仔細瞭解。
建立資料表:
- 必須具備:
- CREATE TABLE許可權
- 儲存空間
- 語法格式:
CREATE TABLE [IF NOT EXISTS] 表名(
欄位1, 資料型別 [約束條件] [預設值],
欄位2, 資料型別 [約束條件] [預設值],
欄位3, 資料型別 [約束條件] [預設值],
……
[表約束條件]
);
加上了IF NOT EXISTS關鍵字,則表示:如果當前資料庫中不存在要建立的資料表,則建立資料表;如果當前資料庫中已經存在要建立的資料表,則忽略建表語句,不再建立資料表。
- 必須指定:
- 表名
- 列名(或欄位名),資料型別,長度
- 可選指定:
- 約束條件
- 預設值
示例:
CREATE TABLE IF NOT EXISTS mytest
(
id INT,
m_name VARCHAR(15),
h_date DATE
);
這裡使用varchar定義字串的時候必須指明其長度。
建立方法2:
-
使用 AS subquery 選項,將建立表和插入資料結合起來
-
指定的列和子查詢中的列要一一對應
-
通過列名和預設值定義列
這種建立方式的更多的細節:
- 使用這種方法建立表後,原始的資料也是跟著保留的。
- 查詢語句中欄位的別名,可以作為新建立的表的欄位的名稱。
- 建立空表的話就可以在where篩選條件中寫一個不可能條件,比如說1=2即可。
具體示例:
CREATE TABLE myemp1
AS
SELECT employee_id,last_name,salary
FROM employees;
管理表
修改表
新增一個欄位
- 向已有的表中新增列
語法格式如下:
ALTER TABLE 表名 ADD 【COLUMN】 欄位名 欄位型別 【FIRST|AFTER 欄位名】;
修改一個欄位
- 可以修改列的資料型別,長度、預設值和位置
- 但一般也不會去改變資料型別。更多的是去改長度。
- 修改欄位資料型別、長度、預設值、位置的語法格式如下:
ALTER TABLE 表名 MODIFY 【COLUMN】 欄位名1 欄位型別 【DEFAULT 預設值】【FIRST|AFTER 欄位名2】;
示例:
ALTER TABLE dept80
MODIFY last_name VARCHAR(30);
ALTER TABLE dept80
MODIFY salary double(9,2) default 1000;
- 對預設值的修改隻影響今後對錶的修改
重新命名一個欄位
使用 CHANGE old_column new_column dataType子句重新命名列。語法格式如下:
ALTER TABLE 表名 CHANGE 【column】 列名 新列名 新資料型別;
舉例:
ALTER TABLE dept80
CHANGE department_name dept_name varchar(15);
刪除一個欄位
刪除表中某個欄位的語法格式如下:
ALTER TABLE 表名 DROP 【COLUMN】欄位名
舉例:
ALTER TABLE dept80
DROP COLUMN job_id;
重命名錶
- 使用RENAME
RENAME TABLE emp
TO myemp;
刪除表
-
在MySQL中,當一張資料表
沒有與其他任何資料表形成關聯關係
時,可以將當前資料表直接刪除。 -
資料和結構都被刪除
-
所有正在執行的相關事務被提交
-
所有相關索引被刪除
-
語法格式:
DROP TABLE [IF EXISTS] 資料表1 [, 資料表2, …, 資料表n];
IF EXISTS
的含義為:如果當前資料庫中存在相應的資料表,則刪除資料表;如果當前資料庫中不存在相應的資料表,則忽略刪除語句,不再執行刪除資料表的操作。
- 舉例:
DROP TABLE dept80;
- DROP TABLE 語句不能回滾
清空表
-
TRUNCATE TABLE語句:
- 刪除表中所有的資料
- 釋放表的儲存空間
- 但是清空過後表的結構還是存在的。
-
舉例:
TRUNCATE TABLE detail_dept;
清空資料和刪除資料的區別
- TRUNCATE語句不能回滾,而使用 DELETE 語句刪除資料,可以回滾
- TRUNCATE是DDL的代表。DDL的操作一旦執行就無法回滾。因為在執行完DDL操作後,一定會執行commit操作。DML的操作預設情況下一旦執行也是沒法回滾的,但是可以修改引數,則此時的操作可以回滾。
- 對比:
SET autocommit = FALSE;
DELETE FROM emp2;
#TRUNCATE TABLE emp2;
SELECT * FROM emp2;
ROLLBACK;
SELECT * FROM emp2;
阿里開發規範:
【參考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但 TRUNCATE 無事務且不觸發 TRIGGER,有可能造成事故,故不建議在開發程式碼中使用此語句。
說明:TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。
DML之增刪改
插入資料
方式1:VALUES的方式新增
使用這種語法一次只能向表中插入一條資料。
情況1:為表的所有欄位按預設順序插入資料
INSERT INTO 表名
VALUES (value1,value2,....);
值列表中需要為表的每一個欄位指定值,並且值的順序必須和資料表中欄位定義時的順序相同。
舉例:
INSERT INTO emp1
VALUES (70, 'Pub', '2000-10-11', 1700);
此時即沒有指明新增的欄位,必須按照宣告的欄位按順序的去新增。
情況2:為表的指定欄位插入資料
INSERT INTO emp1(id,hire_date,salary,`name`)
VALUES(2,'1999-08-08',6545,'yiyi');
為表的指定欄位插入資料,就是在INSERT語句中只向部分欄位中插入值,而其他欄位的值為表定義時的預設值。
在 INSERT 子句中隨意列出列名,但是一旦列出,VALUES中要插入的value1,....valuen需要與column1,...columnn列一一對應。如果型別不同,將無法插入,並且MySQL會產生錯誤。
此時如果上面的欄位多於下面的值,那麼當下面的值個數少的時候,即沒有賦值的則直接賦值為NULL。
情況3:同時插入多條記錄
INSERT語句可以同時向資料表中插入多條記錄,插入時指定多個值列表,每個值列表之間用逗號分隔開,基本語法格式如下:
INSERT INTO table_name
VALUES
(value1 [,value2, …, valuen]),
(value1 [,value2, …, valuen]),
……
(value1 [,value2, …, valuen]);
或者
INSERT INTO table_name(column1 [, column2, …, columnn])
VALUES
(value1 [,value2, …, valuen]),
(value1 [,value2, …, valuen]),
……
(value1 [,value2, …, valuen]);
舉例:
INSERT INTO emp(emp_id,emp_name)
VALUES (1001,'shkstart'),
(1002,'atguigu'),
(1003,'Tom');
使用INSERT同時插入多條記錄時,MySQL會返回一些在執行單行插入時沒有的額外資訊,這些資訊的含義如下:
● Records:表明插入的記錄條數。
● Duplicates:表明插入時被忽略的記錄,原因可能是這些記錄包含了重複的主鍵值。
● Warnings:表明有問題的資料值,例如發生資料型別轉換。
一個同時插入多行記錄的INSERT語句等同於多個單行插入的INSERT語句,但是多行的INSERT語句在處理過程中
效率更高
。因為MySQL執行單條INSERT語句插入多行資料比使用多條INSERT語句快,所以在插入多條記錄時最好選擇使用單條INSERT語句的方式插入。
小結:
-
VALUES
也可以寫成VALUE
,但是VALUES是標準寫法。 -
字元和日期型資料應包含在單引號中。
方式2:將查詢結果插入到表中
INSERT還可以將SELECT語句查詢的結果插入到表中,此時不需要把每一條記錄的值一個一個輸入,只需要使用一條INSERT語句和一條SELECT語句組成的組合語句即可快速地從一個或多個表中向一個表中插入多行。
基本語法格式如下:
INSERT INTO 目標表名
(tar_column1 [, tar_column2, …, tar_columnn])
SELECT
(src_column1 [, src_column2, …, src_columnn])
FROM 源表名
[WHERE condition]
- 在 INSERT 語句中加入子查詢。
- 不必書寫 VALUES 子句。
- 子查詢中的值列表應與 INSERT 子句中的列名對應。且必須一一對應。
- 且被新增的表中的欄位長度不能小於複製物件的表中的欄位的長度。
舉例:
INSERT INTO emp1(id,`name`,salary,hire_date)
SELECT employee_id,last_name,salary,hire_date
FROM employees
WHERE department_id IN (60,70);