跳轉到內容

從 Zip/SQLite 開始程式設計 Gambas

來自 Wikibooks,開放世界中的開放書籍

SQLite 資料庫

[編輯 | 編輯原始碼]

SQLite Logo

資料庫是硬碟上具有結構的檔案,因此它可以儲存大量資訊並快速訪問它。

SQLite 是一種型別的資料庫。它是由 Dwayne Richard Hipp(1961 年出生於北卡羅來納州)編寫的。它於 2000 年 8 月首次釋出。它是公共領域的,這意味著任何人都可以免費使用它。Google Chrome、Firefox、智慧手機的 Android 作業系統、Skype、Adobe Reader 和 iPhone 都使用 SQLite。它就是很好。您應該把它讀作“S Q L Lite”,正如維基百科所說。

資料庫將資訊儲存在表中。Gambas 有一個表格檢視。它也有行和列。您可以將資料庫表視為資料庫檔案中的一個不可見的表格檢視。

  • 行稱為記錄。列稱為欄位

例如,一位教師可能有一個包含“學生”表的資料庫。在該表中,每位學生都有一行。檢視整行,您將看到學生 ID、姓氏、名字、性別、出生日期、地址、電話號碼。這些是欄位。它們是列。

學生 ID 姓氏 名字 性別 出生日期 地址 電話號碼
2019001 Mary Smith F 2008-06-23 21 Holly Crt, Bundaberg 07324657
2019002 Jim Jones M 2003-02-19 14 Primrose St, Bundaberg 07123456
2019003 Lucy Watkins F 2003-10-05 5 Flower St, Bundaberg 07938276

這可能是一個表格檢視或資料庫檔案中的一個表。

每個資料庫表都必須有一個主鍵。每條記錄都必須為此欄位具有唯一的 value:一個其他人都不共享的 value。最簡單的辦法是稱之為RecID 並編號為 1、2、3... 等。在上表中,主鍵將是學生 ID,它是一個整數。前四位數字是入學年份。(我們可能會有另一個名為入學年份的列,並且只為學生 ID 使用順序號。)

在 SQLite 中,所有資料都以字串形式儲存,即使您可能將某些列指定為整數、其他列指定為字串,而其他列指定為日期。SQLite 非常寬容:您可以將非數字內容放入整數列等等,但儘量不要這樣做。空單元格為 NULL。也要儘量避免它們。當您建立一個新的空白記錄時,將 values 初始化為空字串“”。

包含資料庫功能

[編輯 | 編輯原始碼]

Including the database component in Gambas

SQLite 是 Gambas 的一個元件(可選部分)。還有一個數據庫訪問元件。在專案選單 > 屬性... > 元件頁面上,確保選中gb.dbgb.db.sqlite3。如果您的專案中沒有這些元件,您將在嘗試執行程式時立即收到錯誤。

SQL - 結構化查詢語言

[編輯 | 編輯原始碼]

您向SQLite 傳送訊息,它使用一種稱為SQL(“S Q L”或“sequel”,無論哪種讀法都可以)的特殊語言將答案發送回您。這意味著學習另一種語言,但最常用的簡單語句並不難學。無論如何,這些是我知道的唯一語句。SQL 由 Donald D. Chamberlin 和 Raymond F. Boyce 發明,並於 1974 年首次出現。SQL 是如此普遍,以至於所有編寫資料庫的人都知道它。它是一個國際標準。SQLite 是它的一個實現。

例如,您可能會向SQLite 傳送一條訊息,內容為

SELECT * FROM Students

這表示“從‘學生’表中選擇所有內容”。這將為您提供整個表。或者您可能只想檢視男性學生

SELECT * FROM Students WHERE Sex = 'M'

也許您想要所有人,但您希望女性排在前面,男性排在後面

SELECT * FROM Students ORDER BY Sex

這將首先獲取女性,因為“F”位於“M”之前。所有女性將以隨機順序排列,男性也是如此,除非您寫入

SELECT * FROM Students ORDER BY Sex DESC, LastName ASCSELECT * FROM Students ORDER BY Sex DESC, LastName ASC

這將返回一個表,其中男性排在前面(按姓氏字母順序排列),然後是女性(按姓氏字母順序排列)。您可能只想檢視學生姓名,因此您可以寫入

SELECT FirstName, LastName FROM Students ORDER BY LastName

也許您只想檢視 2019 年入學的學生。現在,這是學生 ID 的一部分。您只想檢視學生 ID 號以“2019”開頭的學生。您可以使用“萬用字元”。百分號 (%) 表示“這裡可以是任何內容”。

SELECT FirstName, LastName FROM Students WHERE StudentID LIKE '2019%'

當您將這些SELECT 語句傳送到資料庫時,SQLite 將向您傳送回一個表。Gambas 稱之為RESULT。假設您有一個名為db1 的資料庫(就 Gambas 而言),它連線到您的硬碟上的實際資料庫檔案MyStudentDatabase.sqlite。您需要一個結果來儲存回覆

Dim res as Result
res = db1.exec("SELECT * FROM Students")

res 包含您請求的資訊。您可能希望列印這些資訊,或在表格檢視中顯示它們,或者在陣列中內部儲存它們以便對其進行計算。您需要迴圈遍歷這些記錄,因此

While res.Available
 'do something with res!FirstName, res!LastName and res!DateOfBirth etc
 res.MoveNext
Wend

對於在表格檢視中顯示資訊,有一個特殊的事件會在每次單元格必須在其內容繪製到螢幕上時觸發。如果您的記錄集很大,這將特別有用。表格檢視不需要在自身中包含所有記錄的所有資訊。它可以在需要顯示時獲取資訊。這裡需要注意的是:如果您依賴於表格檢視中的所有資訊,那麼它可能存在或可能不存在。這是一個使用_Data 事件的示例,在需要顯示錶格檢視中的特定單元格時,從結果表 res 中獲取資訊

Public Sub TableView1_Data(Row As Integer, Column As Integer)
  res.MoveTo(row)
  If Column = 0 Then
        TableView1.Data.Text = res!FirstName
  Else
        TableView1.Data.Text = res!LastName
  Endif
End

請注意,TableView1.Data.Text 表示單元格中的文字。

請注意,我們使用result.MoveTo 轉到特定記錄,使用result.MoveNext 一次遍歷一個記錄,並使用result.Available 檢查是否還有另一個記錄要移到。在設定表格檢視中要包含的行數時,result.RecordCount 很有用。

除了訪問資料庫中的資訊之外,使用資料庫時,您還需要能夠

  1. 新增記錄
  2. 刪除記錄
  3. 修改記錄

除了最簡單的資料庫之外,所有資料庫都包含多個表。表可以相互連結,因此記錄可以包含指向其他表中適用的行的指路牌。指路牌是另一個表中記錄的記錄 ID 或其他主鍵。例如,政治候選人的資料庫可能包含指向其所屬政黨的指路牌。SQL 非常聰明,它可以同時檢視這兩個表,以為您提供您需要的資訊,例如這兩個表的“連線”。(候選人屬於特定政黨,而政黨對各種問題制定了政策。)

SELECT Candidate,PolicyOnPensions FROM Candidates,Parties WHERE Candidate.PartyID = Parties.PartyID AND Candidates.Electorate="Fairfax"

包含隨機數的單表資料庫

[編輯 | 編輯原始碼]

下一個程式來自https://kalaharix.wordpress.com/Gambas/creating-a-databases-and-tables-from-Gambas/,稍作調整。它在您的主資料夾中建立一個名為Test.sqlite 的資料庫,用隨機的兩位數填充它,然後訪問資料庫以在表格檢視中顯示它們。

您需要一個包含名為tv1 的表格檢視的窗體。將其設定得又長又薄,因為它有兩列。

Gambas form with tableview Gambas form showing a database table

程式碼為

' Gambas class file

Private db1 As New Connection
Private rs As Result

Public Sub SetupTableView()

  tv1.header = GridView.Horizontal
  tv1.grid = True
  tv1.Rows.count = 0
  tv1.Columns.count = 2
  tv1.Columns[0].text = "RecID"
  tv1.Columns[1].text = "Value"
  tv1.Columns[0].width = 55
  tv1.Columns[1].width = 55

End

Public Sub CreateDatabase()

  db1.Type = "sqlite"
  db1.host = User.home
  db1.name = ""

  'delete an existing test.sqlite
  If Exist(User.home & "/Test.sqlite") Then
    Kill User.home & "/Test.sqlite"
  Endif

  'create test.sqlite
  db1.Open
  db1.Databases.Add("Test.sqlite")
  db1.Close

End

Public Sub MakeTable()

  Dim hTable As Table

  db1.name = "Test.sqlite"
  db1.Open
  hTable = db1.Tables.Add("RandomNumbers")
  hTable.Fields.Add("RecID", db.Integer)
  hTable.Fields.Add("Value", db.Integer)
  hTable.PrimaryKey = ["RecID"]
  hTable.Update

End

Public Sub FillTable()

  Dim i As Integer
  Dim rs1 As Result

  db1.Begin
  rs1 = db1.Create("RandomNumbers")
  For i = 1 To 10000
    rs1!RecID = i
    rs1!Value = Rand(10, 99)
    rs1.Update
  Next
  db1.Commit

Catch
  db1.Rollback
  Message.Error(Error.Text)

End

Public Sub ReadData()
  'read the database
  Dim SQL As String = "SELECT * FROM RandomNumbers"
  rs = db1.Exec(SQL)
End

Public Sub Form_Open()
  SetupTableView
  CreateDatabase
  MakeTable
  FillTable
  ReadData
End

Public Sub Form_Activate()
  'change the rowcount of the gridview from 0 to the number of records.
  'This triggers the data handling event
  tv1.Rows.Count = rs.Count
End

Public Sub tv1_Data(Row As Integer, Column As Integer)
  rs.moveTo(row)
  If Column = 0 Then tv1.Data.Text = rs!RecID Else tv1.Data.Text = rs!Value
  'If Column = 0 Then tv1.Data.Text = Str(rs["RecID"]) Else tv1.Data.Text = Str(rs["Value"])
  'Either of these two lines will do it.
End

Public Sub Form_Close()
  db1.Close
End

當您使用資料庫時,會建立一個臨時“日誌”檔案。該檔案在“提交”時會合併到資料庫中。如果您不想提交,您可以將資料庫“回滾”到您進行最新更改之前的樣子。臨時檔案包含“事務”,這意味著您剛剛進行的最新更改資料庫的工作。這就是db1.Begindb1.Commitdb1.Rollback 的含義。

上面的程式是製作資料庫時需要調整的一個很好的模板。

現金支出應用程式

[編輯 | 編輯原始碼]

此應用程式儲存現金支出的記錄。您可以將每項支出分配到一個類別。每次分配到類別時,都會計算出類別的總計,並且您可以檢視支出中的幾分之幾用於每個類別。

如果您知道自己花了例如 100 歐元,但您只能說明例如 85 歐元,那麼您可以在類別之間分配剩餘的 15 歐元。

在放手編寫程式碼之前,以及檢視窗體之後,我們將看一下設計此類應用程式的過程。

Cash Spending Gambas Form

Gambas Cash Spending File Menu Gambas Cash Spending Data Menu


檔案選單包含MenuNewDatabase、MenuOpenMenuQuit 項。

資料選單包含MenuNewSpending、MenuNewCategory、MenuClearSpending、MenuClearCategories、MenuRound、MenuUnselectAll、MenuCalculateMenuCopy 項。

幫助選單是可選的。

您無法完全看到其名稱的文字框是tbDistribute

Cash Spending Application in Gambas, running

程式啟動時會開啟上次開啟的資料庫檔案,如果這是第一次使用,則會提示建立新的資料庫檔案,或者如果上次開啟後你偷偷地移動了資料庫檔案,則會提示你找到它。它還會在支出類別表格檢視中新增一個空白行。

當為選定的支出行選擇一個類別時(點選類別行中的任何位置,但不包括名稱列,然後按 Enter 鍵),類別總計和百分比將重新計算。

目標文字框中輸入內容是可選的。如果文字框中有數字,則會計算“剩餘待分配”金額。

在內部,資料庫有兩個表,名為支出和類別。您可以看到對應於這兩個資料庫表的兩個表格檢視。以下是每個表中的欄位。

Fields in Cash Spending Database

兩個主鍵是支出ID類別ID。它們按順序對記錄進行編號(1、2、3...)。

支出表的類別欄位包含一個數字,當您在類別表中查詢該數字時,會得到類別名稱。這樣做的好處是:如果您更改類別名稱的拼寫,只需更改一次即可。

隱藏列

[edit | edit source]

使用者不需要看到記錄 ID。它們是資料庫內部的。它們必須是唯一的:每條記錄必須有自己的記錄 ID。它們是支出類別表的 主鍵。它們將是表格檢視中的第一列,但將隱藏(寬度為零)。此外,在支出表中,使用者不希望看到類別 ID(對某個類別的引用)。它將是支出表中的最後一列,也是寬度為零。列從零開始,因此它是第 5 列,位於金額列的右側。

列出任務

[edit | edit source]

在用鉛筆和紙張勾勒出表單設計並規劃好表格和欄位之後,我們接下來考慮希望程式執行的操作。需要注意的是,資料庫可以執行的操作:新增、刪除、修改(以及顯示資料)。下面列出了要執行的任務。這些將是子程式。

資料庫
新建資料庫 在磁碟上建立一個新的資料庫檔案,其中包含兩個表。
開啟資料庫 開啟資料庫並在啟動時顯示其中的內容。
常規
計算 為每個類別彙總總計並計算百分比。
計算總計 支出和類別表中金額的總計。
設定表格檢視 合適的行數和列數以及列標題。
支出表
新建支出 向支出表新增記錄。
顯示支出 在 tv1(表格檢視)中顯示支出表中的內容。
整理支出表 實際上是顯示支出的最後部分。交替顯示藍色線條。
彙總支出 “計算總計”的一部分;彙總所有支出總計。
清除類別(使其成為右鍵選單)。
在選定行上按 DEL 或 BACKSPACE 鍵時刪除記錄。
類別表
新建類別 向類別表新增記錄。
顯示類別 在 tvCategories 中顯示類別表中的內容。
整理類別表 顯示類別的最後部分。交替顯示藍色線條。
彙總類別 “計算總計”的一部分;彙總所有類別金額。
將預設類別插入類別表(選單項)。
在類別行上按 Enter 鍵 在行上按 Enter 鍵會在選定的支出行上插入類別。
在選定行上按 DEL 或 BACKSPACE 鍵時刪除記錄。
其他功能
計算剩餘的分配金額。
將剩餘的金額分配到各個類別。
幫助視窗
將當前使用的資料庫儲存在設定中,以便下次使用。
將所有內容複製為文字,以便貼上到文字處理文件中。
將數字四捨五入到整數歐元(並檢查總計是否差一)。
一個“退出”選單項,用於關閉程式。
有用函式
從 ID 獲取類別名稱 給定 CatID 編號,返回類別名稱(字串)。
整理 給定使用者選擇的檔名,刪除非法字元。

現在開始程式設計。編寫子程式。計算子程式何時會被呼叫以執行其工作。一些子程式可以被分配到選單中。另一些子程式可以在您點選某些內容時執行。您是將要使用此程式的人:您希望點選按鈕嗎?您希望在新增新類別或新支出交易時彈出一個視窗嗎?有沒有什麼好的方法,即直觀的方法,可以讓事情自然而然地發生,正如新使用者所期望的那樣?我們思考一下,並提出一些想法。

  • 我們可以從每個表中的空白行開始,您可以直接在這些行中輸入內容。
  • 在單元格中完成輸入後,儲存該單元格。避免點選儲存按鈕。
  • 在行中的最後一個單元格中按Enter鍵,會建立新的一行。
  • 當選中類別行並且使用者按Enter鍵時,會將該類別放入選定(高亮顯示)的支出行中。移動到下一行沒有類別的支出行,以便您可以點選類別行並按Enter鍵輸入類別。因此,您可以在輸入完其他所有內容後,在最後為所有行輸入類別。
  • 在啟動時,開啟上次開啟的資料庫。如果沒有,則可以選擇建立一個新資料庫,或者瀏覽以找到您移動過的資料庫,或者其他人可能透過 USB 或電子郵件給您的資料庫。
  • 透過點選類別來編輯類別。
  • 透過點選支出表中的單元格來編輯單元格(類別除外,只需在類別表中的一行上按Enter鍵即可插入新的類別)。
  • 當您將支出行分配到某個類別時,重新計算所有類別的百分比。
  • 當您更改目標文字框中的總計金額時,執行減法運算,以計算出剩餘的待分配金額。
  • 在空白的單元格中放入空白,而不是零。
  • 在任一表格檢視中按DeleteBackspace鍵將刪除選定(高亮顯示)的行,並將從資料庫中刪除其記錄。沒有疑問,沒有確認請求,它只是執行操作。一次只能刪除一行,如果您錯誤地按了Delete鍵,重新輸入非常容易。
  • 如果表格檢視行上的第一個單元格中包含記錄 ID 號碼,則該記錄存在,儲存操作只需更新它。如果它是空白的,則資料庫必須首先建立一個新的記錄,併為其分配下一個最高的記錄號碼,將記錄號碼放在第一個單元格中,然後更新它。

以下是FMain表單上的物件名稱。

面板:Panel1(粉色)、Panel2(藍色)。

顯示“支出”、“類別”、“目標:”、“= 已完成:”、“+ 剩餘待分配:”、“金額:”的標籤

名為“LabSpendingTotal”和“LabCategoriesTotal”的標籤,位於表格檢視的右上角。

表格檢視:tv1 用於支出,tvCategories 用於類別。

文字框:tbTarget、tbDone、tbToDo、tbDistribute。

按鈕:bDistribute。

檔案選單:MenuNewDatabase、MenuOpen(Ctrl-O)、MenuQuit(Ctrl-Q)。

資料選單:MenuNewSpending(Ctrl-N)、MenuNewCategory(Ctrl-K)、MenuClearSpending、MenuClearCategories、MenuDefaultCategories、MenuRound(Ctrl-R)、MenuUnselectAll(Ctrl-空格鍵)、MenuCalculate(F4)、MenuCopy(Ctrl-C)。

幫助選單:幫助和說明(F1)(開啟一個名為幫助的單獨表單。在上面放置您喜歡的內容。)

類別選單(不可見,因此不在主選單欄上):MenuClearCategory(當您右鍵單擊支出表中的類別單元格時,該選單會彈出)。

這是程式碼。程式碼後面是對 SQL 語句的解釋。

Public fdb As New Connection 'finance database
Public rs As Result 'result set after querying database
Public SQL As String

Public Sub Form_Open()

  SetUpTableViews
  If IsNull(Settings["Database/host"]) Then
    Select Case Message.Question("Create a new data file, or open an existing one?", "New...", "Open...", "Quit")
      Case 1 'new
        NewDatabase
      Case 2 'open
        OpenDatabase(Null, Null)
      Case Else
        Quit
    End Select
  Else
    OpenDatabase(Settings["Database/host"], Settings["Database/name"])
  Endif

End

Public Sub Form_Close()
  fdb.Close 'close connection
End

Public Sub OpenDatabase(dbHost As String, dbName As String) 'if these are null, ask where the database is

  If Not Exist(dbHost &/ dbName) Or IsNull(dbHost) Then 'it's not where it was last time, or path not supplied
    Dialog.Title = "Where is the database?"
    Dialog.Filter = ["*.db"]
    Dialog.Path = User.Home &/ "Documents/"
    If Dialog.OpenFile() Then Return ' User pressed Cancel; still can't open a database
    Dim s As String = Dialog.Path
    Dim p As Integer = RInStr(s, "/") 'position of last slash
    fdb.host = Left(s, p)
    fdb.Name = Mid(s, p + 1)
  Else
    fdb.host = dbHost
    fdb.Name = dbName
  End If

  Try fdb.Close
  fdb.type = "sqlite3"
  Try fdb.Open
  If fdb.Opened Then
    FMain.Caption = fdb.host &/ fdb.Name
    Settings["Database/host"] = fdb.host
    Settings["Database/name"] = fdb.Name
  Else
    Message.Info("<b>Couldn't connect.</b><br><br>... please try again or create a new database.")
    Return
  Endif

  ShowSpending
  ShowCategories
  Calculate

End

Public Sub NewDatabase()

  Dialog.Path = User.Home & "/" 'setting it to "~/" doesn't work
  Dialog.Title = "Create a New Database"
  If Dialog.SaveFile() Then Return 'clicked Cancel
  Dim s As String = Dialog.Path & ".db"
  Dim p As Integer = RInStr(s, "/") 'position of last slash
  Dim FName As String = Mid(s, p + 1)
  fdb.host = Left(s, p)
  fdb.Name = "" 'This MUST be left blank. If not, database file will not be created
  fdb.Type = "sqlite3"

  If Exist(s) Then Kill s 'delete existing file of that name
  fdb.Close
  Try fdb.Open 'opens a connection to the database; do this after setting properties and before creating
  If Error Then
    Message("Unable to open the database file<br><br>" & Error.Text)
    Return
  Endif
  fdb.Databases.Add(fName) 'does the creating

  fdb.Close
  Dim dbTable As Table
  fdb.name = fName
  Try fdb.Open
  If Not fdb.opened Then
    Message("Unable to open the data file")
    Return
  Endif
  dbTable = fdb.Tables.Add("Spending")
  dbTable.Fields.Add("SpendingID", db.Integer)
  dbTable.Fields.Add("TransDate", db.String)
  dbTable.Fields.Add("Category", db.Integer)
  dbTable.Fields.Add("Comment", db.String)
  dbTable.Fields.Add("Amount", db.Float)
  dbTable.PrimaryKey = ["SpendingID"]
  dbTable.Update
  rs = fdb.Create("Spending")
  If fdb.Error Then
    Message("Couldn't create the Spending table.<br><br>: " & Error.Text)
    Return
  Endif

  rs!SpendingID = 1
  rs!TransDate = ""
  rs!Category = 0
  rs!Comment = ""
  rs!Amount = 0.0
  rs.Update
  fdb.Commit
  If fdb.Error Then
    Message("Couldn't save a first record in the Spending table.<br><br>: " & Error.Text)
    Return
  Endif

  fdb.Close
  fdb.name = fName
  Try fdb.Open
  If Not fdb.opened Then
    Message("Unable to open the data file")
    Return
  Endif
  dbTable = fdb.Tables.Add("Categories")
  dbTable.Fields.Add("CatID", db.Integer)
  dbTable.Fields.Add("Category", db.String)
  dbTable.PrimaryKey = ["CatID"]
  dbTable.Update
  rs = fdb.Create("Categories")
  If fdb.Error Then
    Message("Couldn't create the Categories table.<br><br>: " & Error.Text)
    Return
  Endif

  rs!CatID = 1
  rs!Category = ""
  rs.Update
  fdb.Commit
  If fdb.Error Then
    Message("Couldn't save a first record in the Categories table.<br><br>: " & Error.Text)
    Return
  Endif

End

Public Sub DoTotals()
  labCategoriesTotal.Text = SumTheCategories()
  labSpendingTotal.text = SumTheSpending()
  tbDone.Text = labSpendingTotal.Text
End

Public Sub ShowSpending()

  rs = fdb.Exec("SELECT * FROM Spending")
  Dim L, CatID As Integer
  Dim CatName As String
  tv1.Rows.Count = 0 'clear
  If Not IsNull(rs) Then
    While rs.Available
      tv1.Rows.Count += 1
      L = tv1.Rows.max
      tv1[L, 0].text = rs!SpendingID
      tv1[L, 1].Text = rs!TransDate
      tv1[L, 2].Text = Format(rs!Amount, "0.00")
      CatName = rs!Category
      If Not IsNull(CatName) Then
        CatID = If(IsNull(Val(CatName)), -1, Val(CatName))
        If CatID > -1 Then tv1[L, 3].Text = CategoryNameFromID(CatID)
      Endif
      tv1[L, 4].Text = rs!Comment
      tv1[L, 5].Text = rs!Category 'Category ID in this hidden column
      rs.MoveNext
    Wend
  Endif
  If tv1.Rows.Count = 0 Then tv1.Rows.Count = 1
  TidySpendingTable

End

Public Sub ShowCategories()

  rs = fdb.Exec("SELECT * FROM Categories")
  Dim L As Integer
  Dim t As Float
  tvCategories.Rows.Count = 0 'clear
  If Not IsNull(rs) Then
    While rs.Available
      tvCategories.Rows.Count += 1
      L = tvCategories.Rows.max
      tvCategories[L, 0].text = rs!CatID
      tvCategories[L, 3].Text = rs!Category
      rs.MoveNext
    Wend
  Endif
  If tvCategories.Rows.Count = 0 Then tvCategories.Rows.Count = 1
  TidyCategoriesTable

End

Public Sub NewSpending()
  tv1.Rows.count = tv1.Rows.count + 1
  tv1.MoveTo(tv1.Rows.Max, 1)
  tv1.Edit
End

Public Sub NewCategory()
  tvCategories.Rows.count = tvCategories.Rows.count + 1
  tvCategories.row += 1
  tvCategories.Edit
End

Public Sub tv1_Insert()
  NewSpending
End

Public Sub tvCategories_Insert()
  NewCategory
End

Public Sub tv1_Click()

  Select Case tv1.Column
    Case 1, 2, 4
      tv1.Edit
    Case 3
      If tvCategories.Rows.Count > 0 Then
        tvCategories.SetFocus
        tvCategories.Rows[0].Selected = True
      Endif
  End Select

End

Public Sub tvCategories_Click()
  If tvCategories.Column = 3 Then tvCategories.Edit
End

Public Sub SetUpTableViews()

  Dim i As Integer
  tv1.Columns.count = 6
  tv1.Rows.count = 1
  tv1.Columns[0].Width = 0
  tv1.Columns[1].Alignment = Align.Center
  tv1.Columns[2].Alignment = Align.Right
  For i = 1 To tv1.Columns.Max - 1
    tv1.Columns[i].Width = Choose(i, 80, 80, 130, tv1.Width - tv1.ClientW - 306)
    tv1.Columns[i].Text = Choose(i, "Date", "Amount", "Category", "Comment")
  Next
  tvCategories.Columns.count = 4
  tvCategories.Rows.count = 1
  tvCategories.Columns[0].Width = 0
  For i = 1 To tvCategories.Columns.Max
    tvCategories.Columns[i].Width = Choose(i, 60, 60, tvCategories.Width - tvCategories.ClientW - 350)
    tvCategories.Columns[i].Text = Choose(i, "Total", "%", "Category")
  Next
  tvCategories.Columns[1].Alignment = Align.right
  tvCategories.Columns[2].Alignment = Align.Center
  tv1.Columns[5].Width = 0

End

Public Sub TidySpendingTable()
  For i As Integer = 0 To tv1.Rows.Max
    For j As Integer = 0 To tv1.Columns.Max
      If j = 2 Or j = 3 Then tv1[i, j].Padding = 4
      If i Mod 2 = 1 Then tv1[i, j].Background = &hF0F0FF
    Next
  Next
End

Public Sub TidyCategoriesTable()
  For i As Integer = 0 To tvCategories.Rows.Max
    For j As Integer = 1 To tvCategories.Columns.Max
      tvCategories[i, j].Padding = 4
      If i Mod 2 = 1 Then tvCategories[i, j].Background = &hF0F0FF
    Next
  Next
End

Public Sub Massage(s As String) As String
  'Doesn't like spaces or hyphens in file names. Doesn't complain; just doesn't create the file.

  Dim z As String

  For i As Integer = 0 To Len(s) - 1
    If IsLetter(s[i]) Or IsDigit(s[i]) Or s[i] = "_" Or s[i] = "." Then z &= s[i] Else z &= "_"
  Next
  Return z

End

Public Sub tvCategories_Save(Row As Integer, Column As Integer, Value As String)

  Dim RecID As Integer
  Dim OriginalValue As String = tvCategories[Row, Column].Text

  tvCategories[Row, Column].Text = Value
  If IsNull(tvCategories[Row, 0].Text) Then 'no record ID, so we need a new record
    Dim Res As Result
    SQL = "SELECT MAX(CatID) as 'TheMax' FROM Categories"
    Res = fdb.Exec(SQL)
    If IsNull(Res!TheMax) Then RecID = 1 Else RecID = Res!TheMax + 1
    tvCategories[Row, 0].Text = RecID
    SQL = "INSERT INTO Categories(CatID,Category) VALUES(" & RecID & ",'')"
    fdb.Exec(SQL)
    If fdb.Error Then Message("Couldn't save:<br><br>" & SQL & "<br><br>" & Error.Text)
  Endif
  'update the record
  RecID = tvCategories[Row, 0].Text
  SQL = "UPDATE Categories SET Category = '" & Value & "' WHERE CatID='" & RecID & "'"
  Try fdb.Exec(SQL)
  If fdb.Error Then Message("Couldn't save:" & SQL & "<br><br>" & Error.Text)
  If Value <> OriginalValue Then ShowSpending 'category name was changed

End

Public Sub tv1_Save(Row As Integer, Column As Integer, Value As String)

  Dim RecID As Integer
  Dim FieldName As String = Choose(Column, "TransDate", "Amount", "Category", "Comment")

  If IsNull(tv1[Row, 0].Text) Then 'There's no Record ID, so insert a new record
    Dim Res As Result
    SQL = "SELECT MAX(SpendingID) as 'TheMax' FROM Spending"
    Try Res = fdb.Exec(SQL)
    If IsNull(Res!TheMax) Then RecID = 1 Else RecID = Res!TheMax + 1
    tv1[Row, 0].Text = RecID
    SQL = "INSERT INTO Spending(SpendingID,TransDate,Amount,Category,Comment) VALUES('" & RecID & "',' ',' ',' ',' ')"
    Try fdb.Exec(SQL)
    If Error Then
      Message("Couldn't save: " & Error.Text)
      Return
    Endif
  Endif
  'update record
  RecID = tv1[Row, 0].Text
  SQL = "UPDATE Spending SET " & FieldName & " = '" & Value & "' WHERE SpendingID='" & RecID & "'"
  Try fdb.Exec(SQL)
  If Error Then
    Message("Couldn't save:" & SQL & "<br><br>" & Error.Text)
    Return
  Endif
  If Column = 2 Then
    tv1[Row, Column].Text = Format(Val(Value), "###0.00")
    Calculate 'amount has changed
  Else
    tv1[Row, Column].Text = Value
  Endif

Catch
  Message("Couldn't save ... have you created and opened a database yet?")
  Stop Event 'Don't go automatically to the next cell. If you do, you'll get this message twice.

End

Public Sub tv1_KeyPress()

  Select Case Key.Code
    Case Key.BackSpace, Key.Del 'remove record
      Dim RecID As Integer = tv1[tv1.Row, 0].Text
      SQL = "DELETE FROM Spending WHERE SpendingID='" & RecID & "'"
      Try fdb.Exec(SQL)
      If Error Then
        Message("Couldn't delete<br><br>" & Error.Text)
      Else
        tv1.Rows.Remove(tv1.Row)
        If tv1.Rows.Count = 0 Then tv1.Rows.Count = 1
      Endif
    Case Key.Enter, Key.Return
      If tvCategories.Rows.Count > 0 Then
        tvCategories.SetFocus
        tvCategories.Rows[0].Selected = True
      Endif
  End Select

End

Public Sub tvCategories_KeyPress()

  Select Case Key.Code
    Case Key.BackSpace, Key.Del 'remove record
      Dim RecID As Integer = tvCategories[tvCategories.Row, 0].Text
      SQL = "DELETE FROM Categories WHERE CatID='" & RecID & "'"
      Try fdb.Exec(SQL)
      If Error Then
        Message("Couldn't delete<br><br>" & Error.Text)
      Else
        tvCategories.Rows.Remove(tvCategories.Row)
      Endif
    Case Key.Enter, Key.Return
      EnterOnCategoryLine 'action on pressing Enter
      tvCategories.UnSelectAll
  End Select

End

Public Sub MenuClearSpending_Click()
  fdb.Exec("DELETE FROM Spending")
  tv1.Rows.count = 1
  tv1.Clear
End

Public Sub MenuClearCategories_Click()
  fdb.Exec("DELETE FROM Categories")
  tvCategories.Rows.count = 1
  tvCategories.Clear
End

Public Sub CategoryNameFromID(ID As Integer) As String

  Dim res As Result = fdb.Exec("SELECT Category FROM Categories WHERE CatID=" & ID)

  If Not res.Available Then Return "?"
  If IsNull(res!Category) Then Return "-"
  Return res!Category

End

Public Sub EnterOnCategoryLine()  'apply this category to the selected Spending line

  If tv1.row < 0 Then Return
  If IsNull(tv1[tv1.row, 0].text) Then
    Message("Please save this spending record first by entering some other item of data; there's no record ID yet.")
    Return
  Endif
  tv1[tv1.row, 3].text = tvCategories[tvCategories.row, 3].Text
  Dim CategoryID As String = tvCategories[tvCategories.row, 0].Text
  Dim SpendingID As String = tv1[tv1.row, 0].text
  tv1[tv1.row, 5].text = CategoryID
  SQL = "UPDATE Spending SET Category='" & CategoryID & "' WHERE SpendingID='" & SpendingID & "'"
  Try fdb.Exec(SQL)
  If Error Then
    Message("Couldn't save the category<br><br>" & SQL & "<br><br>" & Error.text)
    Return
  Endif
  Calculate
  For i As Integer = tv1.row To tv1.Rows.Max
    If IsNull(tv1[i, 3].text) Then
      tv1.Rows[i].Selected = True 'select the next Spending row that needs a category
      tvCategories.SetFocus
      Return
    Endif
  Next
  tv1.SetFocus

End

Public Sub Calculate()

  Dim i, j, CategoryID As Integer
  Dim t, GrandTotal As Float
  Dim res As Result
  Dim s As String

  For i = 0 To tvCategories.Rows.Max 'every category
    If IsNull(tvCategories[i, 0].Text) Then Continue
    CategoryID = tvCategories[i, 0].Text
    Try Res = fdb.Exec("SELECT Total(Amount) AS TotalAmount FROM Spending WHERE Category=" & CategoryID)
    If Error Then
      Message("Couldn't total<br><br>" & Error.Text)
      Continue
    Endif
    While res.Available
      t = res!TotalAmount
      GrandTotal += t
      If t = 0 Then tvCategories[i, 1].Text = "" Else tvCategories[i, 1].Text = Format(t, "##0.00")
      res.MoveNext
    Wend
  Next
  If GrandTotal = 0 Then Return
  For i = 0 To tvCategories.Rows.Max
    s = tvCategories[i, 1].Text
    If Not IsNull(s) And If Val(s) > 0 Then tvCategories[i, 2].Text = Format(100 * Val(s) / GrandTotal, "##0.##") Else tvCategories[i, 2].Text = ""
  Next
  tbDone.Text = Format(GrandTotal, "##0.00")
  labSpendingTotal.Text = tbDone.Text
  labCategoriesTotal.Text = SumTheCategories()
  If Not IsNull(tbTarget.text) Then
    tbToDo.Text = Format(Val(tbTarget.Text) - GrandTotal, "##0.00")
    tbDistribute.Text = tbToDo.Text
  Endif

End

Public Sub SaveCategoriesTable()
  For i As Integer = 0 To tvCategories.Rows.Max
    SaveCategoryLine(i)
  Next
End

Public Sub SaveCategoryLine(i As Integer) 'i is the line number

  Dim RecID As Integer
  Dim t, pct As Float
  Dim s, CategoryName As String
  Dim res As Result

  RecID = Val(tvCategories[i, 0].Text)
  CategoryName = tvCategories[i, 3].Text
  t = If(IsNull(tvCategories[i, 1].Text), 0, Val(tvCategories[i, 1].Text))
  s = tvCategories[i, 2].Text
  pct = If(IsNull(s), 0, Val(s))
  If IsNull(RecID) Then 'new record needed
    res = fdb.Exec("SELECT Max(CatID) AS MaxCatID FROM Categories")
    RecID = res!MaxCatID + 1
    SQL = "INSERT INTO Categories(CatID,Category) VALUES(" & RecID & "," & CategoryName & ")"
    fdb.Exec(SQL)
    If Error Then
      Message("Couldn't insert a new record<br><br>" & SQL & "<br><br>" & Error.text)
      Return
    Endif
  Else
    SQL = "UPDATE Categories SET Category='" & CategoryName & "' WHERE CatID=" & RecID
    Try fdb.Exec(SQL)
    'before checking Error, don't forget to use TRY. Otherwise Error will be set and you'll seem to have an error when you don't
    If Error Then
      Message("Couldn't update a record<br><br>" & SQL & "<br><br>" & Error.text)
      Return
    Endif
  Endif

End

Public Sub SumTheCategories() As String

  Dim t As Float
  Dim s As String

  For i As Integer = 0 To tvCategories.Rows.Max
    s = tvCategories[i, 1].Text
    If Not IsNull(s) Then t += Val(s)
  Next
  Return Format(t, "##0.00")

End

Public Sub SumTheSpending() As String

  Dim t As Float
  Dim s As String
  For i As Integer = 0 To tv1.Rows.Max
    s = tv1[i, 2].Text
    If Not IsNull(s) Then t += Val(s)
  Next
  Return Format(t, "##0.00")

End

Public Sub MenuCalculate_Click()
  Calculate
End

Public Sub tbTarget_LostFocus()

  If Not IsNull(tbTarget.text) Then tbTarget.Text = Format(Val(tbTarget.Text), "##0.00") Else tbTarget.Text = ""
  Calculate

End

Public Sub tbTarget_KeyPress()
  If Key.Code = Key.Enter Or Key.Code = Key.Return Then FMain.SetFocus
End

Public Sub bDistribute_Click()

  Dim t, pct, y, z As Float

  If IsNull(tbDistribute.Text) Then Return
  Dim x As Float = Val(tbDistribute.Text)
  For i As Integer = 0 To tvCategories.Rows.Max
    If IsNull(tvCategories[i, 1].Text) Then Continue
    If IsNull(tvCategories[i, 2].Text) Then Continue
    t = Val(tvCategories[i, 1].Text)
    pct = Val(tvCategories[i, 2].Text)
    y = t + pct / 100 * x
    z += y 'running total
    If y = 0 Then tvCategories[i, 1].Text = "" Else tvCategories[i, 1].Text = Format(y, "##0.00")
    SaveCategoryLine(i)
  Next
  labCategoriesTotal.text = Format(z, "##0.00")
  FMain.SetFocus

End

Public Sub tbDistribute_LostFocus() 'when leaving, fix the appearance
  If Not IsNull(tbDistribute.text) Then tbDistribute.Text = Format(Val(tbDistribute.Text), "##0.00") Else tbDistribute.Text = ""
End

Public Sub tbDistribute_KeyPress() 'enter leaves the textbox
  If Key.Code = Key.Enter Or Key.Code = Key.Return Then FMain.SetFocus
End

Public Sub MenuDefaultCategories_Click()

  Try fdb.Exec("DELETE FROM Categories") 'it might be already cleared
  tvCategories.Rows.Count = 9
  tvCategories.Clear
  Dim s As String
  For i As Integer = 0 To 8
    s = Choose(i + 1, "Provisions", "Travel", "Medical", "Donations", "Papers etc", "Clothes", "Personal", "Phone", "Repairs")
    tvCategories[i, 3].text = s
    tvCategories[i, 0].text = i + 1
    SQL = "INSERT INTO Categories(CatID,Category) VALUES(" & Str(i + 1) & ",'" & s & "')"
    Try fdb.Exec(SQL)
    If Error Then Message("Couldn't insert a new record in the categories table.<br><br>" & SQL & "<br><br>" & Error.Text)
  Next
  labCategoriesTotal.text = ""

End

Public Sub MenuRound_Click()

  Dim s As String
  Dim x, t As Float

  For i As Integer = 0 To tvCategories.Rows.Max
    s = tvCategories[i, 1].Text
    If IsNull(s) Then
      tvCategories[i, 1].Text = ""
    Else
      x = Round(Val(s))
      t = t + x
      tvCategories[i, 1].Text = x
    Endif
  Next
  labCategoriesTotal.Text = Format(t, "##0.00")
  For i As Integer = 0 To tvCategories.Rows.Max
    s = tvCategories[i, 2].Text
    If Not IsNull(s) Then tvCategories[i, 2].Text = Round(Val(s))
  Next

End

Public Sub MenuOpen_Click()
  OpenDatabase(Null, Null)
End

Public Sub MenuNewDatabase_Click()
  NewDatabase
End

Public Sub MenuNewSpending_Click()
  NewSpending
End

Public Sub MenuNewCategory_Click()
  NewCategory
End

Public Sub MenuQuit_Click()
  Quit
End

Public Sub MenuCopy_Click()
  Dim s, z As String
  Dim i, j As Integer

  For i = 0 To tv1.Rows.Max
    s = tv1[i, 1].Text
    For j = 2 To 4
      s &= gb.Tab & tv1[i, j].Text
    Next
    z &= If(IsNull(z), "", gb.NewLine) & s
  Next
  z &= gb.NewLine
  For i = 0 To tvCategories.Rows.Max
    s = tvCategories[i, 1].Text
    For j = 2 To 3
      s &= gb.Tab & tvCategories[i, j].Text
    Next
    z &= If(IsNull(z), "", gb.NewLine) & s
  Next
  z &= gb.NewLine & gb.NewLine & "Total Withdrawn: " & tbTarget.Text & gb.tab & " =  Total Accounted For: " & tbDone.Text & gb.tab & " +  Cash on hand: " & tbToDo.Text
  Clipboard.Copy(z)

End

Public Sub MenuShowHelp_Click()
  Help.ShowModal
End

Public Sub MenuClearCategory_Click()
  Dim RecID As Integer = tv1[tv1.row, 0].Text
  fdb.Exec("UPDATE Spending Set Category=' ' WHERE SpendingID=" & RecID)
  tv1[tv1.row, 3].Text = "" 'Cat text
  tv1[tv1.row, 5].Text = "" 'Cat ID
  Calculate
End

Public Sub MenuUnselectAll_Click()
  tv1.Rows.UnSelectAll
  tvCategories.Rows.UnSelectAll
End

SQL 語句

[edit | edit source]

其中一些語句按原樣使用。另一些語句是由多個部分組成的字串。您可能會看到SQL = …。語句的一部分是 SQL,欄位名稱可能會在正確的位置新增,並存儲在變數中,例如。或者記錄 ID 可能儲存在名為 RecID 的變數中。在傳送到 SQLite 的字串中使用單引號。在 Gambas 中組裝語句時使用雙引號。

SELECT * FROM Spending 從支出表中選擇所有內容。
SELECT * FROM Categories 從類別表中選擇所有內容。
SELECT MAX(CatID) as 'TheMax' FROM Categories 從類別表中獲取最大的 CatID,並將其命名為“TheMax”。
INSERT INTO Categories(CatID,Category) VALUES(123,'Entertainment') 在類別表中建立一個新記錄。

123放入CatID欄位中,將Entertainment放入Category欄位中。

UPDATE Categories SET Category = 'Entertainment' WHERE CatID='123' 必須更新類別表。

CatID等於123的記錄中,將Entertainment放入Category欄位中。

SELECT MAX(SpendingID) as 'TheMax' FROM Spending 支出表中找到最大的SpendingID,並將其命名為“TheMax”。
INSERT INTO Spending(SpendingID,TransDate,Amount,Category,Comment) VALUES('123',' ',' ',' ',' ') 在支出表中建立一個新記錄。

支出ID = 123

TransDate = 空白

金額 = 空白

類別 = 空白

註釋 = 空白

UPDATE Spending SET TransDate = '4-11-2019' WHERE SpendingID='123' 將“4-11-2019”放入支出表中SpendingID為 123 的記錄的TransDate欄位中。
DELETE FROM Spending WHERE SpendingID='123' 刪除支出表中記錄 ID 為 123 的記錄。
DELETE FROM Categories WHERE CatID='123' 刪除類別表中記錄 ID 為 123 的記錄。
DELETE FROM Spending 刪除支出表中的所有記錄。所有資料都會消失,永遠不會再出現。
DELETE FROM Categories 刪除類別表中的所有記錄。所有類別記錄都會消失,永遠不會再出現。
SELECT Category FROM Categories WHERE CatID=123 給我CatID記錄號為 123 的類別的名稱。
UPDATE Spending SET Category='4' WHERE SpendingID='123' 支出表中記錄 123 的Category欄位設定為 4。此支出專案歸入第四個類別,無論它是什麼。要查詢第四個類別是什麼,請查詢類別表,並找到CatID=4 的記錄。
SELECT Total(Amount) AS TotalAmount FROM Spending WHERE Category='4' 獲取支出表中Category欄位為 4 的所有記錄的Amount欄位中所有數字的總和。簡單地說,就是將類別 4 中的所有支出金額加起來。將結果命名為“TotalAmount”。
SELECT Max(CatID) AS MaxCatID FROM Categories 從類別表中獲取最大的 CatID。將其命名為MaxCatID
SQL = "INSERT INTO Categories(CatID,Category) VALUES(4,Travel)" 建立一個新的類別記錄。將CatID欄位設定為 4,將Category欄位設定為“Travel”。
UPDATE Categories SET Category='Travel' WHERE CatID=4 更新Categories表中記錄 ID 為 4 的記錄。將“Travel”放入Category欄位中。
UPDATE Spending Set Category=' ' WHERE SpendingID=123 將空白放入Spending表中記錄 123 的Category欄位中。

這些語句可以是SELECTINSERTDELETEUPDATE

這些模式是

SELECT fields FROM table

SELECT fields FROM table WHERE field = something

SELECT * FROM table

SELECT Total(field) AS nameForIt FROM table

SELECT Max(field) AS nameForIt FROM table

INSERT INTO table(fields) VALUES(values)

DELETE FROM table

DELETE FROM table WHERE field = something

UPDATE table SET field = something WHERE keyfield = something

這些不是唯一的 SQL 語句:還有很多。它們足以讓你對 SQL 有一個基本的瞭解。SQLite 的線上幫助可以在這裡找到

http://www.sqlitetutorial.net/

http://sqlitetutorials.com/

https://w3schools.tw/sql/

關於使用 UPDATE 語句的最重要的一點

更新記錄時要小心。

如果你省略 WHERE 子句,所有記錄都將被更新!

例如,不要寫這個:UPDATE Spending SET Amount=12.50。這會將 12.50 放入所有記錄的Amount欄位中。所有金額都將變成 12.50。你應該說UPDATE Spending SET Amount=12.50 WHERE SpendingID=42

從 Zip 中程式設計 Gambas
 ← 模組 SQLite 列印 → 
華夏公益教科書