SQLite 推新版本~修復資料庫損壞漏洞以及漂亮呈現的QRF(Query Result Formatter)

SQLite 是全世界應用最廣泛的嵌入式資料庫引擎,從手機 App、瀏覽器到物聯網裝置都能看到它的身影。2026 年 7 月 24 日發布的 3.53.4 版本,除了修補前幾個小版本(3.53.1~3.53.3)遺留的問題外,也整合了自 3.53.0 以來累積的多項重要功能。以下用淺顯的方式,把這次更新拆解成幾個重點面向來介紹。

一、最值得關心的修復:資料庫損壞漏洞

這次更新最關鍵的一項,是修復了與 WAL(Write-Ahead Log,預寫式日誌)重置有關的資料庫損壞問題。WAL 是 SQLite 用來提升寫入效能與並行處理能力的機制,一旦這個環節出錯,最壞情況可能導致資料庫檔案損毀。對一般使用者來說,這代表:只要有使用資料庫寫入操作的應用程式,都建議儘快升級到這個版本,以避免潛在的資料遺失風險。

二、新增QRF(Query Result Formatter)

QRF(Query Result Formatter)是這次版本很有特色的新功能,簡單來說,它能把 SQL 查詢結果自動排版成整齊的表格,方便在文字介面(例如命令列視窗)中閱讀,例如數字會自動靠右對齊、輸出以框線字元組成表格。這對於習慣用命令列工具查資料庫的工程師或維運人員來說,等於是「不用額外工具,資料庫自己就把報表排版好了」。目前互動式命令列(CLI)已預設採用這種新格式,而批次執行(例如寫腳本自動跑 SQL)則仍維持舊格式,避免既有自動化流程被打亂。

三、SQL 語法更靈活

這次也強化了 SQL 語言本身的能力,重點包括:

  • ALTER TABLE 更彈性:現在可以直接新增或移除欄位的 NOT NULL、CHECK 限制條件,不需要像以前一樣重建整張表。
  • REINDEX EXPRESSIONS:可以單獨重建「表達式索引」(例如根據函數運算結果建立的索引),用來修復索引過期失效的問題。
  • VACUUM INTO 加強:備份資料庫時可以透過 URI 參數指定保留空間大小,方便預留未來成長空間。
  • 新增了 json_array_insert()jsonb_array_insert() 兩個 JSON 處理函數,讓操作 JSON 陣列更方便。

四、查詢效能與規劃器的優化

SQLite 的「查詢規劃器」負責決定一條 SQL 該怎麼執行最有效率,這次做了幾項調整:對於 EXCEPT、INTERSECT、UNION 這類集合運算,一律改用排序合併演算法,官方說明這樣「幾乎總是」比雜湊表快;另外也改善了多表格連接(JOIN)時的順序選擇邏輯,減少不必要的運算。對開發者而言,這些改動通常不需要修改任何程式碼,資料庫會自動跑得更快。

五、浮點數精度改變 — 需要留意的相容性議題

比較值得注意的是,浮點數與文字互相轉換的精度,預設從過去的 15 位有效數字,提升到 17 位。這代表數字顯示可能比以前更精確,但也可能讓某些依賴舊有顯示格式的程式或測試出現差異。如果需要維持舊行為,可以透過 SQLITE_DBCONFIG_FP_DIGITS 設定調整回來。這是一個「預設值改變」型的更新,建議有大量數值運算的系統在升級前先做測試。

六、命令列工具(CLI)多項細節優化

命令列工具本身也有不少貼心的小改進,例如 .timer 指令可以設定只計時下一句 SQL、.progress 可以設定逾時自動中斷查詢、.indexes 指令的搜尋邏輯改為比對索引名稱本身(而非資料表名稱),讓搜尋功能真正符合預期。另外要特別提醒:點指令結尾若有沒加引號的分號,現在會被直接忽略,這是一項可能造成相容性問題的改變,若有腳本依賴舊行為,需要特別檢查。

七、其他技術亮點

新增了多個 C 語言介面函數,方便進階開發者操作字串緩衝區與虛擬表;Session 擴充功能新增了逐項新增變更的介面,方便做資料同步;JavaScript/WASM 版本則新增了 opfs-wl 檔案系統選項,透過瀏覽器的 Web Locks 機制達到更公平的鎖定共享,不過需要較新版本的瀏覽器支援。

重點總結

如果只想記住三件事:第一,這個版本修了一個可能造成資料庫損壞的漏洞,建議儘快升級;第二,新增的 QRF 讓命令列查詢結果變得更好讀;第三,浮點數精度與分號規則有變動,升級前建議簡單測試一下既有系統是否受影響。

需要特別說明的是,官方頁面提到 3.53.4 這個修補版本,主要是為了修正前幾個版本(3.53.1 至 3.53.3)中「主要由人工智慧引起的問題」,詳細細節官方僅指向提交紀錄,並未在頁面上進一步展開說明。

MySQL監控常用語法

1 mysql連線數

一、取得mysql最大連線數

show status like ‘Max_used_connections’

二、取得mysql目前開啟的連線數

show status like ‘Threads_connected’

2 mysql 暫存

show status like ‘Innodb_buffer_pool_reads’

show status like ‘Innodb_buffer_pool_read_requests’

show status like ‘Innodb_buffer_pool_pages_total’

show status like ‘Innodb_buffer_pool_pages_free’

(1-Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests)*100%

((Innodb_buffer_pool_pages_total-Innodb_buffer_pool_pages_free)/Innodb_buffer_pool_pages_total)*100%

3 鎖

show status like ‘Innodb_row_lock_waits’

show status like ‘Innodb_row_lock_time_avg’

show open TABLES where in_use>0;

4 sql語法

一、慢查詢mysql 語法開關是否打開

show variables like ‘slow_query_log’;

二、慢查詢mysql語法打開

set global slow_query_log=1

三、設定慢sql的時間(秒)

set long_query_time=1

三、慢sql路徑

show variables like ‘slow_query_log_file’;

四、透過慢sql分析工具格式化記錄

mysqldumpslow -s at -t 10 /export/data/mysql/log/slow.log

四.1、取出使用最多的10條慢sql

./mysqldumpslow -s c -t 10 /export/data/mysql/log/slow.log

四.2、取出查詢時間最慢的3條sql

./mysqldumpslow -s t -t 3 /export/data/mysql/log/slow.log

幾個會導致sql變慢的原因

1 子查詢

SELECT FROM t1 WHERE id (SELECT id FROM t2 WHERE name=’hechunyang’);

改用 join ,但update/delete沒作用

2 避免用函數

SELECT FROM t WHERE YEAR(d) >= 2016;

改用 SELECT FROM t WHERE d >= ‘2016-01-01’;

3 用in 取代 or

SELECT FROM t WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30;

改用  SELECT FROM t WHERE LOC_IN IN (10,20,30);

4 雙%號無法用到索引

SELECT FROM t WHERE name LIKE ‘%de%’;

改為 SELECT FROM t WHERE name LIKE ‘de%’;

5 分組統計可以禁止排序

SELECT goods_id,count() FROM t GROUP BY goods_id;

改為  SELECT goods_id,count () FROM t GROUP BY goods_id ORDER BY NULL;

6不要用無意義的order by

SELECT count(1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id WHERE 1 = 1 ORDER BY u.create_time DESC;

改為

SELECT count (1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id;

 

Delphi 仿DBNavigator方式

Delphi有很好用的DBNavitor元件,美中不足的是按鈕的樣式稍嫌不足!那是否可以仿DBNavigator的方式?
筆者提供一個簡單的範例給大家 🙂
delphi_simulate_dbnavigator
一、自訂首筆/上筆/下筆/末筆
[pascal]
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
BtnFirst.Enabled := not Datasource1.DataSet.Bof;
BtnPrior.Enabled := not Datasource1.DataSet.Bof;
BtnNext.Enabled := not Datasource1.DataSet.eof;
BtnLast.Enabled := not Datasource1.DataSet.eof;
if TDataSource(Sender).State in [dsEdit,dsInsert] then begin
BtnFirst.Enabled := False;
BtnPrior.Enabled := False;
BtnNext.Enabled := False;
BtnLast.Enabled := False;
end;
end;
[/pascal]
二、自訂新增/修改/刪除/取消/存檔
[pascal]
procedure TForm1.DataSource1StateChange(Sender: TObject);
begin
BtnAdd.Enabled := TDataSource(Sender).State in [dsbrowse];
BtnEdit.Enabled := TDataSource(Sender).State in [dsbrowse];
BtnDelete.Enabled := TDataSource(Sender).State in [dsBrowse];
BtnAbort.Enabled := TDataSource(Sender).State in [dsEdit,dsInsert];
BtnSave.Enabled := TDataSource(Sender).State in [dsEdit,dsInsert];
end;
[/pascal]
三、讓button按下後與DBNavigator一樣
[pascal]
procedure TForm1.BtnAbortClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbcancel);
end;

procedure TForm1.BtnAddClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbinsert);
end;

procedure TForm1.BtnDeleteClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbdelete);
end;

procedure TForm1.BtnEditClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbedit);
end;

procedure TForm1.BtnFirstClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbfirst);
end;

procedure TForm1.BtnLastClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nblast);
end;

procedure TForm1.BtnNextClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbnext);
end;

procedure TForm1.BtnPriorClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbprior);
end;

procedure TForm1.BtnSaveClick(Sender: TObject);
begin
dbnavigator1.BtnClick(nbpost);
end;
[/pascal]

Delphi BDE逐漸走入歷史

Delphi XE7版本release之後,發現一直跟隨Delphi很久的BDE(Borland Database Engine)並沒有隨著版本發佈。
而是需要註冊使用者才能自行下載,BDE是Borland在16位元環境下一直叱吒資料庫開發界的利器,不過隨著Embarcadero將Delphi推至行動開發,更不斷精進新版本的dbExpress以及FireDAC技術。
看來應該是Embarcadero準備放手BDE的時候了!

相關連結:
ID: 29997, BDE Installer for RAD Studio, Delphi, C++Builder XE7
http://cc.embarcadero.com/item/29997

Delphi透過MySQL存入檔案備份

MySQL存Blog的大小
TINYBLOB – 255 bytes
BLOB – 65535 bytes
MEDIUMBLOB – 16,777,215 bytes (2^24 – 1)
LONGBLOB – 4G bytes (2^32 – 1)

透過資料庫存取的方式重點:

TBlobField – LoadFromStream、SaveToStream
Tmemorystream – LoadFromFile、SaveToFile(.Position :=0)

存入資料庫

[pascal]
var
astream : Tmemorystream;
begin
AStream := TMemoryStream.Create;
try
astream.LoadFromFile(filename);
AStream.Position := 0;
if adotable1.Active then
begin
adotable1.Edit;
TBlobField(adotable1.FieldByName(‘t2’)).LoadFromStream(AStream);
adotable1.Post;
end;
finally
AStream.free;
end;
end;
[/pascal]

存回檔案

[pascal]
var
Ms:Tmemorystream;
begin
Ms := Tmemorystream.Create;
try
if adotable1.Active then
begin
TBlobField(adotable1.FieldByName(‘t2’)).SaveToStream(Ms);
Ms.Position := 0;
MS.SaveToFile(sname);
end;
finally
ms.Free;
end;
end;
[/pascal]

MySQL預設的BLOB為1M
別忘了 My.ini調整
max_allowed_packet = XXM

[Delphi] DBGrid顯示不同字型、顏色

dbgrid選擇顏色字型的方式
1.在DBGrid的Columns Editor中,將要出現按鈕的欄位的ButtonStyle設為cbsEllipsis
2.接著到OnEditButtonClick中增加程式判斷
if dbgrid1.SelectedField = xxxx(欄位名稱) then begin
end;
3.顯示的方式則是在OnDrawColumnCell中增加程式
IF COLUMN.FieldName = ‘xxxx(欄位名稱)’ THEN BEGIN
DBGrid1.canvas.brush.Color := STRINGTOCOLOR(‘clGreen’);
END;
// 記得要加上
DbGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, []);

Delphi-將POS系統的Interbase匯到MySQL資料庫

前一陣子研究各家的餐飲、簡餐POS系統,意外的發現不少系統都是用Delphi寫的,資料庫也大多使用Interbase或是Firebird。

為了方便研究,就嘗試將資料庫轉到MySQL上去~記錄一下轉移方式。

工具:

做法:

在MySQL建立空白資料庫(utf8)

4

 

資料來源 (ODBC)新增 MySQL ODBC 3.51 Driver,注意一點要記得打開Details,將 Character Set 選成 Big5(不是utf8喔)

5

SQL Explorer→New Database→Database Driver Name選INTRBASE,SERVER NAME選擇您要連結的GDB檔案

8

6

7

打開Datapump,Source Alias選擇在SQL Explorer建立的名稱,User Name是SYSDBA(注意大小寫),password是 masterkey(注意大小寫)。接著Target Alias選擇MySQL ODBC建立的名稱。

11

10

9

12

13

 

 

 

14