SQLite3命令操做大全html
SQLite庫包含一個名字叫作sqlite3的命令行,它可讓用戶手工輸入並執行面向SQLite數據庫的SQL命令。本文檔提供一個樣使用sqlite3的簡要說明.node
建立表: create table 表名(元素名 類型,…);android
刪除表: drop table 表名; git
插入數據: insert into 表名 values(, , ,) ; sql
建立索引: create [unique] index 索引名on 表名(col….);shell
刪除索引: drop index 索引名(索引是不可更改的,想更改必須刪除從新建)
刪除數據: delete from 表名; 數據庫
更新數據: update 表名 set 字段=’修改後的內容’ where 條件; 服務器
增長一個列: Alter table 表名 add column 字段 數據類型; 網絡
選擇查詢: select 字段(以」,」隔開) from 表名 where 條件; 數據結構
日期和時間: Select datetime('now')
日期: select date('now');
時間: select time('now');
總數:select count(*) from table1;
求和:select sum(field1) from table1;
平均:select avg(field1) from table1;
最大:select max(field1) from table1;
最小:select min(field1) from table1;
排序:select 字段 from table1 order by 字段(desc或asc) ;(降序或升序)
分組:select 字段 from table1 group by 字段,字段… ;
限制輸出:select 字段fromtable1 limit x offset y;
= select 字段 from table1 limit y , x;
(備註:跳過y行,取x行數據)
(操做仍待完善)…
NULL 值爲NULL
INTEGER 值爲帶符號的整型,根據類別用1,2,3,4,6,8字節存儲
REAL 值爲浮點型,8字節存儲
TEXT 值爲text字符串,使用數據庫編碼(UTF-8, UTF-16BE or UTF-16-LE)存儲
BLOB 值爲二進制數據,具體看實際輸入
但實際上,sqlite3也接受以下的數據類型:
smallint 16 位元的整數
interger 32 位元的整數
decimal(p,s) p 精確值和 s 大小的十進位整數,精確值p是指所有有幾個數(digits)大小值 ,s是指小數點後有幾位數。若是沒有特別指定,則系統會設爲 p=5; s=0 。
float 32位元的實數。
double 64位元的實數。
char(n) n 長度的字串,n不能超過 254。
varchar(n) 長度不固定且其最大長度爲 n 的字串,n不能超過 4000。
graphic(n) 和 char(n) 同樣,不過其單位是兩個字元 double-bytes, n不能超過127。這個形態是爲了支援兩個字元長度的字體,例如中文字。
vargraphic(n) 可變長度且其最大長度爲 n 的雙字元字串,n不能超過 2000。
date 包含了 年份、月份、日期。
time 包含了 小時、分鐘、秒。
timestamp 包含了 年、月、日、時、分、秒、千分之一秒。
若是將聲明表的一列設置爲 INTEGER PRIMARY KEY,則具備:
1.每當你在該列上插入一NULL值時, NULL自動被轉換爲一個比該列中最大值大1的一個整數;
2.若是表是空的, 將會是1;
abs(X)返回給定數字表達式的絕對值。
max(X,Y[,...])返回表達式的最大值。
min(X,Y[,...])返回表達式的最小值。
random(*)返回隨機數。
round(X[,Y])返回數字表達式並四捨五入爲指定的長度或精度。
length(X)返回給定字符串表達式的字符個數。
lower(X)將大寫字符數據轉換爲小寫字符數據後返回字符表達式。
upper(X)返回將小寫字符數據轉換爲大寫的字符表達式。
substr(X,Y,Z)返回表達式的一部分。
randstr()
quote(A)
like(A,B)
肯定給定的字符串是否與指定的模式匹配。
glob(A,B)
coalesce(X,Y[,...])
ifnull(X,Y)
nullif(X,Y)
avg(X)返回組中值的平均值。
count(X)返回組中項目的數量。
max(X)返回組中值的最大值。
min(X)返回組中值的最小值。
sum(X)返回表達式中全部值的和。
typeof(X)返回數據的類型。
last_insert_rowid()返回最後插入的數據的 ID 。
sqlite_version(*)返回 SQLite 的版本。
change_count()返回受上一語句影響的行數。
last_statement_change_count()
(成批操做的時候,啓動事務,比不啓動事務快n倍)
開始事物處理
BEGIN TRANSACTION;
…………..
進行對數據庫操做
…………..
事物提交
COMMIT;
具體事例以下:
假設有一個 t1 表,其中有 "a", "b", "c" 三列, 若是要刪除列 c ,如下過程描述如何作:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;[h1]
僅僅須要敲入帶有SQLite數據庫名字的"sqlite3"命令便可。若是文件不存在,則建立一個新的(數據庫)文件。而後 sqlite3程序將提示你輸入SQL。敲入SQL語句(以分號「;」結束),敲回車鍵以後,SQL語句就會執行。
例如,建立一個包含一個表"tb11"名字爲"ex1"的SQLite數據庫,你能夠這樣作:
數據庫、表的創建,記錄的添加、查詢、修改和刪除
F:\>sqlite3 database.db
sqlite> create table admin(username text,age integer);
sqlite> insert into admin values('kuang',25);
sqlite> select * from admin;
sqlite> update admin set username='kk',age=24 where username='kuang' and age=25;
sqlite> delete from admin where username='kk';
注:每條sql語句後必須以";"號結尾!
$sqlite3 ex1
SQLite version 3.3.17
Enter ".help" for instructions
sqlite> create table tbl1(one varchar(10), two smallint);
sqlite> insert into tbl1 values('hello!', 10);
sqlite> insert into tbl1 values('goodbye', 20);
sqlite> select * from tbl1;
hello!|10
goodbye|20
sqlite>
你能夠經過敲你所用系統的文件結束符(一般是Ctrl + D)或者中斷字符(一般是Ctrl + C)。來終止sqlite3程序。肯定你在每一個SQL語句結束敲入分號!sqlite3程序經過查找分號來決定一個SQL語句的結束。若是你省略分 號,sqlite3將給你一個連續的命令提示符並等你給當前的SQL命令添加更多的文字。這個特色讓你輸入多行的多個SQL語句,例如:
sqlite> create table tbl2(
...> f1 varchar(30) primary key,
...> f2 text,
...> f3 real
...> );
sqlite>
SQLite數據庫的框架被保存在一個名叫"sqlite_master"的特殊的表中。你能夠像查詢其它表同樣經過執行「SELECT」查詢這個特殊的表。例如:
$ sqlite3 ex1
SQlite vresion 3.3.10
Enter ".help" for instructions
sqlite> select * from sqlite_master;
type = table
name = tbl1
tbl_name = tbl1
rootpage = 3
sql = create table tbl1(one varchar(10), two smallint)
sqlite>
但你不能在sqlite_master表中執行諸如DROP TABLE, UPDATE, INSERT 或者DELETE命令。sqlite_master表在你建立、刪除和索引數據庫時自動更新這個表。你不能手工更改sqlite_master表。
TEMPORARY表的結構沒有存儲在"sqlite_master"表中,因爲TEMPORARY表對應用是不可見的,而不是應用程序建立這個表。 TEMPORARY表結構被存儲在另一個名叫"sqlite_temp_master"的特定的表中。"sqlite_temp_master"表是臨 時表自身。
大多數候,sqlite3讀入輸入行,並把它們傳遞到SQLite庫中去運行。可是若是輸入行以一個點(「.」)開始,那麼這行將被sqlite3程序自 己截取並解釋。這些「點命令」一般被用來改變查詢輸出的格式,或者執行鞭個預封包(預約義prepackaged)的查詢語句。
你能夠在任什麼時候候輸入「.help」,列出可用的點命令。例如
sqlite> .help
Stop after hitting an error. Default OFF(遇到錯誤時再也不繼續, 默認爲OFF)
List names and files of attached databases(列出附加到數據庫的數據庫和文件)
Dump the database in an SQL text format(保存表到SQL格式的文件中, 沒有指表名, 則保存全部. 若是要保存到磁盤上須要結合 .output 命令.)
Turn command echo on or off(打開/關閉 命令行回顯)
Exit this program(退出該命令行)
Turn output mode suitable for EXPLAIN on or off.( 以合適的方式顯示錶頭, 不帶參數則爲開啓)
Turn display of headers on or off(是否顯示錶頭, 和 .explain 差異不是很大)
Show this message(顯示幫助信息)
Import data from FILE into TABLE(從文件中導入表)
Show names of all indices on TABLE(顯示索引)
Load an extension library(加載一個擴展庫)
Set output mode where MODE is one of:
(設置輸出模式, 模式能夠是如下幾種):
csv Comma-separated values( 以逗號分隔的值)
column Left-aligned columns. (See .width)( 表頭左對齊(參見 .width))
html HTML <table> code(顯示 HTML 代碼)
insert SQL insert statements for TABLE( SQL插入語句)
line One value per line( 一行一個值)
list Values delimited by separator string(值用 string 分隔)
tabs Tab-separated values(以 tab 分隔的值)
tcl TCL list elements(TCL 列表元素)
Print STRING in place of NULL values(以 STRING 代替 NULL 值的輸出)
Send output to FILENAME(輸出到文件, 而不是顯示在屏幕上)
Send output to the screen(輸出到屏幕上)
Replace the standard prompts(替換默認的命令提示信息, 默認就是 sqlite>)
Exit this program(退出命令行)
Execute SQL in FILENAME(執行 FILENAME 中的 SQL語句)
Show the CREATE statements(顯示 CREATE 語句)
Change separator used by output mode and .import
Show the current values for various settings(顯示各類設置)
List names of tables matching a LIKE pattern(查看數據庫的表列表)
Try opening locked tables for MS milliseconds(在 MS 時間內嘗試打開被鎖定的表)
Set column widths for "column" mode(設置 column 模式中的列的寬度)
(顯示CPU時間)
(顯示 VFS 棧信息)
(從文件中還原數據到表, 默認表爲 main)
sqlite>
sqlite3程序能夠以八種不一樣的格式顯示一個查詢的結果:"csv", "列", "html", "插入", "行", "製表"和"tcl"。你能夠用".mode"點命令在這些輸出格式之間切換。
默認的輸出格式是「列表」。在列表模式下,每條查詢結果記錄被寫在一行中而且每列之間以一個字符串分割符隔開。默認的分隔符是一個管道符號(「|」)。列表符號在當你輸出查詢結果到另一個符加處理的程序(如AWK)中去是尤其有用。
sqlite> .mode list
sqlite> select * from tbl1;
hello|10
goodbye|20
sqlite>
你能夠用「.separator」點命令來改變分界符。例如,爲了把分割符改成一個逗號和一個空格,你能夠這樣作:
sqlite> .separator ", "
sqlite> select * from tbl1;
hello, 10
goodbye, 20
sqlite>
在「line"模式下,每個位於條記錄中的列在它本身那行顯示。每行由列名、一個等號和列數據組成。下一條記錄以一個空行隔開。這是一個行模式輸出的例子:
sqlite> .mode line
sqlite> select * from tbl1;
one = hello
two = 10
one = goodbye
two = 20
sqlite>
在列模式下,每條記錄在一個單獨的行中以數據列對齊的方式顯示。列如:
sqlite> .mode column
sqlite> select * from tbl1;
one two
---------- ----------
hello 10
goodbye 20
sqlite>
在默認的狀況下,每列至少10個字符寬。太寬(超過設定的寬度)的數據將被截取。你能夠用「.width」命令來調整列寬。以下所示:
sqlite> .width 12 6 (第一列12,第二列6)
sqlite> select * from tbl1;
one two
------------ ------
hello 10
goodbye 20
sqlite>
上面例子中".width"命令設置第一列寬爲12第二列寬爲6。其它的列寬不變。你能夠指定與你查詢結果須要的列數同樣多的「.width」參數。
若是你指定一列寬爲0,那麼這個列寬將自動如下面三個數字中的最大值作爲列寬:十、表頭寬度和最寬的數據列的寬度。這可讓列自動調整寬度。每列的默認設置爲自動調整的0值。
出如今輸出開頭兩行的列標示能夠用".header"點命令關閉。在上面的例子中,列標示是打開的。能夠用下面的方法關閉列標示:
sqlite> .header off
sqlite> select * from tbl1;
hello 10
goodbye 20
sqlite>
另一個有用的輸出模式是"insert"。在插入模式下,被子格式化爲看起來像SQL INSERT語句的樣式。你能夠用插入模式來產生文件(便於)之後用於不一樣數據庫的輸入。
當指定插入模式時,你必須給定一個特定參數就是要插入的表名。例如:
sqlite> .mode insert new_table
sqlite> select * from tbl1;
INSERT INTO 'new_table' VALUES('hello',10);
INSERT INTO 'new_table' VALUES('goodbye',20);
sqlite>
最新的輸出格式是「html」。在這種模式下,sqlite3把查詢的結果寫作XHTML表。開始的<TABLE>和結束 的</TABLE>(標記)沒有寫出,但有<TR>、<TH>和<TD>等分界符。html輸出對 CGI來講是至關有用地。
把結果寫到文件中
sqlite3程序提供幾個有用的用於查詢數據庫結構的快捷命令。這些不是不能夠用別的方式來實現。這些命令僅僅是一個快捷方式而已。
例如,爲了查看數據庫的表列表,你能夠敲入「.tables」。
sqlite> .tables
tbl1
tbl2
sqlite>
「.tables」命令類似於設置列表模式而後執行接下來的查詢:
SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'UNION ALL SELECT name FROM sqlite_temp_master WHERE type IN ('table','view') ORDER BY 1sqlite> .schema
create table tbl1(one varchar(10), two smallint)
CREATE TABLE tbl2 (
f1 varchar(30) primary key,
f2 text,
f3 real
)
sqlite> .schema tbl2
CREATE TABLE tbl2 (
f1 varchar(30) primary key,
f2 text,
f3 real
)
sqlite>
你能夠查看sqlite3的源代碼(能夠在源文件樹的src/shell.c中),你可找到上面的具體的查詢。
「.indices」 命令做用相似的方式是列出特定表的全部的索引。「.indics」命令須一個參數即所要索引表的表名。最後,但不是至少,是「.schema」命令。不帶 任何參數,「.schema」命令顯示原始的用於建立當前數據庫的CREATE TABLE和CREATE INDEX語句。若是你給".schema"命令一個表名,它顯示原始的建立該表和它全部索引的CREATE語句。咱們能夠:
".schema"命令能夠用設置列表而後執行如下查詢來實現:
SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master)WHERE type!='meta'ORDER BY tbl_name, type DESC, name
或者,若是你給".schema"命令一個參數,因爲你只想獲得一個表的結構,查詢能夠是這樣:
SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master)WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'ORDER BY substr(type,2,1), name
你能夠擔供一個參數給.schema命令。若是這橛,查詢能夠是這樣的:
SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master)WHERE tbl_name LIKE '%s' AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'ORDER BY substr(type,2,1), name
在查詢中「%S「爲你的參數所取代。這使你能夠詢數據庫結構的某個子集。
sqlite> .schema %abc%
與這些一塊兒,「.table」命令也接受一個模式做爲他的參數。若是你給「.table」一個參數,「%」將被先後擴展而且一個LIKE子句被附加到查詢上。這讓你能夠列出只與特定模式相匹配的的表。
「.databases」 命令顯示全部當前鏈接打開的數據庫的一個列表。將容許一次到少兩個。第一個是「main」,最初打開的那個數據庫。第二個是"temp",用於臨時表的數 據庫。對於用ATTACH語句附加的數據也許有附加數據庫列表。輸出的第一列與之相聯的數據庫名,第二列是外部文件名。
sqlite> .databases
".dump"命令成一個單一的ASCII文本文件。這個文件能夠被用做管道傳遞給sqlite3命令來轉換回數據庫。
一個最好的製做一個數據庫檔案拷貝的命令是:
$ echo '.dump' | sqlite3 ex1 | gzip -c >ex1.dump.gz
它產生一個一個名爲ex1.dump.gz的文件,它包含了你之後或在其它機器上重構數據庫的全部的信息。要重構數據庫,只須敲入:
$ zcat ex1.dump.gz | sqlite3 ex2
這個文本格式是純粹的SQL語句因此你能夠用.dump命令來導出一個SQLite數據庫到另外一個經常使用的SQL數據庫引擎。好比:
$ createdb ex2
$ sqlite3 ex1 .dump | psql ex2
數據庫結構導出和導入:
「.output」 默認狀況下,sqlte3把結送到標準輸出。你能夠用「.output」命令改變它。只須把輸出文件名作爲.output命令的輸出參數而後全部後續查詢結果將被寫到那個文件中。
用「.output stdout」再一次改成標準輸出。例如:
所有導出
$sqlite3 data.db
sqlite >.output dd
sqlite >.dump
sqlite > .exit
$cat dd //如下是數據庫的結構
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE long(h,m);
INSERT INTO "long" VALUES('hhhhhh',2);
INSERT INTO "long" VALUES('hello',2);
INSERT INTO "long" VALUES('mall',5);
INSERT INTO "long" VALUES('suinvzi',8);
INSERT INTO "long" VALUES('meimei',5);
CREATE TABLE bak(m varchar(10), n int);
INSERT INTO "bak" VALUES('hhhhhh',2);
INSERT INTO "bak" VALUES('hell',2);
INSERT INTO "bak" VALUES('mall',5);
INSERT INTO "bak" VALUES('suinvzi',8);
INSERT INTO "bak" VALUES('meimei',5);
COMMIT;
[h2] 導出表結構
sqlite >.output dd
sqlite >.schema
sqlite > .exit
$cat dd //如下是表的結構
CREATE TABLE bak(m varchar(10), n int);
CREATE TABLE long(h,m);
所有導入 : (用.read命令,執行 FILENAME 中的 SQL語句)
sqlite3 mydb.db
sqlite >.read dd
sqlite >.tables
bak long //導入的是空表bak,long
數據的導出和導入:
導出:例如:
$sqlite3 data.db
sqlite> .mode list
sqlite> .separator |
sqlite> .output test_file_1
sqlite> select * from long;
sqlite> .exit
$ cat test_file_1
hhhhhh|2
hello|2
mall|5
suinvzi|8
meimei|5
$
備註:每次導出若爲同一個文本,則正在導出的內容覆蓋上次導出的內容
導入: 例如:(用.import 導入的文本 被導入的表 命令, 把文件中的數據導入表中)
$sqlite3 mydb.db
sqlite> .show
echo: off
explain: off
headers: off
mode: list
nullvalue: ""
output: stdout
separator: "|"
stats: off
width:
sqlite> .import test_file_1 long
sqlite> select * from long;
hhhhhh|2
hello|2
mall|5
suinvzi|8
meimei|5
導入結束
備註:文本的內容所對應的數據分佈與所要導入的表一致,元素間的分割符要與」.show」中separator: "|"的字符一致若是不一致可直接修改, 好比: sqlite>.separator "," 將分隔符轉爲逗號,否則導入會出錯.
".explain" 命令能夠被用來設置輸出格式爲「column」 並設置列寬爲EXPLAIN命令看起來比較合理的寬度。EXPLAIN命令是SQLite特有的SQL擴展,它是對調試有用。若是任何常規的SQL被 EXPLAIN執行,那麼SQL命令被分解並分析但並不執行。取而代之的是,虛擬機指令序列將被用於執行SQL命令並返回一個相似的查詢結果。如:
sqlite> .explain
sqlite> explain delete from tbl1 where two<20;
addr opcode p1 p2 p3
---- ------------ ----- ----- -------------------------------------
0 ListOpen 0 0
1 Open 0 1 tbl1
2 Next 0 9
3 Field 0 1
4 Integer 20 0
5 Ge 0 2
6 Key 0 0
7 ListWrite 0 0
8 Goto 0 2
9 Noop 0 0
10 ListRewind 0 0
11 ListRead 0 14
12 Delete 0 0
13 Goto 0 11
14 ListClose 0 0
「.timeout」命令設置sqlite3等待一個試圖存儲文件鎖定請除直到錯誤返回的總時間。默認的超時值是0所以若是任何須要的數據庫表或序列列被鎖定時它將當即返回一個錯誤。
最後,咱們提到「.exit」命令它將致使sqlite3退出。
一 個在腳本命令中使用sqlite3的方式是用「echo」或「cat」來產生一個命令序列在一個文件中,而後當從一個產生的命令行中重定向輸入時調用 sqlite3。它有用而且適應許多環境。但做爲一附加的便利是,sqlite3容許一個單一的SQL語句在命令行中做爲數據庫名後的第二個參數輸入。當 sqlite3程序帶着兩個參數啓動時,第二個參數被傳遞給SQLite庫處理,查詢以列表模式打印到標準輸出,而後程序退出。這個機制被設計用於讓 sqlite3容易於用於鏈接諸如"AWK"的程序。例如:
$ sqlite3 ex1 'select * from tbl1' |
> awk '{printf "<tr><td>%s<td>%s\n",$1,$2 }'
<tr><td>hello<td>10
<tr><td>goodbye<td>20
$
SQLite 命令一般以一個分號結束。在一個命令行中你也能夠用「GO」單詞(大小寫敏感)或者一個「/」斜線在它所在好行結束一個命令。這常被SQL Server和Oracle使用。這些將不在sqlite3_exec()中有用,由於命令行在傳遞它們到函數以前把這些翻譯爲分號。
sqlite能夠在shell/dos command底下直接執行命令:
對數據庫進行SQL操做:
sqlite3數據庫 "SQL語句 或 .命令;"
輸出 HTML 表格:
sqlite3 -html film.db "select * from film;"
將數據庫「導出來」:
sqlite3 film.db ".dump" > output.sql
利用輸出的資料,創建一個如出一轍的數據庫(加上以上指令,就是標準的SQL數據庫備份了):
將數據庫「導進去」:
sqlite3 film.db < output.sql
在大量插入資料時,你可能會須要先打這個指令:
begin;
插入完資料後要記得打這個指令,資料纔會寫進數據庫中:
commit;
簡短回答:聲明爲 INTEGER PRIMARY KEY 的列將會自動增加 。
(若是是最大可能的主鍵 9223372036854775807,那個,將鍵值將是隨機未使用的數。)如,有下列表:
CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b INTEGER
);
在該表上,下列語句
INSERT INTO t1 VALUES(NULL,123);
在邏輯上等價於:
INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);
有一個新的API叫作 sqlite3_last_insert_rowid(), 它將返回最近插入的整數值。 注意該整數會比表中該列上的插入以前的最大值大1。 該鍵值在當前的表中是惟一的。但有可能與已從表中刪除的值重疊。要想創建在整個表的生命週期中惟一的鍵值,須要在 INTEGER PRIMARY KEY 上增長AUTOINCREMENT聲明。那麼,新的鍵值將會比該表中曾能存在過的最大值大1。若是最大可能的整數值在數據表中曾經存在過,INSERT將會失敗, 並返回SQLITE_FULL錯誤代碼。
多個進程可同時打開同一個數據庫。多個進程能夠同時進行SELECT 操做,但在任一時刻,只能有一個進程對數據庫進行更改。
SQLite使用讀、寫鎖控制對數據庫的訪問。(在Win95/98/ME等不支持讀、寫鎖的系統下,使用一個機率性的模擬來代替。)但使用時要注意:若是數據庫文件存放於一個NFS文件系統上,這種鎖機制可能不能正常工做。 這是由於 fcntl() 文件鎖在不少NFS上沒有正確的實現。 在可能有多個進程同時訪問數據庫的時候,應該避免將數據庫文件放到NFS上。在Windows上,Microsoft的文檔中說:若是使用 FAT 文件系統而沒有運行 share.exe 守護進程,那麼鎖多是不能正常使用的。那些在Windows上有不少經驗的人告訴我:對於網絡文件,文件鎖的實現有好多Bug,是靠不住的。若是他們說的是對的,那麼在兩臺或多臺Windows機器間共享數據庫可能會引發不指望的問題。
咱們意識到,沒有其它嵌入式的 SQL 數據庫引擎能象 SQLite 這樣處理如此多的併發。SQLite容許多個進程同時打開一個數據庫,同時讀一個數據庫。當有任何進程想要寫時,它必須在更新過程當中鎖住數據庫文件。但那一般只是幾毫秒的時間。其它進程只需等待寫進程幹完活結束。典型地,其它嵌入式的SQL數據庫引擎同時只容許一個進程鏈接到數據庫。
可是,Client/Server數據庫引擎(如 PostgreSQL, MySQL, 或 Oracle)一般支持更高級別的併發,而且容許多個進程同時寫同一個數據庫。這種機制在Client/Server結構的數據庫上是可能的,由於老是有一個單一的服務器進程很好地控制、協調對數據庫的訪問。若是你的應用程序須要不少的併發,那麼你應該考慮使用一個Client/Server 結構的數據庫。但經驗代表,不少應用程序須要的併發,每每比其設計者所想象的少得多。
當SQLite試圖訪問一個被其它進程鎖住的文件時,缺省的行爲是返回 SQLITE_BUSY。 能夠在C代碼中使用 sqlite3_busy_handler() 或 sqlite3_busy_timeout() API 函數調整這一行爲。
若是你運行 sqlite3 命令行來訪問你的數據庫,能夠鍵入 「.tables」來得到全部表的列表。或者,你能夠輸入 「.schema」 來看整個數據庫模式,包括全部的表的索引。輸入這些命令,後面跟一個LIKE模式匹配能夠限制顯示的表。
在一個 C/C++ 程序中(或者腳本語言使用 Tcl/Ruby/Perl/Python 等) 你能夠在一個特殊的名叫 SQLITE_MASTER 上執行一個SELECT查詢以得到全部 表的索引。每個 SQLite 數據庫都有一個叫 SQLITE_MASTER 的表, 它定義數據庫的模式。 SQLITE_MASTER 表看起來以下:
CREATE TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);
對於表來講,type 字段永遠是 'table',name 字段永遠是表的名字。因此,要得到數據庫中全部表的列表,使用下列SELECT語句:
SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;
對於索引,type 等於 'index', name 則是索引的名字,tbl_name 是該索引所屬的表的名字。無論是表仍是索引,sql 字段是原先用 CREATE TABLE 或 CREATE INDEX 語句建立它們時的命令文本。對於自動建立的索引(用來實現 PRIMARY KEY 或 UNIQUE 約束),sql字段爲NULL。
SQLITE_MASTER 表是隻讀的。不能對它使用 UPDATE、INSERT 或 DELETE。 它會被 CREATE TABLE、CREATE INDEX、DROP TABLE 和 DROP INDEX 命令自動更新。
臨時表不會出如今 SQLITE_MASTER 表中。臨時表及其索引和觸發器存放在另一個叫 SQLITE_TEMP_MASTER 的表中。SQLITE_TEMP_MASTER 跟 SQLITE_MASTER 差很少,但它只是對於建立那些臨時表的應用可見。若是要得到全部表的列表, 無論是永久的仍是臨時的,可使用相似下面的命令:
SELECT name FROM
(SELECT * FROM sqlite_master UNION ALL
SELECT * FROM sqlite_temp_master)
WHERE type='table'
ORDER BY name
SQLite 不強制 VARCHAR 的長度。 你能夠在 SQLITE 中聲明一個 VARCHAR(10),SQLite仍是能夠很高興地容許你放入500個字符。 而且這500個字符是原封不動的,它永遠不會被截斷。
SQLite 3.0 及之後版本容許你在任何列中存儲 BLOB 數據。即便該列被聲明爲其它類型也能夠。
在SQLite中,如何在一個表上添加或刪除一列?
SQLite 有有限地 ALTER TABLE 支持。你可使用它來在表的末尾增長一列,可更改表的名稱。若是須要對錶結構作更復雜的改變,則必須從新建表。重建時能夠先將已存在的數據放到一個臨時表中,刪除原表, 建立新表,而後將數據從臨時表中複製回來。
如,假設有一個 t1 表,其中有 "a", "b", "c" 三列, 若是要刪除列 c ,如下過程描述如何作:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;[h3]
不是。當你從SQLite數據庫中刪除數據時, 未用的磁盤空間將會加入一個內部的「自由列表」中。 當你下次插入數據時,這部分空間能夠重用。磁盤空間不會丟失,但也不會返還給操做系統。
若是刪除了大量數據,而又想縮小數據庫文件佔用的空間,執行 VACUUM 命令。 VACUUM 將會從頭從新組織數據庫。這將會使用數據庫有一個空的「自由鏈表」, 數據庫文件也會最小。但要注意的是,VACUUM 的執行會須要一些時間(在SQLite開發時,在Linux上,大約每M字節須要半秒種),而且, 執行過程當中須要原數據庫文件至多兩倍的臨時磁盤空間。
對於 SQLite 3.1版本,一個 auto-vacumm 模式能夠替代 VACUUM 命令。 可使用 auto_vacuum pragma 打開。
當一個準備好的(prepared)SQL語句再也不有效或者沒法執行時,將返回一個 SQLITE_SCHEMA 錯誤。發生該錯誤時,SQL語句必須使用 sqlite3_prepare() API來從新編譯. 在 SQLite 3 中, 一個 SQLITE_SCHEMA 錯誤只會發生在用 sqlite3_prepare()/sqlite3_step()/sqlite3_finalize() API 執行 SQL 時。而不會發生在使用 sqlite3_exec()時。 在版本2中不是這樣。
準備好的語句失效的最一般緣由是:在語句準備好後, 數據庫的模式又被修改了。另外的緣由會發生在:
數據庫離線:DETACHed.
數據庫被 VACUUMed
一個用戶存儲過程定義被刪除或改變。
一個 collation 序列定義被刪除或改變。
認證函數被改變。
在全部狀況下,解決方法是從新編譯並執行該SQL語句。 由於一個已準備好的語句能夠因爲其它進程改變數據庫模式而失效,全部使用 sqlite3_prepare()/sqlite3_step()/sqlite3_finalize() API 的代碼都應準備處理 SQLITE_SCHEMA 錯誤。下面給出一個例子:
int rc;
sqlite3_stmt *pStmt;
char zSql[] = "SELECT .....";
do {
/* Compile the statement from SQL. Assume success. */
sqlite3_prepare(pDb, zSql, -1, &pStmt, 0);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
/* Do something with the row of available data */
}
/* Finalize the statement. If an SQLITE_SCHEMA error has
** occured, then the above call to sqlite3_step() will have
** returned SQLITE_ERROR. sqlite3_finalize() will return
** SQLITE_SCHEMA. In this case the loop will execute again.
*/
rc = sqlite3_finalize(pStmt);
} while( rc==SQLITE_SCHEMA );
Insert into SAMPLE(PRJNUM, PRJNAME, EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE)
values(100001, 'TPMS', 200001, 'Johnson', 'A', 2000), (100001, 'TPMS', 200002,
'Christine', 'B', 3000), (100001, 'TPMS', 200003, 'Kevin', 'C', 4000), (100002,
'TCT', 200001, 'Johnson', 'A', 2000), (100002, 'TCT', 200004, 'Apple', 'B',
3000);
先打開數據庫test.db
sqlite>sqlite3 test.db
sqlite> .backup test.bak
先打開數據庫test.db
sqlite>sqlite3 test.db
sqlite> .restore test.bak
SQL能夠以下寫,供參考:
create table if not exists student(id integer primary key autoincrement,/
age smallint ,anchor smallint);
執行 select sqlite_version();命令便可
要使用索引對數據庫的數據操做進行優化,那必須明確幾個問題:
1.什麼是索引
2.索引的原理
3.索引的優缺點
4.何時須要使用索引,如何使用
圍繞這幾個問題,來探究索引在數據庫操做中所起到的做用。
回憶一下小時候查字典的步驟,索引和字典目錄的概念是一致的。字典目錄可讓咱們不用翻整本字典就找到咱們須要的內容頁數,而後翻到那一頁就能夠。索引也是同樣,索引是對記錄按照多個字段進行排序的一種展示。對錶中的某個字段創建索引會建立另外一種數據結構,其中保存着字段的值,每一個值還包括指向與它相關記錄的指針。這樣,就沒必要要查詢整個數據庫,天然提高了查詢效率。同時,索引的數據結構是通過排序的,於是能夠對其執行二分查找,那就更快了。
大多數的數據庫都是以B-樹或者B+樹做爲存儲結構的,B樹索引也是最多見的索引。先簡單介紹下B-樹,能夠加強對索引的理解。
B-樹是爲磁盤設計的一種多叉平衡樹,B樹的真正最準確的定義爲:一棵含有t(t>=2)個關鍵字的平衡多路查找樹。一棵M階的B樹知足如下條件:
1)每一個結點至多有M個孩子;
2)除根結點和葉結點外,其它每一個結點至少有M/2個孩子;
3)根結點至少有兩個孩子(除非該樹僅包含一個結點);
4)全部葉結點在同一層,葉結點不包含任何關鍵字信息,能夠看做一種外部節點;
5)有K個關鍵字的非葉結點剛好包含K+1個孩子;
B樹中的每一個結點根據實際狀況能夠包含大量的關鍵字信息和分支(固然是不能超過磁盤塊的大小,根據磁盤驅動(disk drives)的不一樣,通常塊的大小在1k~4k左右);這樣樹的深度下降了,這就意味着查找一個元素只要不多結點從外存磁盤中讀入內存,很快訪問到要查找的數據。B-樹上操做的時間一般由存取磁盤的時間和CPU計算時間這兩部分構成。而相對於磁盤的io速度,cpu的計算時間能夠忽略不計,因此B樹的意義就顯現出來了,樹的深度下降,而深度決定了io的讀寫次數。
B樹索引是一個典型的樹結構,其包含的組件主要是:
1)葉子節點(Leaf node):包含條目直接指向表裏的數據行。
2)分支節點(Branch node):包含的條目指向索引裏其餘的分支節點或者是葉子節點。
3) 根節點(Root node):一個B樹索引只有一個根節點,它實際就是位於樹的最頂端的分支節點。
以下圖所示:
每一個索引都包含兩部份內容,一部分是索引自己的值,第二部分即指向數據頁或者另外一個索引也的指針。每一個節點即爲一個索引頁,包含了多個索引。
當你爲一個空表創建一個索引,數據庫會分配一個空的索引頁,這個索引頁即表明根節點,在你插入數據以前,這個索引頁都是空的。每當你插入數據,數據庫就會在根節點建立索引條目,。當根節點插滿的時候,再插入數據時,根節點就會分裂。舉個例子,根節點插入瞭如圖所示的數據。(超過4個就分裂),這時候插入H,就會分裂成2個節點,移動G到新的根節點,把H和N放在新的右孩子節點中。如圖所示:
根節點插滿4個節點
插入H,進行分裂。
大體的分裂步驟以下:
1)建立兩個兒子節點
2)將原節點中的數據近似分爲兩半,寫入兩個新的孩子節點中。
3)在跟節點中放置指向頁節點的指針
當你不斷向表中插入數據,根節點中指向葉節點的指針也被插滿,當葉子還須要分裂的時候,根節點沒有空間再建立指向新的葉節點的指針。那麼數據庫就會建立分支節點。隨着葉子節點的分裂,根節點中的指針都指向了這些分支節點。隨着數據的不斷插入,索引會增長更多的分支節點,使樹結構變成這樣的一個多級結構。
1)彙集索引:表中行的物理順序與鍵值的邏輯(索引)順序相同。由於數據的物理順序只能有一種,因此一張表只能有一個彙集索引。若是一張表沒有彙集索引,那麼這張表就沒有順序的概念,全部的新行都會插入到表的末尾。對於彙集索引,葉節點即存儲了數據行,再也不有單獨的數據頁。就好比說我小時候查字典歷來不看目錄,我以爲字典自己就是一個目錄,好比查裴字,只須要翻到p字母開頭的,再按順序找到e。經過這個方法我每次都能最快的查到老師說的那個字,獲得老師的表揚。
2)非彙集索引:表中行的物理順序與索引順序無關。對於非彙集索引,葉節點存儲了索引字段值以及指向相應數據頁的指針。葉節點緊鄰在數據之上,對數據頁的每一行都有相應的索引行與之對應。有時候查字典,我並不知道這個字讀什麼,那我就不得不經過字典目錄的「部首」來查找了。這時候我會發現,目錄中的排序和實際正文的排序是不同的,這對我來講很苦惱,由於我不能比別人快了,我須要先再目錄中找到這個字,再根據頁數去找到正文中的字。
1)查詢。查詢操做就和查字典是同樣的。當咱們去查找指定記錄時,數據庫會先查找根節點,將待查數據與根節點的數據進行比較,再經過根節點的指針查詢下一個記錄,直到找到這個記錄。這是一個簡單的平衡樹的二分搜索的過程,我就不贅述了。在彙集索引中,找到頁節點即找到了數據行,而在非彙集索引中,咱們還須要再去讀取數據頁。
2)插入。彙集索引的插入操做比較複雜,最簡單的狀況,插入操做會找到對於的數據頁,而後爲新數據騰出空間,執行插入操做。若是該數據頁已經沒有空間,那就須要拆分數據頁,這是一個很是耗費資源的操做。對於僅有非彙集索引的表,插入只需在表的末尾插入便可。若是也包含了彙集索引,那麼也會執行彙集索引須要的插入操做。
3)刪除。刪除行後下方的數據會向上移動以填補空缺。若是刪除的數據是該數據頁的最後一行,那麼這個數據頁會被回收,它的先後一頁的指針會被改變,被回收的數據頁也會在特定的狀況被從新使用。與此同時,對於彙集索引,若是索引頁只剩一條記錄,那麼該記錄可能會移動到鄰近的索引表中,原來的索引頁也會被回收。而非彙集索引沒辦法作到這一點,這就會致使出現多個數據頁都只有少許數據的狀況。
其實經過前面的介紹,索引的優缺點已經一目瞭然。
先說優勢:
1)大大加快數據的檢索速度,這也是建立索引的最主要的緣由
2)加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。
3)在使用分組和排序子句進行數據檢索時,一樣能夠顯著減小查詢中分組和排序的時間。
再說缺點:
1)建立索引須要耗費必定的時間,可是問題不大,通常索引只要build一次
2)索引須要佔用物理空間,特別是彙集索引,須要較大的空間
3)當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,下降了數據的維護速度,這個是比較大的問題。
根據上文的分析,咱們大體對何時使用索引有了本身的想法(若是你沒有,回頭再看一遍。。。)。通常咱們須要在這些列上創建索引:
1)在常常須要搜索的列上,這是毋庸置疑的;
2)常常同時對多列進行查詢,且每列都含有重複值能夠創建組合索引,組合索引儘可能要使經常使用查詢造成索引覆蓋(查詢中包含的所需字段皆包含於一個索引中,咱們只須要搜索索引頁便可完成查詢)。 同時,該組合索引的前導列必定要是使用最頻繁的列。對於前導列的問題,在後面sqlite的索引使用介紹中還會作討論。
3)在常常用在鏈接的列上,這些列主要是一些外鍵,能夠加快鏈接的速度,鏈接條件要充分考慮帶有索引的表。;
4)在常常須要對範圍進行搜索的列上建立索引,由於索引已經排序,其指定的範圍是連續的,一樣,在常常須要排序的列上最好也建立索引。
6)在常常放到where子句中的列上面建立索引,加快條件的判斷速度。要注意的是where字句中對列的任何操做(如計算表達式,函數)都須要對錶進行整表搜索,而沒有使用該列的索引。因此查詢時儘可能把操做移到等號右邊。
對於如下的列咱們不該該建立索引:
1)不多在查詢中使用的列
2)含有不多非重複數據值的列,好比只有0,1,這時候掃描整表一般會更有效
3)對於定義爲TEXT,IMAGE的數據不該該建立索引。這些字段長度不固定,或許很長,或許爲空。
固然,對於更新操做遠大於查詢操做時,不創建索引。也能夠考慮在大規模的更新操做前drop索引,以後從新建立,不過這就須要把建立索引對資源的消耗考慮在內。總之,使用索引須要平衡投入與產出,找到一個產出最好的點。
1)Sqlite不支持彙集索引,android默認須要一個_id字段,這保證了你插入的數據會按「_id」的整數順序插入,這個integer類型的主鍵就會扮演和彙集索引同樣的角色。因此不要再在對於聲明爲:INTEGER PRIMARY KEY的主鍵上建立索引。
2)不少對索引不熟悉的朋友在表中建立了索引,卻發現沒有生效,其實這大多數和我接下來說的有關。對於where子句中出現的列要想索引生效,會有一些限制,這就和前導列有關。所謂前導列,就是在建立複合索引語句的第一列或者連續的多列。好比經過:CREATE INDEX comp_ind ON table1(x, y, z)建立索引,那麼x,xy,xyz都是前導列,而yz,y,z這樣的就不是。下面講的這些,對於其餘數據庫或許會有一些小的差異,這裏以sqlite爲標準。在where子句中,前導列必須使用等於或者in操做,最右邊的列可使用不等式,這樣索引才能夠徹底生效。同時,where子句中的列不須要全創建了索引,可是必須保證創建索引的列之間沒有間隙。舉幾個例子來看吧:
用以下語句建立索引:
CREATE INDEX idx_ex1 ON ex1(a,b,c,d,e,...,y,z);
這裏是一個查詢語句:
...WHERE a=5 AND b IN (1,2,3) AND c IS NULL AND d='hello'
這顯然對於abcd四列都是有效的,由於只有等於和in操做,而且是前導列。
再看一個查詢語句:
... WHERE a=5 AND b IN (1,2,3) AND c>12 AND d='hello'
那這裏只有a,b和c的索引會是有效的,d列的索引會失效,由於它在c列的右邊,而c列使用了不等式,根據使用不等式的限制,c列已經屬於最右邊。
最後再看一條:
... WHERE b IN (1,2,3) AND c NOT NULL AND d='hello'
索引將不會被使用,由於沒有使用前導列,這個查詢會是一個全表查詢。
3)對於between,or,like,都沒法使用索引。
如 ...WHERE myfield BETWEEN 10 and 20;
這時就應該將其轉換成:
...WHERE myfield >= 10 AND myfield <= 20;
再如LIKE:...mytable WHERE myfield LIKE 'sql%';;
此時應該將它轉換成:
...WHERE myfield >= 'sql' AND myfield < 'sqm';
再如OR:...WHERE myfield = 'abc' OR myfield = 'xyz';
此時應該將它轉換成:
...WHERE myfield IN ('abc', 'xyz');
其實除了索引,對查詢性能的影響因素還有不少,好比表的鏈接,是否排序等。影響數據庫操做的總體性能就須要考慮更多因素,使用更對的技巧,不得不說這是一個很大的學問。
最後在android上使用sqlite寫一個簡單的例子,看下索引對數據庫操做的影響。
建立以下表和索引:
db.execSQL("create table if not exists t1(a,b)");
db.execSQL("create index if not exists ia on t1(a,b)");
插入10萬條數據,分別對錶進行以下操做:
select * from t1 where a='90012'
插入:insert into t1(a,b) values('10008','name1.6982235534984673')
更新:update t1 set b='name1.999999' where a = '887'
刪除:delete from t1 where a = '1010'
數據以下(5次不一樣的操做取平均值):
操做 無索引 有索引
查詢 170ms 5ms
插入 65ms 75ms
更新 240ms 52ms
刪除 234ms 78ms
能夠看到顯著提高了查詢的速度,稍稍減慢了插入速度,還稍稍提高了更新數據和刪除數據的速度。若是把更新和刪除中的where子句中的列換成b,速度就和沒有索引同樣了,由於索引失效。因此索引能大幅度提高查詢速度,對於刪除和更新操做,若是where子句中的列使用了索引,即便須要從新build索引,有可能速度仍是比不使用索引要快的。對與插入操做,索引顯然是個負擔。同時,索引讓db的大小增長了2倍多。
還有個要吐槽的是,android中的rawQurey方法,執行完sql語句後返回一個cursor,其實並無完成一個查詢操做,我在rawquery以前和以後計算查詢時間,永遠是1ms...這讓我無比苦悶。看了下源碼,在對cursor調用moveToNext這些移動遊標方法時,都會最終先調用getCount方法,而getCount方法纔會調用native方法調用真正的查詢操做。這種設計顯然更加合理