1、SQLite Joins
SQLite 的 Joins 子句用於結合兩個或多個數據庫中表的記錄。JOIN 是一種通過共同值來結合兩個表中字段的手段。
SQL 定義了三種主要類型的連接:
交叉連接 - CROSS JOIN
內連接 - INNER JOIN
外連接 - OUTER JOIN
交叉連接 - CROSS JOIN
交叉連接(CROSS JOIN)把第一個表的每一行與第二個表的每一行進行匹配。如果兩個輸入表分別有 x 和 y 行,則結果表有 x*y 行。由於交叉連接(CROSS JOIN)有可能產生非常大的表,使用時必須謹慎,只在適當的時候使用它們。
下面是交叉連接(CROSS JOIN)的語法:
SELECT ... FROM table1 CROSS JOIN table2 ...
在我們繼續之前,讓我們假設有兩個表 COMPANY 和 DEPARTMENT。我們已經看到了用來填充 COMPANY 表的 INSERT 語句。現在讓我們假設 COMPANY 表的記錄列表如下:
ID NAME AGE ADDRESS SALARY
---------- ---------- ---------- ---------- ----------
1 Paul 32 California 20000.0
2 Allen 25 Texas 15000.0
3 Teddy 23 Norway 20000.0
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
最後,我們在 DEPARTMENT 表中有下列的記錄列表:
ID DEPT EMP_ID
---------- ---------- ----------
1 IT Billing 1
2 Engineerin 2
3 Finance 7
基於上面的表,我們可以寫一個交叉連接(CROSS JOIN),如下所示:
sqlite> SELECT EMP_ID, NAME, DEPT FROM COMPANY CROSS JOIN DEPARTMENT;
上面的查詢會產生以下結果:
EMP_ID NAME DEPT
---------- ---------- ----------
1 Paul IT Billing
2 Paul Engineerin
7 Paul Finance
1 Allen IT Billing
2 Allen Engineerin
7 Allen Finance
1 Teddy IT Billing
2 Teddy Engineerin
7 Teddy Finance
1 Mark IT Billing
2 Mark Engineerin
7 Mark Finance
1 David IT Billing
2 David Engineerin
7 David Finance
1 Kim IT Billing
2 Kim Engineerin
7 Kim Finance
1 James IT Billing
2 James Engineerin
7 James Finance
內連接 - INNER JOIN
內連接(INNER JOIN)根據連接謂詞結合兩個表(table1 和 table2)的列值來創建一個新的結果表。查詢會把 table1 中的每一行與 table2 中的每一行進行比較,找到所有滿足連接謂詞的行的匹配對。當滿足連接謂詞時,A 和 B 行的每個匹配對的列值會合併成一個結果行。
內連接(INNER JOIN)是最常見的連接類型,是默認的連接類型。INNER 關鍵字是可選的。
下面是內連接(INNER JOIN)的語法:
SELECT ... FROM table1 [INNER] JOIN table2 ON conditional_expression ...
爲了避免冗餘,並保持較短的措辭,可以使用 USING 表達式聲明內連接(INNER JOIN)條件。這個表達式指定一個或多個列的列表:
SELECT ... FROM table1 JOIN table2 USING ( column1 ,... ) ...
自然連接(NATURAL JOIN)類似於 JOIN...USING,只是它會自動測試存在兩個表中的每一列的值之間相等值:
SELECT ... FROM table1 NATURAL JOIN table2...
基於上面的表,我們可以寫一個內連接(INNER JOIN),如下所示:
sqlite> SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID;
上面的查詢會產生以下結果:
EMP_ID NAME DEPT
---------- ---------- ----------
1 Paul IT Billing
2 Allen Engineerin
7 James Finance
外連接 - OUTER JOIN
外連接(OUTER JOIN)是內連接(INNER JOIN)的擴展。雖然 SQL 標準定義了三種類型的外連接:LEFT、RIGHT、FULL,但 SQLite 只支持 左外連接(LEFT OUTER JOIN)。
外連接(OUTER JOIN)聲明條件的方法與內連接(INNER JOIN)是相同的,使用 ON、USING 或 NATURAL 關鍵字來表達。最初的結果表以相同的方式進行計算。一旦主連接計算完成,外連接(OUTER JOIN)將從一個或兩個表中任何未連接的行合併進來,外連接的列使用 NULL 值,將它們附加到結果表中。
下面是左外連接(LEFT OUTER JOIN)的語法:
SELECT ... FROM table1 LEFT OUTER JOIN table2 ON conditional_expression ...
爲了避免冗餘,並保持較短的措辭,可以使用 USING 表達式聲明外連接(OUTER JOIN)條件。這個表達式指定一個或多個列的列表:
SELECT ... FROM table1 LEFT OUTER JOIN table2 USING ( column1 ,... ) ...
基於上面的表,我們可以寫一個外連接(OUTER JOIN),如下所示:
sqlite> SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID;
上面的查詢會產生以下結果:
EMP_ID NAME DEPT
---------- ---------- ----------
1 Paul IT Billing
2 Allen Engineerin
Teddy
Mark
David
Kim
7 James Finance
2、SQLite Unions 子句
SQLite的 UNION 子句/運算符用於合併兩個或多個 SELECT 語句的結果,不返回任何重複的行。
爲了使用 UNION,每個 SELECT 被選擇的列數必須是相同的,相同數目的列表達式,相同的數據類型,並確保它們有相同的順序,但它們不必具有相同的長度。
語法
UNION 的基本語法如下:
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
UNION
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
這裏給定的條件根據需要可以是任何表達式。
實例
假設有下面兩個表,(1)COMPANY 表如下所示:
sqlite> select * from COMPANY;
ID NAME AGE ADDRESS SALARY
---------- -------------------- ---------- ---------- ----------
1 Paul 32 California 20000.0
2 Allen 25 Texas 15000.0
3 Teddy 23 Norway 20000.0
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
(2)另一個表是 DEPARTMENT,如下所示:
ID DEPT EMP_ID
---------- -------------------- ----------
1 IT Billing 1
2 Engineering 2
3 Finance 7
4 Engineering 3
5 Finance 4
6 Engineering 5
7 Finance 6
現在,讓我們使用 SELECT 語句及 UNION 子句來連接兩個表,如下所示:
sqlite> SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID
UNION
SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID;
這將產生以下結果:
EMP_ID NAME DEPT
---------- -------------------- ----------
1 Paul IT Billing
2 Allen Engineerin
3 Teddy Engineerin
4 Mark Finance
5 David Engineerin
6 Kim Finance
7 James Finance
UNION ALL 子句
UNION ALL 運算符用於結合兩個 SELECT 語句的結果,包括重複行。
適用於 UNION 的規則同樣適用於 UNION ALL 運算符。
語法
UNION ALL 的基本語法如下:
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
UNION ALL
SELECT column1 [, column2 ]
FROM table1 [, table2 ]
[WHERE condition]
這裏給定的條件根據需要可以是任何表達式。
實例
現在,讓我們使用 SELECT 語句及 UNION ALL 子句來連接兩個表,如下所示:
sqlite> SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID
UNION ALL
SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID;
這將產生以下結果:
3、SQLite NULL 值
SQLite 的 NULL 是用來表示一個缺失值的項。表中的一個 NULL 值是在字段中顯示爲空白的一個值。
帶有 NULL 值的字段是一個不帶有值的字段。NULL 值與零值或包含空格的字段是不同的,理解這點是非常重要的。
語法
創建表時使用 NULL 的基本語法如下:
SQLite> CREATE TABLE COMPANY(
ID INT PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
在這裏,NOT NULL 表示列總是接受給定數據類型的顯式值。這裏有兩個列我們沒有使用 NOT NULL,這意味着這兩個列可以爲 NULL。
帶有 NULL 值的字段在記錄創建的時候可以保留爲空。
4、SQLite 別名
您可以暫時把表或列重命名爲另一個名字,這被稱爲別名。使用表別名是指在一個特定的 SQLite 語句中重命名錶。重命名是臨時的改變,在數據庫中實際的表的名稱不會改變。
列別名用來爲某個特定的 SQLite 語句重命名錶中的列
語法
表 別名的基本語法如下:
SELECT column1, column2....
FROM table_name AS alias_name
WHERE [condition];
列 別名的基本語法如下:
SELECT column_name AS alias_name
FROM table_name
WHERE [condition];
假設有下面兩個表,(1)COMPANY 表如下所示:
sqlite> select * from COMPANY;
ID NAME AGE ADDRESS SALARY
---------- -------------------- ---------- ---------- ----------
1 Paul 32 California 20000.0
2 Allen 25 Texas 15000.0
3 Teddy 23 Norway 20000.0
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
(2)另一個表是 DEPARTMENT,如下所示:
ID DEPT EMP_ID
---------- -------------------- ----------
1 IT Billing 1
2 Engineering 2
3 Finance 7
4 Engineering 3
5 Finance 4
6 Engineering 5
7 Finance 6
現在,下面是 表別名 的用法,在這裏我們使用 C 和 D 分別作爲 COMPANY 和 DEPARTMENT 表的別名:
sqlite> SELECT C.ID, C.NAME, C.AGE, D.DEPT
FROM COMPANY AS C, DEPARTMENT AS D
WHERE C.ID = D.EMP_ID;
上面的 SQLite 語句將產生下面的結果:
ID NAME AGE DEPT
---------- ---------- ---------- ----------
1 Paul 32 IT Billing
2 Allen 25 Engineerin
3 Teddy 23 Engineerin
4 Mark 25 Finance
5 David 27 Engineerin
6 Kim 22 Finance
7 James 24 Finance
讓我們看一個 列別名 的實例,在這裏 COMPANY_ID 是 ID 列的別名,COMPANY_NAME 是 name 列的別名:
sqlite> SELECT C.ID AS COMPANY_ID, C.NAME AS COMPANY_NAME, C.AGE, D.DEPT
FROM COMPANY AS C, DEPARTMENT AS D
WHERE C.ID = D.EMP_ID;
上面的 SQLite 語句將產生下面的結果:
COMPANY_ID COMPANY_NAME AGE DEPT
---------- ------------ ---------- ----------
1 Paul 32 IT Billing
2 Allen 25 Engineerin
3 Teddy 23 Engineerin
4 Mark 25 Finance
5 David 27 Engineerin
6 Kim 22 Finance
7 James 24 Finance
5、SQLite 觸發器(Trigger)
SQLite 的觸發器是數據庫的回調函數,它會自動執行/指定的數據庫事件發生時調用。以下是關於SQLite的觸發器的要點: SQLite 觸發器(Trigger)是數據庫的回調函數,它會在指定的數據庫事件發生時自動執行/調用。以下是關於 SQLite 的觸發器(Trigger)的要點:
SQLite 的觸發器(Trigger)可以指定在特定的數據庫表發生 DELETE、INSERT 或 UPDATE 時觸發,或在一個或多個指定表的列發生更新時觸發。
SQLite 只支持 FOR EACH ROW 觸發器(Trigger),沒有 FOR EACH STATEMENT 觸發器(Trigger)。因此,明確指定 FOR EACH ROW 是可選的。
WHEN 子句和觸發器(Trigger)動作可能訪問使用表單 NEW.column-name 和 OLD.column-name 的引用插入、刪除或更新的行元素,其中 column-name 是從與觸發器關聯的表的列的名稱。
如果提供 WHEN 子句,則只針對 WHEN 子句爲真的指定行執行 SQL 語句。如果沒有提供 WHEN 子句,則針對所有行執行 SQL 語句。
BEFORE 或 AFTER 關鍵字決定何時執行觸發器動作,決定是在關聯行的插入、修改或刪除之前或者之後執行觸發器動作。
當觸發器相關聯的表刪除時,自動刪除觸發器(Trigger)。
要修改的表必須存在於同一數據庫中,作爲觸發器被附加的表或視圖,且必須只使用 tablename,而不是 database.tablename。
一個特殊的 SQL 函數 RAISE() 可用於觸發器程序內拋出異常。
語法
創建 觸發器(Trigger) 的基本語法如下:
CREATE TRIGGER trigger_name [BEFORE|AFTER] event_name
ON table_name
BEGIN
-- Trigger logic goes here....
END;
在這裏,event_name 可以是在所提到的表 table_name 上的 INSERT、DELETE 和 UPDATE 數據庫操作。您可以在表名後選擇指定 FOR EACH ROW。
以下是在 UPDATE 操作上在表的一個或多個指定列上創建觸發器(Trigger)的語法:
CREATE TRIGGER trigger_name [BEFORE|AFTER] UPDATE OF column_name
ON table_name
BEGIN
-- Trigger logic goes here....
END;
實例
讓我們假設一個情況,我們要爲被插入到新創建的 COMPANY 表(如果已經存在,則刪除重新創建)中的每一個記錄保持審計試驗:
sqlite> CREATE TABLE COMPANY(
ID INT PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
爲了保持審計試驗,我們將創建一個名爲 AUDIT 的新表。每當 COMPANY 表中有一個新的記錄項時,日誌消息將被插入其中:
sqlite> CREATE TABLE AUDIT(
EMP_ID INT NOT NULL,
ENTRY_DATE TEXT NOT NULL
);
在這裏,ID 是 AUDIT 記錄的 ID,EMP_ID 是來自 COMPANY 表的 ID,DATE 將保持 COMPANY 中記錄被創建時的時間戳。所以,現在讓我們在 COMPANY 表上創建一個觸發器,如下所示:
sqlite> CREATE TRIGGER audit_log AFTER INSERT
ON COMPANY
BEGIN
INSERT INTO AUDIT(EMP_ID, ENTRY_DATE) VALUES (new.ID, datetime('now'));
END;
現在,我們將開始在 COMPANY 表中插入記錄,這將導致在 AUDIT 表中創建一個審計日誌記錄。因此,讓我們在 COMPANY 表中創建一個記錄,如下所示:
sqlite> INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (1, 'Paul', 32, 'California', 20000.00 );
這將在 COMPANY 表中創建如下一個記錄:
ID NAME AGE ADDRESS SALARY
---------- ---------- ---------- ---------- ----------
1 Paul 32 California 20000.0
同時,將在 AUDIT 表中創建一個記錄。這個紀錄是觸發器的結果,這是我們在 COMPANY 表上的 INSERT 操作上創建的觸發器(Trigger)。類似的,可以根據需要在 UPDATE 和 DELETE 操作上創建觸發器(Trigger)。
EMP_ID ENTRY_DATE
---------- -------------------
1 2013-04-05 06:26:00
列出觸發器(TRIGGERS)
您可以從 sqlite_master 表中列出所有觸發器,如下所示:
sqlite> SELECT name FROM sqlite_master
WHERE type = 'trigger';
上面的 SQLite 語句只會列出一個條目,如下:
name
----------
audit_log
如果您想要列出特定表上的觸發器,則使用 AND 子句連接表名,如下所示:
sqlite> SELECT name FROM sqlite_master
WHERE type = 'trigger' AND tbl_name = 'COMPANY';
上面的 SQLite 語句只會列出一個條目,如下:
name
----------
audit_log
刪除觸發器(TRIGGERS)
下面是 DROP 命令,可用於刪除已有的觸發器:
sqlite> DROP TRIGGER trigger_name;
上面沒有關於 for each row 和 when 的實例,這裏補充一下。
for each row 是操作語句每影響到一行的時候就觸發一次,也就是刪了 10 行就觸發 10 次,而 for each state 一條操作語句就觸發一次,有時沒有被影響的行也執行。sqlite 只實現了 for each row 的觸發。when 和 for each row 用法是這樣的:
CREATE TRIGGER trigger_name
AFTER UPDATE OF id ON table_1
FOR EACH ROW
WHEN new.id>30
BEGIN
UPDATE table_2 SET id=new.id WHERE table_2.id=old.id;
END;
上面的觸發器在 table_1 改 id 的時候如果新的 id>30 就把 表table_2 中和表table_1 id 相等的行一起改爲新的 id
6、SQLite 索引(Index)
索引(Index)是一種特殊的查找表,數據庫搜索引擎用來加快數據檢索。簡單地說,索引是一個指向表中數據的指針。一個數據庫中的索引與一本書後邊的索引是非常相似的。
例如,如果您想在一本討論某個話題的書中引用所有頁面,您首先需要指向索引,索引按字母順序列出了所有主題,然後指向一個或多個特定的頁碼。
索引有助於加快 SELECT 查詢和 WHERE 子句,但它會減慢使用 UPDATE 和 INSERT 語句時的數據輸入。索引可以創建或刪除,但不會影響數據。
使用 CREATE INDEX 語句創建索引,它允許命名索引,指定表及要索引的一列或多列,並指示索引是升序排列還是降序排列。
索引也可以是唯一的,與 UNIQUE 約束類似,在列上或列組合上防止重複條目。
CREATE INDEX 命令
CREATE INDEX 的基本語法如下:
CREATE INDEX index_name ON table_name;
單列索引
單列索引是一個只基於表的一個列上創建的索引。基本語法如下:
CREATE INDEX index_name
ON table_name (column_name);
唯一索引
使用唯一索引不僅是爲了性能,同時也爲了數據的完整性。唯一索引不允許任何重複的值插入到表中。基本語法如下:
CREATE UNIQUE INDEX index_name
on table_name (column_name);
組合索引
組合索引是基於一個表的兩個或多個列上創建的索引。基本語法如下:
CREATE INDEX index_name
on table_name (column1, column2);
是否要創建一個單列索引還是組合索引,要考慮到您在作爲查詢過濾條件的 WHERE 子句中使用非常頻繁的列。
如果只使用到一個列,則選擇使用單列索引。如果在作爲過濾的 WHERE 子句中有兩個或多個列經常使用,則選擇使用組合索引。
隱式索引
隱式索引是在創建對象時,由數據庫服務器自動創建的索引。索引自動創建爲主鍵約束和唯一約束。
實例
下面是一個例子,我們將在 COMPANY 表的 salary 列上創建一個索引:
sqlite> CREATE INDEX salary_index ON COMPANY (salary);
現在,讓我們使用 .indices 命令列出 COMPANY 表上所有可用的索引,如下所示:
sqlite> .indices COMPANY
這將產生如下結果,其中 sqlite_autoindex_COMPANY_1 是創建表時創建的隱式索引。
salary_index
sqlite_autoindex_COMPANY_1
您可以列出數據庫範圍的所有索引,如下所示:
sqlite> SELECT * FROM sqlite_master WHERE type = 'index';
DROP INDEX 命令
一個索引可以使用 SQLite 的 DROP 命令刪除。當刪除索引時應特別注意,因爲性能可能會下降或提高。
基本語法如下:
DROP INDEX index_name;
您可以使用下面的語句來刪除之前創建的索引:
sqlite> DROP INDEX salary_index;
什麼情況下要避免使用索引?
雖然索引的目的在於提高數據庫的性能,但這裏有幾個情況需要避免使用索引。使用索引時,應重新考慮下列準則:
索引不應該使用在較小的表上。
索引不應該使用在有頻繁的大批量的更新或插入操作的表上。
索引不應該使用在含有大量的 NULL 值的列上。
索引不應該使用在頻繁操作的列上。
7、SQLite Indexed By
"INDEXED BY index-name" 子句規定必須需要命名的索引來查找前面表中值。
如果索引名 index-name 不存在或不能用於查詢,然後 SQLite 語句的準備失敗。
"NOT INDEXED" 子句規定當訪問前面的表(包括由 UNIQUE 和 PRIMARY KEY 約束創建的隱式索引)時,沒有使用索引。
然而,即使指定了 "NOT INDEXED",INTEGER PRIMARY KEY 仍然可以被用於查找條目。
語法
下面是 INDEXED BY 子句的語法,它可以與 DELETE、UPDATE 或 SELECT 語句一起使用:
SELECT|DELETE|UPDATE column1, column2...
INDEXED BY (index_name)
table_name
WHERE (CONDITION);
實例
假設有表 COMPANY,我們將創建一個索引,並用它進行 INDEXED BY 操作。
sqlite> CREATE INDEX salary_index ON COMPANY(salary);
sqlite>
現在使用 INDEXED BY 子句從表 COMPANY 中選擇數據,如下所示:
sqlite> SELECT * FROM COMPANY INDEXED BY salary_index WHERE salary > 5000;
8、SQLite Alter 命令
SQLite 的 ALTER TABLE 命令不通過執行一個完整的轉儲和數據的重載來修改已有的表。您可以使用 ALTER TABLE 語句重命名錶,使用 ALTER TABLE 語句還可以在已有的表中添加額外的列。
在 SQLite 中,除了重命名錶和在已有的表中添加列,ALTER TABLE 命令不支持其他操作。
語法
用來重命名已有的表的 ALTER TABLE 的基本語法如下:
ALTER TABLE database_name.table_name RENAME TO new_table_name;
用來在已有的表中添加一個新的列的 ALTER TABLE 的基本語法如下:
ALTER TABLE database_name.table_name ADD COLUMN column_def...;
實例
假設我們的 COMPANY 表有如下記錄:
ID NAME AGE ADDRESS SALARY
---------- ---------- ---------- ---------- ----------
1 Paul 32 California 20000.0
2 Allen 25 Texas 15000.0
3 Teddy 23 Norway 20000.0
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
現在,讓我們嘗試使用 ALTER TABLE 語句重命名該表,如下所示:
sqlite> ALTER TABLE COMPANY RENAME TO OLD_COMPANY;
上面的 SQLite 語句將重命名 COMPANY 表爲 OLD_COMPANY。現在,讓我們嘗試在 OLD_COMPANY 表中添加一個新的列,如下所示:
sqlite> ALTER TABLE OLD_COMPANY ADD COLUMN SEX char(1);
現在,COMPANY 表已經改變,使用 SELECT 語句輸出如下:
ID NAME AGE ADDRESS SALARY SEX
---------- ---------- ---------- ---------- ---------- ---
1 Paul 32 California 20000.0
2 Allen 25 Texas 15000.0
3 Teddy 23 Norway 20000.0
4 Mark 25 Rich-Mond 65000.0
5 David 27 Texas 85000.0
6 Kim 22 South-Hall 45000.0
7 James 24 Houston 10000.0
請注意,新添加的列是以 NULL 值來填充的。
9、SQLite Truncate Table
在 SQLite 中,並沒有 TRUNCATE TABLE 命令,但可以使用 SQLite 的 DELETE 命令從已有的表中刪除全部的數據,但建議使用 DROP TABLE 命令刪除整個表,然後再重新創建一遍。
語法
DELETE 命令的基本語法如下:
sqlite> DELETE FROM table_name;
DROP TABLE 的基本語法如下:
sqlite> DROP TABLE table_name;
如果您使用 DELETE TABLE 命令刪除所有記錄,建議使用 VACUUM 命令清除未使用的空間。