从 Zip/SQLite 开始编程 Gambas
数据库是硬盘上具有结构的文件,因此它可以存储大量信息并快速访问它。
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 初始化为空字符串“”。
SQLite 是 Gambas 的一个组件(可选部分)。还有一个数据库访问组件。在项目菜单 > 属性... > 组件页面上,确保选中gb.db 和gb.db.sqlite3。如果您的项目中没有这些组件,您将在尝试运行程序时立即收到错误。
您向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 很有用。
除了访问数据库中的信息之外,使用数据库时,您还需要能够
- 添加记录
- 删除记录
- 修改记录
除了最简单的数据库之外,所有数据库都包含多个表。表可以相互链接,因此记录可以包含指向其他表中适用的行的指路牌。指路牌是另一个表中记录的记录 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 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.Begin、db1.Commit 和db1.Rollback 的含义。
上面的程序是制作数据库时需要调整的一个很好的模板。
此应用程序保存现金支出的记录。您可以将每项支出分配到一个类别。每次分配到类别时,都会计算出类别的总计,并且您可以查看支出中的几分之几用于每个类别。
如果您知道自己花了例如 100 欧元,但您只能说明例如 85 欧元,那么您可以在类别之间分配剩余的 15 欧元。
在放手编写代码之前,以及查看窗体之后,我们将看一下设计此类应用程序的过程。
文件菜单包含MenuNewDatabase、MenuOpen 和MenuQuit 项。
数据菜单包含MenuNewSpending、MenuNewCategory、MenuClearSpending、MenuClearCategories、MenuRound、MenuUnselectAll、MenuCalculate 和MenuCopy 项。
帮助菜单是可选的。
您无法完全看到其名称的文本框是tbDistribute。
程序启动时会打开上次打开的数据库文件,如果这是第一次使用,则会提示创建新的数据库文件,或者如果上次打开后你偷偷地移动了数据库文件,则会提示你找到它。它还会在支出和类别表格视图中添加一个空白行。
当为选定的支出行选择一个类别时(点击类别行中的任何位置,但不包括名称列,然后按 Enter 键),类别总计和百分比将重新计算。
在目标文本框中输入内容是可选的。如果文本框中有数字,则会计算“剩余待分配”金额。
在内部,数据库有两个表,名为支出和类别。您可以看到对应于这两个数据库表的两个表格视图。以下是每个表中的字段。
两个主键是支出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键即可插入新的类别)。
- 当您将支出行分配到某个类别时,重新计算所有类别的百分比。
- 当您更改目标文本框中的总计金额时,执行减法运算,以计算出剩余的待分配金额。
- 在空白的单元格中放入空白,而不是零。
- 在任一表格视图中按Delete或Backspace键将删除选定(高亮显示)的行,并将从数据库中删除其记录。没有疑问,没有确认请求,它只是执行操作。一次只能删除一行,如果您错误地按了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字段中。 |
这些语句可以是SELECT、INSERT、DELETE或UPDATE。
这些模式是
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/
关于使用 UPDATE 语句的最重要的一点
更新记录时要小心。
如果你省略 WHERE 子句,所有记录都将被更新! |
---|
例如,不要写这个:UPDATE Spending SET Amount=12.50。这会将 12.50 放入所有记录的Amount字段中。所有金额都将变成 12.50。你应该说UPDATE Spending SET Amount=12.50 WHERE SpendingID=42。