「值」是一個數字、字串、BLOB 或 NULL。有時會使用限定名稱「純量值」來強調只涉及單一數量。
「列值」是有序的兩個或多個純量值清單。換句話說,「列值」是一個向量或元組。
列值的「大小」是列值包含的純量值數目。列值的大小永遠至少為 2。單一欄位的列值只是一個純量值。沒有欄位的列值是語法錯誤。
SQLite 允許以兩種方式表示列值
SQLite 可以使用列值在兩個內容中
列值的語法以及可以使用列值的狀況說明如下。
比較兩個列值時,會由左至右查看組成元素值。NULL 表示「未知」。如果透過將元素 NULL 替換為其他值,可以讓結果為真或假,則比較結果為 NULL。下列查詢示範一些列值比較
SELECT (1,2,3) = (1,2,3), -- 1 (1,2,3) = (1,NULL,3), -- NULL (1,2,3) = (1,NULL,4), -- 0 (1,2,3) < (2,3,4), -- 1 (1,2,3) < (1,2,4), -- 1 (1,2,3) < (1,3,NULL), -- 1 (1,2,3) < (1,2,NULL), -- NULL (1,3,5) < (1,2,NULL), -- 0 (1,2,NULL) IS (1,2,NULL); -- 1
「(1,2,3)=(1,NULL,3)」的結果為 NULL,因為如果將 NULL→2 替換,結果會為真;如果將 NULL→9 替換,結果會為假。而「(1,2,3)=(1,NULL,4)」的結果並非 NULL,因為沒有任何元素 NULL 的替換值會讓此表達式為真,因為第三欄的 3 永遠不會等於 4。
前一個範例中的任何列值都可以替換為傳回三欄的子查詢,結果會相同。例如
CREATE TABLE t1(a,b,c); INSERT INTO t1(a,b,c) VALUES(1,2,3); SELECT (1,2,3)=(SELECT * FROM t1); -- 1
對於列值 IN 運算子,左手邊 (以下簡稱「LHS」) 可以是括號括起的數值清單或多欄子查詢。但是,右手邊 (以下簡稱「RHS」) 必須是子查詢表達式。
CREATE TABLE t2(x,y,z); INSERT INTO t2(x,y,z) VALUES(1,2,3),(2,3,4),(1,NULL,5); SELECT (1,2,3) IN (SELECT * FROM t2), -- 1 (7,8,9) IN (SELECT * FROM t2), -- 0 (1,3,5) IN (SELECT * FROM t2); -- NULL
列值也可以用於 UPDATE 陳述式的 SET 子句中。LHS 必須是欄位名稱清單。RHS 可以是任何列值。例如
UPDATE tab3 SET (a,b,c) = (SELECT x,y,z FROM tab4 WHERE tab4.w=tab3.d) WHERE tab3.e BETWEEN 55 AND 66;
假設應用程式想要以捲動視窗顯示聯絡人清單,一次只能顯示 7 筆聯絡人,且聯絡人依據姓氏、名字順序排列。初始化捲動視窗至前 7 筆資料很簡單
SELECT * FROM contacts ORDER BY lastname, firstname LIMIT 7;
當使用者向下捲動時,應用程式需要找出第二組 7 筆資料。執行此項操作的方法之一是使用 OFFSET 子句
SELECT * FROM contacts ORDER BY lastname, firstname LIMIT 7 OFFSET 7;
OFFSET 會提供正確答案。然而,OFFSET 所需時間與偏移值成正比。使用「LIMIT x OFFSET y」時實際發生的情況是,SQLite 會將查詢計算為「LIMIT x+y」,並捨棄前 y 個值,而不會將它們傳回應用程式。因此,當視窗向下捲動至長清單的底部,且 y 值變得越來越大的時候,後續的偏移計算會花費越來越多的時間。
更有效率的方法是記住目前顯示的最後一筆資料,然後在 WHERE 子句中使用列值比較
SELECT * FROM contacts WHERE (lastname,firstname) > (?1,?2) ORDER BY lastname, firstname LIMIT 7;
如果前一個畫面底部的姓氏和名字繫結到 ?1 和 ?2,則上述查詢會計算下一個 7 列。而且,假設有適當的索引,它會非常有效率地執行此項操作,比 OFFSET 有效率許多。
在資料庫表格中儲存日期的常見方式是將其儲存在單一欄位中,可能是 Unix 時間戳記、儒略日數或 ISO-8601 日期字串。但有些應用程式會將日期儲存在三個個別欄位中,分別代表年、月和日。
CREATE TABLE info( year INT, -- 4 digit year month INT, -- 1 through 12 day INT, -- 1 through 31 other_stuff BLOB -- blah blah blah );
當日期以這種方式儲存時,列值比較提供了比較日期的便利方式
SELECT * FROM info WHERE (year,month,day) BETWEEN (2015,9,12) AND (2016,9,12);
假設我們想要知道任何項目中的訂單編號、產品編號和數量,其中產品編號和數量與訂單編號 365 中的任何項目的產品編號和數量相符
SELECT ordid, prodid, qty FROM item WHERE (prodid, qty) IN (SELECT prodid, qty FROM item WHERE ordid = 365);
上面的查詢可以改寫為聯結,而不使用列值
SELECT t1.ordid, t1.prodid, t1.qty FROM item AS t1, item AS t2 WHERE t1.prodid=t2.prodid AND t1.qty=t2.qty AND t2.ordid=365;
由於相同的查詢可以改寫而不使用列值,因此列值不會提供新的功能。然而,許多開發人員表示列值格式較易於閱讀、撰寫和除錯。
即使在 JOIN 表單中,也可以透過使用列值讓查詢更清楚
SELECT t1.ordid, t1.prodid, t1.qty FROM item AS t1, item AS t2 WHERE (t1.prodid,t1.qty) = (t2.prodid,t2.qty) AND t2.ordid=365;
這個後續查詢會產生與前一個標量公式完全相同的 位元組碼,但使用較簡潔且易於閱讀的語法。
列值符號對於根據單一查詢的結果更新資料表的兩個或多個欄位很有用。其中一個範例是在 Fossil 版本控制系統 的全文搜尋功能中。
在 Fossil 全文搜尋系統中,參與全文搜尋的文件(Wiki 頁面、票證、簽入、文件檔等)會由稱為「ftsdocs」的資料表(full text search documents)追蹤。當新的文件新增至儲存庫時,並不會立即建立索引。索引會延後到有搜尋要求時才建立。ftsdocs 資料表包含一個「idxed」欄位,如果文件已建立索引,則為 true,否則為 false。
當搜尋要求發生且待處理文件第一次建立索引時,必須透過將 idxed 欄位設定為 true 並且填入數個其他與搜尋相關的資訊欄位,來更新 ftsdocs 資料表。其他資訊會從聯結中取得。查詢如下
UPDATE ftsdocs SET idxed=1, name=NULL, (label,url,mtime) = (SELECT printf('Check-in [%%.16s] on %%s',blob.uuid, datetime(event.mtime)), printf('/timeline?y=ci&c=%%.20s',blob.uuid), event.mtime FROM event, blob WHERE event.objid=ftsdocs.rid AND blob.rid=ftsdocs.rid) WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed
(請參閱 原始碼 以進一步了解。其他範例 在此 和 在此。)
ftsdocs 表格中的九個欄位有五個已更新。兩個已修改的欄位「idxed」和「name」,可以獨立於查詢更新。但是三個欄位「label」、「url」和「mtime」都需要針對「event」和「blob」表格進行連接查詢。如果沒有列值,等效的 UPDATE 將需要重複連接三次,每次針對一個要更新的欄位。
有時使用列值可以讓 SQL 更容易閱讀和撰寫。考慮以下兩個 UPDATE 陳述式
UPDATE tab1 SET (a,b)=(b,a); UPDATE tab1 SET a=b, b=a;
兩個 UPDATE 陳述式執行完全相同的功能。(它們產生相同的 位元組碼。)但是第一個表格,列值表格,似乎更清楚地說明陳述式的目的是要交換欄位 A 和 B 中的值。
或者考慮這些相同的查詢
SELECT * FROM tab1 WHERE a=?1 AND b=?2; SELECT * FROM tab1 WHERE (a,b)=(?1,?2);
同樣地,SQL 陳述式產生相同的位元組碼,因此完全相同的方式執行完全相同的工作。但是第二個表格讓人類更容易閱讀,方法是將查詢參數分組到單一列值,而不是將它們分散在 WHERE 子句中。
列值已新增至 SQLite 版本 3.15.0(2016-10-14)。嘗試在先前版本的 SQLite 中使用列值會產生語法錯誤。