ZetCode-数据库教程-二-

ZetCode 数据库教程(二)

原文:ZetCode

协议:CC BY-NC-SA 4.0

MySQL Ruby 教程

原文: http://zetcode.com/db/mysqlrubytutorial/

这是针对 MySQL 数据库的 Ruby 编程教程。 它涵盖了使用 Ruby 进行 MySQL 编程的基础。 它使用mysql模块。 这些示例是在 Ubuntu Linux 上创建和测试的。

在 ZetCode 上有一个类似的 MySQL C API 教程MySQL Visual Basic 教程MySQL Python 教程

您也可以考虑查看 MySQL 教程

MySQL & Ruby

MySQL 是领先的开源数据库管理系统。 它是一个多用户,多线程的数据库管理系统。 MySQL 在网络上特别流行。 它是由 Linux,Apache,MySQL 和 PHP 组成的非常流行的LAMP平台的一部分。 目前,MySQL 由 Oracle 拥有。 MySQL 数据库在最重要的 OS 平台上可用。Ruby 是一种动态的,反射性的,通用的面向对象的编程语言。 最近,它在 Web 编程中变得非常流行,这主要归功于 Ruby on Rails 框架的成功。

mysql 模块

mysql模块是 MySQL 服务器的 Ruby 接口。 它为 Ruby 程序提供的功能与 MySQL C API 为 C 程序提供的功能相同。

$ sudo gem1.9 install mysql

在这里,我们为 MySQL 数据库安装 Ruby 模块。

开始之前

我们将创建一个新的数据库用户和一个新的数据库。 为此,我们使用mysql客户端程序。

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 30
Server version: 5.0.67-0ubuntu6 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema | 
| mysql              | 
+--------------------+
2 rows in set (0.00 sec)

我们使用 root 帐户连接到 MySQL 服务器。 我们用SHOW DATABASES语句显示所有可用的数据库。

mysql> CREATE DATABASE mydb;
Query OK, 1 row affected (0.02 sec)

我们创建一个新的mydb数据库。 在整个教程中,我们将使用此数据库。

mysql> CREATE USER user12@localhost IDENTIFIED BY '34klq*';
Query OK, 0 rows affected (0.00 sec)

mysql> USE mydb;
Database changed

mysql> GRANT ALL ON mydb.* to user12@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql> quit;
Bye

我们创建一个新的数据库用户。 我们授予该用户mydb数据库所有表的所有特权。

MySQL 服务器版本

在第一个示例中,我们将获取 MySQL 数据库的版本。

#!/usr/bin/ruby

require 'mysql'

begin
    con = Mysql.new 'localhost', 'user12', '34klq*'
    puts con.get_server_info
    rs = con.query 'SELECT VERSION()'
    puts rs.fetch_row    

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

在此脚本中,我们获取服务器版本。 我们用两种不同的方式来做。

require 'mysql'

我们导入mysql模块。 该模块具有用于 MySQL 数据库的类和方法。

con = Mysql.new 'localhost', 'user12', '34klq*'

我们创建连接对象。 参数包括主机名,用户名和密码。 在我们的情况下,主机名是localhost,例如我们的电脑。

puts con.get_server_info

我们创建的 Mysql 对象具有get_server_info方法。 它返回安装的 MySQL 服务器的版本。

rs = con.query 'SELECT VERSION()'
puts rs.fetch_row    

获取版本的另一种方法是执行SELECT VERSION() SQL 语句。 我们获取数据。 由于只检索一条记录,因此我们称为fetch_row方法。

rescue Mysql::Error => e
    puts e.errno
    puts e.error

我们检查错误。 这很重要,因为使用数据库容易出错。

ensure
    con.close if con
end

最后,我们释放资源。

$ ./version.rb
5.5.9
5.5.9

输出可能类似于上面。

列出数据库

MySQL Ruby 模块具有list_dbs方法,该方法返回可用的数据库。

#!/usr/bin/ruby

require 'mysql'

begin
    con = Mysql.new 'localhost', 'user12', '34klq*'

    con.list_dbs.each do |db|
        puts db
    end

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

在此脚本中,我们在 MySQL 服务器上打印所有可用的数据库。

con.list_dbs.each do |db|
    puts db
end

list_dbs方法返回可用数据库名称的数组。 使用数组的each方法,我们将数组的每个项目打印到控制台。

$ ./listdb.rb
information_schema
mydb
test
world

在我的系统上,我创建了上述数据库。

创建并填充表

我们创建一个表,并用一些数据填充它。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'

    con.query("CREATE TABLE IF NOT EXISTS \
        Writers(Id INT PRIMARY KEY AUTO_INCREMENT, Name VARCHAR(25))")
    con.query("INSERT INTO Writers(Name) VALUES('Jack London')")
    con.query("INSERT INTO Writers(Name) VALUES('Honore de Balzac')")
    con.query("INSERT INTO Writers(Name) VALUES('Lion Feuchtwanger')")
    con.query("INSERT INTO Writers(Name) VALUES('Emile Zola')")
    con.query("INSERT INTO Writers(Name) VALUES('Truman Capote')")   

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

我们创建一个Writers表,并向其中添加五个作者。

con.query("CREATE TABLE IF NOT EXISTS \
        Writers(Id INT PRIMARY KEY AUTO_INCREMENT, Name VARCHAR(25))")

要执行 SQL 语句,我们使用query方法。 该 SQL 语句创建一个称为Writers的新数据库表。 它有两列。 ID 和名称。

con.query("INSERT INTO Writers(Name) VALUES('Jack London')")
con.query("INSERT INTO Writers(Name) VALUES('Honore de Balzac')")
...

我们使用INSERT语句在表中插入作者。 在这里,我们添加两行。

mysql> SELECT * FROM Writers;
+----+-------------------+
| Id | Name              |
+----+-------------------+
|  1 | Jack London       |
|  2 | Honore de Balzac  |
|  3 | Lion Feuchtwanger |
|  4 | Emile Zola        |
|  5 | Truman Capote     |
+----+-------------------+
5 rows in set (0.00 sec)

执行脚本后,我们使用mysql客户端工具从Writers表中选择所有数据。

检索数据

现在我们已经将一些数据插入数据库中,我们可以将其取回。

#!/usr/bin/ruby

require "mysql"

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'

    rs = con.query("SELECT * FROM Writers")
    n_rows = rs.num_rows

    puts "There are #{n_rows} rows in the result set"

    n_rows.times do
        puts rs.fetch_row.join("\s")
    end

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

在此示例中,我们从 Writers 表中检索所有数据。

con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'

构造器的最后一个参数是我们要连接的数据库名称。

rs = con.query("SELECT * FROM Writers")

该 SQL 语句从Writers表中选择所有数据。

n_rows = rs.num_rows

我们使用结果集对象的num_rows方法获得结果集中的行数。

n_rows.times do
    puts rs.fetch_row.join("\s")
end

在这里,我们使用fetch_row方法获取每一行。 它返回一行作为字段数组。 默认情况下,打印时这些字段用换行分隔。 使用join方法,我们将每一行打印在一行上。 字段由一个空格分隔。

$ ./retrieve1.rb
There are 5 rows in the result set
1 Jack London
2 Honore de Balzac
3 Lion Feuchtwanger
4 Emile Zola
5 Truman Capote

接下来,我们介绍另一种从表中检索数据的方法。

#!/usr/bin/ruby

require "mysql"

begin
    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'

    rs = con.query("SELECT * FROM Writers")

    rs.each do |row|
        puts row.join("\s")
    end

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

我们从Writers表中打印所有数据。 这次我们使用结果集的每种方法遍历数据。

rs.each do |row|
    puts row.join("\s")
end

我们使用每种方法遍历结果集。

$ ./retrieve2.rb
1 Jack London
2 Honore de Balzac
3 Lion Feuchtwanger
4 Emile Zola
5 Truman Capote

这是示例的输出。

我们可以以 Ruby 哈希的形式遍历数据。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'    
    rs = con.query "SELECT * FROM Writers WHERE Id IN (1, 2, 3)"
    puts "We have #{rs.num_rows} row(s)"

    rs.each_hash do |row|
       puts row['Id'] + " " + row['Name']
    end      

rescue Mysql::Error => e
    puts e    
ensure
    con.close if con
end

在示例中,我们使用each_hash迭代器。 可以通过列名称检索结果集中的记录。

rs.each_hash do |row|
    puts row['Id'] + " " + row['Name']
end     

我们使用each_hash方法查看结果集。 返回的每一行都是一个 Ruby 哈希; 键值对的集合。 键是列名。

$ ./retrieve3.rb
We have 3 row(s)
1 Jack London
2 Honore de Balzac
3 Lion Feuchtwanger

示例的输出。

多个语句

MySQL 支持多条语句执行。 必须通过特殊选项启用它。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'
    con.set_server_option Mysql::OPTION_MULTI_STATEMENTS_ON

    rs = con.query "SELECT Name FROM Writers WHERE Id=1;
                    SELECT Name FROM Writers WHERE Id=2;
                    SELECT Name FROM Writers WHERE Id=3"

    puts rs.fetch_row        

    while con.next_result
        rs = con.store_result    
        puts rs.fetch_row
    end       

rescue Mysql::Error => e
    puts e.errno
    puts e.error

ensure
    con.close if con
end

在此示例中,我们在一个查询中具有三个SELECT语句。

con.set_server_option Mysql::OPTION_MULTI_STATEMENTS_ON

首先,我们需要使用Mysql::OPTION_MULTI_STATEMENTS_ON启用多条语句处理。

rs = con.query "SELECT Name FROM Writers WHERE Id=1;
                SELECT Name FROM Writers WHERE Id=2;
                SELECT Name FROM Writers WHERE Id=3"

在这里,我们定义了三个SELECT语句。 它们之间用分号分隔。

puts rs.fetch_row 

查询方法返回第一个结果集。 我们从该结果集中获取一行。

while con.next_result
    rs = con.store_result    
    puts rs.fetch_row
end   

我们将获得其他结果集,直到没有更多要处理的语句为止。

$ ./multiplest.rb
Jack London
Honore de Balzac
Lion Feuchtwanger

运行示例。

元数据

元数据是有关数据库中数据的信息。 MySQL 系统中的元数据包含有关存储数据的表和列的信息。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'    
    rs = con.query "SELECT * FROM Writers WHERE Id IN (1, 2, 3)"
    puts "We have #{con.field_count} fields"
    puts "We have #{rs.num_rows} row(s)"
    puts rs.fetch_row.join("\s")

rescue Mysql::Error => e
    puts e    
ensure
    con.close if con
end

在此脚本中,我们从 SQL 查询中找出行数和列数。

rs = con.query "SELECT * FROM Writers WHERE Id IN (1, 2, 3)"

该 SQL 语句返回三行。 每行有两列。

puts "We have #{con.field_count} fields"
puts "We have #{rs.num_rows} row(s)"

这两行返回结果集中的列数和行数。 请注意,此处的字段是列的同义词。 返回的数据是元数据。

puts rs.fetch_row.join("\s")

在这里,我们从结果集中返回一行。 这是存储在我们数据库表中的原始数据。

对于INSERTDELETEUPDATE语句,有一种称为rows_affected的方法。 此方法返回受这三个语句影响的行数。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'    
    con.query "DELETE FROM Writers WHERE Id IN (1, 2, 3)"

    puts "The query has affected #{con.affected_rows} rows"

rescue Mysql::Error => e
    puts e    
ensure
    con.close if con
end

在我们的示例中,我们从Writers表中删除了前三行。

con.query "DELETE FROM Writers WHERE Id IN (1, 2, 3)"

一个 SQL 语句,该语句删除Writers表的前三行。

puts "The query has affected #{con.affected_rows} rows"

在这里,我们得到受上述 SQL 语句影响的行数。 该数字属于元数据。

$ ./affected.rb
The query has affected 3 rows

mysql> SELECT * FROM Writers;
+----+---------------+
| Id | Name          |
+----+---------------+
|  4 | Emile Zola    |
|  5 | Truman Capote |
+----+---------------+
2 rows in set (0.00 sec)

我们执行受影响的.rb脚本,并检查Writers表中的更改。 三行已被删除。

在下一个示例中,我们将检查有关字段的元数据。

#!/usr/bin/ruby

require 'mysql'

begin
    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'
    rs = con.query "SELECT * FROM Writers WHERE Id=1"
    field = rs.fetch_field_direct 1

    puts "Table name: #{field.table}"
    puts "Field name: #{field.name}"
    puts "Field length: #{field.length}"
    puts "Field type: #{field.type}"

rescue Mysql::Error => e
    puts e

ensure
    con.close if con
end

我们从数据库中获得一条记录。 我们得到字段的表名,列名,长度和类型。

rs = con.query "SELECT * FROM Writers WHERE Id=1"

该查询返回一行。 它有两列。

field = rs.fetch_field_direct 1

使用fetch_field_direct方法,我们得到了一条特定的记录。 更准确地说,记录来自第一行第二列的交集。

puts "Table name: #{field.table}"
puts "Field name: #{field.name}"
puts "Field length: #{field.length}"
puts "Field type: #{field.type}"

我们使用字段对象的属性读取器获取元数据。

$ ./metadata.rb
Table name: Writers
Field name: Name
Field length: 25
Field type: 253

这是示例的输出。

在与元数据有关的最后一个示例中,我们将打印表中的所有行及其列名。

#!/usr/bin/ruby

require 'mysql'

begin
    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'
    rs = con.query "SELECT * FROM Writers"

    fields = rs.fetch_fields    
    puts "%3s %s" % [fields[0].name, fields[1].name]

    rs.each_hash do |row|
        puts "%3s %s" % [row['Id'], row['Name']]
    end

rescue Mysql::Error => e
    puts e

ensure
    con.close if con
end

我们将Writers表的内容打印到控制台。 现在,我们也包括列的名称。

fields = rs.fetch_fields    
puts "%3s %s" % [fields[0].name, fields[1].name]

第一步,我们获取列名。 它们使用标准的 Ruby 字符串格式化函数进行打印。

rs.each_hash do |row|
    puts "%3s %s" % [row['Id'], row['Name']]
end

现在,将获取数据并将其打印到控制台。 我们也进行一些格式化。

$ ./columnheaders.rb
 Id Name
  1 Jack London
  2 Honore de Balzac
  3 Lion Feuchtwanger
  4 Emile Zola
  5 Truman Capote

脚本的输出。

预备语句

现在,我们将以预备语句来关注自己。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

#!/usr/bin/ruby

require 'mysql'

name = "Stefan Zweig"

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'    
    pst = con.prepare "INSERT INTO Writers(Name) VALUES(?)"
    pst.execute name

rescue Mysql::Error => e
    puts e    

ensure
    con.close if con
    pst.close if pst
end

在上面的示例中,我们将新行插入Writers表。 我们使用预备语句。

pst = con.prepare "INSERT INTO Writers(Name) VALUES(?)"

prepare方法用于创建预备语句。 ?字符是一个占位符。 稍后,我们将一个值绑定到该占位符。

pst.execute name

我们将名称变量中的值绑定到占位符,并执行预备语句。

pst.close if pst

预备语句已关闭。

mysql> SELECT * FROM Writers;
+----+-------------------+
| Id | Name              |
+----+-------------------+
|  1 | Jack London       |
|  2 | Honore de Balzac  |
|  3 | Lion Feuchtwanger |
|  4 | Emile Zola        |
|  5 | Truman Capote     |
|  6 | Stefan Zweig      |
+----+-------------------+
6 rows in set (0.00 sec)

脚本成功运行后,我们在Writers表中看到一个新作者。

写图像

有些人喜欢将其图像放入数据库中,有些人则希望将其保留在文件系统中以供其应用使用。 当我们处理大量图像时,会出现技术难题。 图像是二进制数据。 MySQL 数据库具有一种特殊的数据类型来存储称为BLOB(二进制大对象)的二进制数据。

mysql> CREATE TABLE Images(Id INT PRIMARY KEY AUTO_INCREMENT, Data MEDIUMBLOB);
Query OK, 0 rows affected (0.06 sec)

对于此示例,我们创建一个名为Images的新表。

#!/usr/bin/ruby

require 'mysql'

begin

    fin = File.open("woman.jpg" , "rb")
    img = fin.read

rescue SystemCallError => e      
    puts e
ensure
    fin.close if fin 
end

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'  
    pst = con.prepare("INSERT INTO Images SET Data='%s'" % img.unpack('H*'))
    pst.execute    

rescue Mysql::Error => e
    puts e

ensure
    con.close if con
    pst.close if pst
end

在上面的脚本中,我们读取 JPG 图像并将其插入到Images表中。

fin = File.open("woman.jpg" , "rb")
img = fin.read

我们打开并阅读图像。 read方法以字符串形式返回数据。

pst = con.prepare("INSERT INTO Images SET Data='%s'" % img.unpack('H*'))

将该字符串数据放入预备语句中。 在这样做之前,使用 Ruby 字符串对象的unpack方法对其进行解码。 解码是必需的,因为图像对象具有许多无法正常处理的特殊字符。

读取图像

在前面的示例中,我们已将图像插入数据库表中。 现在,我们将从表中读取图像。

#!/usr/bin/ruby

require 'mysql'

begin

    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'          
    rs = con.query "SELECT Data FROM Images LIMIT 1"    
    f = File.new "woman2.jpg", "wb"
    f.write rs.fetch_row.pack 'H*'

rescue Mysql::Error, SystemCallError => e
    puts e

ensure
    con.close if con
    f.close if f
end

我们从图片表中读取了一张图片。

rs = con.query "SELECT Data FROM Images LIMIT 1" 

我们从表中选择一条记录。

f = File.new "woman2.jpg", "wb"

我们创建一个可写的二进制文件。

f.write rs.fetch_row.pack 'H*'

我们从先前的 SQL 语句中获取数据并将其写入文件。 fetch_row方法返回一个数组对象。 在将数据写入文件之前,使用数组的pack方法将其放回原始格式。 对于解码和反转这两种操作,我们使用相同的指令'H*'。 它代表十六进制字符串。

现在我们在当前目录中应该有一个名为woman2.jpg的映像。 我们可以检查它是否与我们插入表中的图像相同。

事务支持

transaction是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

默认情况下,MySQL 在自动提交模式下运行。 在这种模式下,对表的所有更改将立即生效。 为了防止这种情况,我们必须关闭自动提交模式。 禁用自动提交后,对事务感知表的更改不会立即永久更改。 要存储更改,我们必须调用COMMIT语句或ROLLBACK还原它们。 Ruby MySQL 为这些 SQL 语句commitrollback提供了便捷的方法。

MySQL 数据库具有不同类型的存储引擎。 最常见的是 MyISAM 和 InnoDB 引擎。 在数据安全性和数据库速度之间需要权衡。 MyISAM 表的处理速度更快,并且不支持事务。 commitrollback方法未实现。 他们什么都不做。 另一方面,InnoDB 表可以更安全地防止数据丢失。 他们支持事务。 它们处理较慢。

mysql> SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES 
    -> where TABLE_SCHEMA = 'mydb' AND TABLE_NAME='Writers';
+------------+--------+
| TABLE_NAME | ENGINE |
+------------+--------+
| Writers    | InnoDB |
+------------+--------+
1 row in set (0.00 sec)

Writers表的引擎是 InnoDB,它支持事务。

#!/usr/bin/ruby

require 'mysql'

begin
    con = Mysql.new 'localhost', 'user12', '34klq*', 'mydb'

    con.autocommit false

    pst = con.prepare "UPDATE Writers SET Name = ? WHERE Id = ?"
    pst.execute "Leo Tolstoy", "1"
    pst.execute "Boris Pasternak", "2"
    pst.execute "Leonid Leonov"

    con.commit

rescue Mysql::Error => e
    puts e
    con.rollback

ensure
    pst.close if pst
    con.close if con
end

在此脚本中,我们尝试更新三行。

con.autocommit false

自动提交模式被禁用。

pst = con.prepare "UPDATE Writers SET Name = ? WHERE Id = ?"
pst.execute "Leo Tolstoy", "1"
pst.execute "Boris Pasternak", "2"
pst.execute "Leonid Leonov"

我们执行三个UPDATE语句。 最后一个不正确。 第二个参数丢失。

con.commit 

如果一切正常,则更改将提交到表中。

rescue Mysql::Error => e
    puts e
    con.rollback

如果发生错误,则会回滚更改。

$ ./update.rb
execute: param_count(2) != number of argument(1)

mysql> SELECT * FROM Writers;
+----+-------------------+
| Id | Name              |
+----+-------------------+
|  1 | Jack London       |
|  2 | Honore de Balzac  |
|  3 | Lion Feuchtwanger |
|  4 | Emile Zola        |
|  5 | Truman Capote     |
|  6 | Stefan Zweig      |
+----+-------------------+
6 rows in set (0.00 sec)

运行脚本会出现错误。 但是,该事务已回滚,并且前两行未更改。

Tweet

这是 MySQL Ruby 教程。 您可能也对 PostgreSQL Ruby 教程SQLite Ruby 教程MongoDB Ruby 教程感兴趣。

MySQL C# 教程

原文: http://zetcode.com/db/mysqlcsharptutorial/

关于本教程

这是 MySQL 数据库的 C# 教程。 它涵盖了使用 C# 进行 MySQL 编程的基础。 在本教程中,我们使用 Connector/Net 驱动程序。 该驱动程序基于 ADO.NET 规范。 这些示例是在 Ubuntu Linux 上创建和测试的。 在 ZetCode 上有一个类似的 MySQL Visual Basic 教程

如果您需要重新学习 C# 语言的知识,请在 ZetCode 上找到完整的 C# 教程

关于 MySQL 数据库

MySQL 是领先的开源数据库管理系统。 它是一个多用户,多线程的数据库管理系统。 MySQL 在网络上特别流行。 它是由 Linux,Apache,MySQL 和 PHP 组成的非常流行的 LAMP 平台的一部分。 目前,MySQL 由 Oracle 拥有。 MySQL 数据库在最重要的 OS 平台上可用。 它可以在 BSD Unix,Linux,Windows 或 Mac OS 上运行。 维基百科和 YouTube 使用 MySQL。 这些站点每天管理数百万个查询。 MySQL 有两个版本:MySQL 服务器系统和 MySQL 嵌入式系统。

开始之前

在 Linux 上,我们需要安装几个包来执行本教程中的示例:libmysql6.1-cilmysql-servermysql-client。 我们还需要从 Mono 项目(从包或从源代码)安装 C# 编译器。

libmysql6.1-cil是 CLI 的 MySQL 数据库连接器。 它用 C# 编写,并且可用于所有 CLI 语言:C# ,Visual Basic,Boo 等。

$ ls /usr/lib/cli/MySql.Data-6.1/MySql.Data.dll 
/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll

从技术角度来看,我们需要一个 DLL。 在 Ubuntu Linux 上,它位于上述路径下。 我们需要知道 DLL 库的路径。 汇编我们的例子。

如果您尚未安装 MySQL,则必须安装它。

$ sudo apt-get install mysql-server

此命令将安装 MySQL 服务器和其他各种包。 在安装包时,提示我们输入 MySQL 根帐户的密码。 要从源代码安装 MySQL,请查看 MySQL 安装页面。

$ service mysql status
mysql start/running, process 1238

我们检查 MySQL 服务器是否正在运行。 如果没有,我们需要启动服务器。

$ sudo -b /usr/local/mysql/bin/mysqld_safe

上面的命令使用 MySQL 服务器启动脚本启动 MySQL 服务器。 我们启动 MySQL 服务器的方式可能有所不同。 这取决于我们是否从源代码或包安装了 MySQL,也取决于 Linux 发行版。 有关更多信息,请查阅 MySQL 的第一步或您的 Linux 发行版信息。

接下来,我们将创建一个新的数据库用户和一个新的数据库。 我们使用mysql客户端。

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 30
Server version: 5.0.67-0ubuntu6 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema | 
| mysql              | 
+--------------------+
2 rows in set (0.00 sec)

我们使用 mysql 监视器客户端应用连接到服务器。 我们使用根帐户连接到数据库。 我们用SHOW DATABASES语句显示所有可用的数据库。

mysql> CREATE DATABASE mydb;
Query OK, 1 row affected (0.02 sec)

我们创建一个新的mydb数据库。 在整个教程中,我们将使用此数据库。

mysql> CREATE USER user12@localhost IDENTIFIED BY '34klq*';
Query OK, 0 rows affected (0.00 sec)

mysql> USE mydb;
Database changed

mysql> GRANT ALL ON mydb.* to user12@localhost;
Query OK, 0 rows affected (0.00 sec)

mysql> quit;
Bye

我们创建一个新的数据库用户。 我们授予该用户mydb数据库所有表的所有特权。

定义

ADO.NET 是 .NET 框架的重要组成部分。 该规范统一了对关系数据库,XML 文件和其他应用数据的访问。MySQL Connector/Net 是 MySQL 数据库 ADO.NET 规范的实现。 它是用 C# 语言编写的驱动程序,可用于所有.NET 语言。

ConnectionCommandDataReaderDataSetDataProvider是 .NET 数据供应器模型的核心元素。 Connection创建到特定数据源的连接。 Command对象针对数据源执行一条 SQL 语句。 DataReader从数据源读取数据流。 DataSet对象用于脱机处理大量数据。 它是一种断开连接的数据表示形式,可以保存来自各种不同来源的数据。 DataReaderDataSet都用于处理数据。 它们在不同的情况下使用。 如果只需要读取查询结果,则DataReader是更好的选择。 如果需要更广泛的数据处理,或者要将 Winforms 控件绑定到数据库表,则首选DataSet

MySQL 版本

如果以下程序运行正常,则我们已安装一切正常。 我们检查 MySQL 服务器的版本。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;

        try 
        {
          conn = new MySqlConnection(cs);
          conn.Open();
          Console.WriteLine("MySQL version : {0}", conn.ServerVersion);

        } catch (MySqlException ex) 
        {
          Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {          
          if (conn != null) 
          {
              conn.Close();
          }
        }
    }
}

我们连接到数据库并获取有关 MySQL 服务器的一些信息。

using MySql.Data.MySqlClient; 

我们导入 MySQL 数据供应器的元素。

string cs = @"server=localhost;userid=user12;
    password=34klq*;database=mydb";

这是连接字符串。 数据提供者使用它来建立与数据库的连接。 我们指定主机名,用户名,密码和数据库名。

conn = new MySqlConnection(cs);

创建一个MySQLConnection对象。 该对象用于打开与数据库的连接。

conn.Open();

这行打开数据库连接。

Console.WriteLine("MySQL version : {0}", conn.ServerVersion);

在这里,我们使用连接对象的ServerVersion属性打印 MySQL 的版本。

} catch (MySqlException ex) 
{
  Console.WriteLine("Error: {0}",  ex.ToString());

如果发生异常,我们将错误消息打印到控制台。

} finally 
{  
  if (conn != null) 
  {
      conn.Close();
  }
}

在最后一步,我们关闭连接对象。

$ dmcs -r:/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll version.cs 

我们汇编示例。 提供了 MySQL 连接器 DLL 的路径。

$ ./version.exe 
MySQL version : 5.5.9

这是我系统上程序的输出。

接下来是一个更复杂的程序。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;

        try 
        {
          conn = new MySqlConnection(cs);
          conn.Open();

          string stm = "SELECT VERSION()";   
          MySqlCommand cmd = new MySqlCommand(stm, conn);
          string version = Convert.ToString(cmd.ExecuteScalar());
          Console.WriteLine("MySQL version : {0}", version);

        } catch (MySqlException ex) 
        {
          Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {

          if (conn != null) 
          {
              conn.Close();
          }

        }
    }
}

我们检查 MySQL 数据库的版本。 这次使用 SQL 查询。

string stm = "SELECT VERSION()";

这是 SQL SELECT语句。 它返回数据库的版本。 VERSION()是内置的 MySQL 函数。

MySqlCommand cmd = new MySqlCommand(stm, conn);

MySqlCommand是一个对象,用于在数据库上执行查询。 参数是 SQL 语句和连接对象。

string version = Convert.ToString(cmd.ExecuteScalar());

有些查询仅返回标量值。 在我们的例子中,我们需要一个简单的字符串来指定数据库的版本。 在这种情况下使用ExecuteScalar()。 我们避免了使用更复杂的对象的开销。

$ ./version2.exe 
MySQL version : 5.5.9

结果与前面的示例相同。

创建和填充表

接下来,我们将创建数据库表并用数据填充它们。 这些表将在本教程中使用。

DROP TABLE IF EXISTS Books, Authors;

CREATE TABLE IF NOT EXISTS Authors(Id INT PRIMARY KEY AUTO_INCREMENT, 
    Name VARCHAR(25)) ENGINE=INNODB;

INSERT INTO Authors(Id, Name) VALUES(1, 'Jack London');
INSERT INTO Authors(Id, Name) VALUES(2, 'Honore de Balzac');
INSERT INTO Authors(Id, Name) VALUES(3, 'Lion Feuchtwanger');
INSERT INTO Authors(Id, Name) VALUES(4, 'Emile Zola');
INSERT INTO Authors(Id, Name) VALUES(5, 'Truman Capote');

CREATE TABLE IF NOT EXISTS Books(Id INT PRIMARY KEY AUTO_INCREMENT, 
    AuthorId INT, Title VARCHAR(100), 
    FOREIGN KEY(AuthorId) REFERENCES Authors(Id) ON DELETE CASCADE)
    ENGINE=INNODB;

INSERT INTO Books(Id, AuthorId, Title) VALUES(1, 1, 'Call of the Wild');
INSERT INTO Books(Id, AuthorId, Title) VALUES(2, 1, 'Martin Eden');
INSERT INTO Books(Id, AuthorId, Title) VALUES(3, 2, 'Old Goriot');
INSERT INTO Books(Id, AuthorId, Title) VALUES(4, 2, 'Cousin Bette');
INSERT INTO Books(Id, AuthorId, Title) VALUES(5, 3, 'Jew Suess');
INSERT INTO Books(Id, AuthorId, Title) VALUES(6, 4, 'Nana');
INSERT INTO Books(Id, AuthorId, Title) VALUES(7, 4, 'The Belly of Paris');
INSERT INTO Books(Id, AuthorId, Title) VALUES(8, 5, 'In Cold blood');
INSERT INTO Books(Id, AuthorId, Title) VALUES(9, 5, 'Breakfast at Tiffany');

我们有一个books.sql文件。 它创建两个数据库表:AuthorsBooks。 这些表是 InnoDB 类型的。 InnoDB 数据库支持外键约束和事务。 我们将外键约束放在Books表的AuthorId列上。 我们用初始数据填充表。

mysql> source books.sql
Query OK, 0 rows affected (0.07 sec)
Query OK, 0 rows affected (0.12 sec)
Query OK, 1 row affected (0.04 sec)
...

我们使用source命令执行books.sql脚本。

预备语句

现在,我们将以预备语句来关注自己。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;

        try 
        {
          conn = new MySqlConnection(cs);
          conn.Open();

          MySqlCommand cmd = new MySqlCommand();
          cmd.Connection = conn;
          cmd.CommandText = "INSERT INTO Authors(Name) VALUES(@Name)";
          cmd.Prepare();

          cmd.Parameters.AddWithValue("@Name", "Trygve Gulbranssen");
          cmd.ExecuteNonQuery();

        } catch (MySqlException ex) 
        {
          Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {
            if (conn != null) {
                conn.Close();
            }

        }
    }
}

我们将新作者添加到Authors表中。 我们使用参数化命令。

cmd.CommandText = "INSERT INTO Authors(Name) VALUES(@Name)";
cmd.Prepare();

在这里,我们创建一个预备语句。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预备语句更快,并且可以防止 SQL 注入攻击。 @Name是一个占位符,稍后将填充。

cmd.Parameters.AddWithValue("@Name", "Trygve Gulbranssen");

值绑定到占位符。

cmd.ExecuteNonQuery();

执行预备语句。 当我们不希望返回任何数据时,我们使用MySQLCommand对象的ExecuteNonQuery()方法。 这是当我们创建数据库或执行INSERTUPDATEDELETE语句时。

$ ./prepared.exe 

mysql> SELECT * FROM Authors;
+----+--------------------+
| Id | Name               |
+----+--------------------+
|  1 | Jack London        |
|  2 | Honore de Balzac   |
|  3 | Lion Feuchtwanger  |
|  4 | Emile Zola         |
|  5 | Truman Capote      |
|  6 | Trygve Gulbranssen |
+----+--------------------+
6 rows in set (0.00 sec)

我们在表中插入了一位新作者。

使用MySqlDataReader检索数据

MySqlDataReader是用于从数据库检索数据的对象。 它提供对查询结果的快速,仅转发和只读访问。 这是从表中检索数据的最有效方法。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;
        MySqlDataReader rdr = null;

        try 
        {
            conn = new MySqlConnection(cs);
            conn.Open();

            string stm = "SELECT * FROM Authors";
            MySqlCommand cmd = new MySqlCommand(stm, conn);
            rdr = cmd.ExecuteReader();

            while (rdr.Read()) 
            {
                Console.WriteLine(rdr.GetInt32(0) + ": " 
                    + rdr.GetString(1));
            }

        } catch (MySqlException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {
            if (rdr != null) 
            {
                rdr.Close();
            }

            if (conn != null) 
            {
                conn.Close();
            }

        }
    }
}

我们从Authors表中获取所有作者并将其打印到控制台。

reader = cmd.ExecuteReader();

要创建MySQLDataReader,我们必须调用MySqlCommand对象的ExecuteReader()方法。

while (reader.Read()) 
{
    Console.WriteLine(reader.GetInt32(0) + ": " 
        + reader.GetString(1));
}

Read()方法将数据读取器移至下一条记录。 如果有更多行,则返回true;否则,返回true。 否则为假。 我们可以使用数组索引符号来检索值,或者使用特定的方法来访问其本机数据类型中的列值。 后者效率更高。

if (rdr != null) 
{
    rdr.Close();
}

阅读完毕后,请务必调用阅读器的Close()方法。

$ ./retrieve.exe 
1: Jack London
2: Honore de Balzac
3: Lion Feuchtwanger
4: Emile Zola
5: Truman Capote
6: Trygve Gulbranssen

这是示例的输出。

列标题

接下来,我们将展示如何使用数据库表中的数据打印列标题。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;
        MySqlDataReader rdr = null;

        try 
        {
            conn = new MySqlConnection(cs);
            conn.Open();

            string stm = @"SELECT Name, Title From Authors,
                Books WHERE Authors.Id=Books.AuthorId";

            MySqlCommand cmd = new MySqlCommand(stm, conn);
            rdr = cmd.ExecuteReader();

            Console.WriteLine("{0} {1}", rdr.GetName(0), 
                rdr.GetName(1).PadLeft(18));

            while (rdr.Read()) 
            {
                Console.WriteLine(rdr.GetString(0).PadRight(18) +
                    rdr.GetString(1));
            }

        } catch (MySqlException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {
            if (rdr != null) 
            {
                rdr.Close();
            }

            if (conn != null) 
            {
                conn.Close();
            }

        }
    }
}

在此程序中,我们从Authors表中选择作者,并从Books表中选择他们的书。

string stm = @"SELECT Name, Title From Authors,
    Books WHERE Authors.Id=Books.AuthorId";

这是将作者与他们的书联系在一起的 SQL 语句。

reader = cmd.ExecuteReader();

我们创建一个MySqlDataReader对象。

Console.WriteLine("{0} {1}", reader.GetName(0), 
    reader.GetName(1).PadLeft(18));

我们使用阅读器的GetName()方法获得列的名称。 PadLeft()方法返回指定长度的新字符串,其中当前字符串的开头用空格填充。 我们使用此方法正确对齐字符串。

while (reader.Read()) 
{
    Console.WriteLine(reader.GetString(0).PadRight(18) +
        reader.GetString(1));
}

我们将 SQL 语句返回的数据打印到终端。

$ ./headers.exe 
Name              Title
Jack London       Call of the Wild
Jack London       Martin Eden
Honore de Balzac  Old Goriot
Honore de Balzac  Cousin Bette
Lion Feuchtwanger Jew Suess
Emile Zola        Nana
Emile Zola        The Belly of Paris
Truman Capote     In Cold blood
Truman Capote     Breakfast at Tiffany

该程序的输出。

数据集& MySqlDataAdapter

DataSet是数据库表中数据的副本以及数据之间的关系。 它在内存中创建,并在需要对数据进行大量处理或将数据表绑定到 Winforms 控件时使用。 处理完成后,更改将被写入数据源。 MySqlDataAdapter是数据集和数据源之间的中介。 它填充DataSet并解析数据源的更新。

using System;
using System.Data;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;

        try 
        {
            conn = new MySqlConnection(cs);
            conn.Open();

            string stm = "SELECT * FROM Authors";
            MySqlDataAdapter da = new MySqlDataAdapter(stm, conn);

            DataSet ds = new DataSet();

            da.Fill(ds, "Authors");
            DataTable dt = ds.Tables["Authors"];

            dt.WriteXml("authors.xml");

            foreach (DataRow row in dt.Rows) 
            {            
                foreach (DataColumn col in dt.Columns) 
                {
                  Console.WriteLine(row[col]);
                }

                Console.WriteLine("".PadLeft(20, '='));
            }

        } catch (MySqlException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {          
            if (conn != null) 
            {
                conn.Close();
            }

        }
    }
}

我们从Authors表中打印作者。 我们还将它们保存在 XML 文件中。 这次,我们使用MySqlDataAdapterDataSet对象。

MySqlDataAdapter da = new MySqlDataAdapter(stm, conn);

创建一个MySqlDataAdapter对象。 它以 SQL 语句和连接为参数。

DataSet ds = new DataSet();

da.Fill(ds, "Authors");

我们创建并填充DataSet

DataTable dt = ds.Tables["Authors"];

我们得到名为Authors的表。 我们只给了DataSet一个表,但是它可以包含多个表。

dt.WriteXml("authors.xml");

我们将数据写入 XML 文件。

foreach (DataRow row in dt.Rows) 
{            
    foreach (DataColumn col in dt.Columns) 
    {
      Console.WriteLine(row[col]);
    }

    Console.WriteLine("".PadLeft(20, '='));
}

我们向终端显示Authors表的内容。 为了遍历数据,我们利用了DataTable对象的行和列。

在下一个示例中,我们将表绑定到 Winforms DataGrid控件。

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Data;
using MySql.Data.MySqlClient; 

class MForm : Form
{

    private DataGrid dg = null;
    private MySqlConnection conn = null;
    private MySqlDataAdapter da = null;        
    private DataSet ds = null;

    public MForm()
    {

       this.Text = "DataGrid";
       this.Size = new Size(350, 300);

       this.InitUI();
       this.InitData();

       this.CenterToScreen();
    }

    void InitUI()
    {    
        dg = new DataGrid();

        dg.CaptionBackColor = System.Drawing.Color.White;
        dg.CaptionForeColor = System.Drawing.Color.Black;
        dg.CaptionText = "Authors";

        dg.Location = new Point(8, 0);
        dg.Size = new Size(350, 300);
        dg.TabIndex = 0;
        dg.Parent = this;        
    }

    void InitData()
    {    
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        string stm = "SELECT * FROM Authors";

        try 
        {
            conn = new MySqlConnection(cs);          
            conn.Open();
            ds = new DataSet();
            da = new MySqlDataAdapter(stm, conn);
            da.Fill(ds, "Authors");  

            dg.DataSource = ds.Tables["Authors"];

        } catch (MySqlException ex) 
        {
            Console.WriteLine("Error: " + ex.ToString());

        } finally 
        {
            if (conn != null) 
            {
                conn.Close();
            }
        }    
    }
}

class MApplication 
{
    public static void Main() 
    {
        Application.Run(new MForm());
    }
}

在此示例中,我们将Authors表绑定到 Winforms DataGrid控件。

using System.Windows.Forms;
using System.Drawing;

这两个名称空间用于 GUI。

this.InitUI();
this.InitData();

InitUI()方法内部,我们构建了用户界面。 在InitData()方法中,我们连接到数据库,将数据检索到DataSet中并将其绑定到DataGrid控件。

dg = new DataGrid();

DataGrid控件已创建。

string stm = "SELECT * FROM Authors";

我们将在DataGrid控件中显示Authors表中的数据。

dg.DataSource = ds.Tables["Authors"];

我们将DataGrid控件的DataSource属性绑定到所选表。

$ dmcs -r:/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll -r:System.Windows.Forms.dll 
    -r:System.Drawing.dll -r:System.Data.dll dataadapter2.cs

要编译该示例,我们必须包括其他 DLL:用于 MySQL 连接器,WinformsDrawingData的 DLL。

DataGrid

图:DataGrid

事务支持

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

MySQL 数据库具有不同类型的存储引擎。 最常见的是 MyISAM 和 InnoDB 引擎。 在数据安全性和数据库速度之间需要权衡。 MyISAM 表的处理速度更快,并且不支持事务。 另一方面,InnoDB 表可以更安全地防止数据丢失。 他们支持事务。 它们处理较慢。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;
        MySqlTransaction tr = null; 

        try 
        {
            conn = new MySqlConnection(cs); 
            conn.Open();
            tr = conn.BeginTransaction();

            MySqlCommand cmd = new MySqlCommand();
            cmd.Connection = conn;
            cmd.Transaction = tr;

            cmd.CommandText = "UPDATE Authors SET Name='Leo Tolstoy' WHERE Id=1";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "UPDATE Books SET Title='War and Peace' WHERE Id=1";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
            cmd.ExecuteNonQuery();

            tr.Commit();

        } catch (MySqlException ex) 
        {
            try 
            { 
                tr.Rollback();                

            } catch (MySqlException ex1) 
            {
                Console.WriteLine("Error: {0}",  ex1.ToString());                
            }

            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {
           if (conn != null)
           {
              conn.Close();
           }
        }
    }
}

在此程序中,我们想在Authors表的第一行上更改作者的姓名。 我们还必须更改与该作者相关的书籍。 一个需要进行事务的很好的例子。 如果我们更改作者但不更改作者的书,则数据已损坏。

MySqlTransaction tr = null; 

MySqlTransaction是用于处理事务的对象。

tr = conn.BeginTransaction();

我们开始事务。

cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
cmd.ExecuteNonQuery();

第三个 SQL 语句有一个错误。 表中没有"Titl"列。

tr.Commit();

如果没有异常,则提交事务。

try 
{ 
    tr.Rollback();                

} catch (MySqlException ex1) 
{
    Console.WriteLine("Error: {0}",  ex1.ToString());                
}

发生异常时,事务将回滚。 没有更改提交到数据库。 在回滚期间,可能会出现Exception。 我们在单独的try/catch语句中对此进行处理。

$ ./transaction.exe 
Error: MySql.Data.MySqlClient.MySqlException: Unknown column 'Titl' in 'field list'
  at MySql.Data.MySqlClient.MySqlStream.ReadPacket () [0x00000] 
  at MySql.Data.MySqlClient.NativeDriver.ReadResult () [0x00000] 

mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name              | Title                |
+-------------------+----------------------+
| Jack London       | Call of the Wild     |
| Jack London       | Martin Eden          |
| Honore de Balzac  | Old Goriot           |
| Honore de Balzac  | Cousin Bette         |
| Lion Feuchtwanger | Jew Suess            |
| Emile Zola        | Nana                 |
| Emile Zola        | The Belly of Paris   |
| Truman Capote     | In Cold blood        |
| Truman Capote     | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.00 sec)

引发异常。 事务已回滚,并且未进行任何更改。

但是,如果没有事务,数据是不安全的。

using System;
using MySql.Data.MySqlClient; 

public class Example
{

    static void Main() 
    {
        string cs = @"server=localhost;userid=user12;
            password=34klq*;database=mydb";

        MySqlConnection conn = null;

        try 
        {
            conn = new MySqlConnection(cs); 
            conn.Open();

            MySqlCommand cmd = new MySqlCommand();
            cmd.Connection = conn;

            cmd.CommandText = "UPDATE Authors SET Name='Leo Tolstoy' WHERE Id=1";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "UPDATE Books SET Title='War and Peace' WHERE Id=1";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
            cmd.ExecuteNonQuery();

        } catch (MySqlException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally {
           if (conn != null)
           {
              conn.Close();
           }
        }
    }
}

我们有同样的例子。 这次,没有事务支持。

$ ./notransaction.exe 
Error: MySql.Data.MySqlClient.MySqlException: Unknown column 'Titl' in 'field list'
  at MySql.Data.MySqlClient.MySqlStream.ReadPacket () [0x00000] 
  at MySql.Data.MySqlClient.NativeDriver.ReadResult () [0x00000] 

mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name              | Title                |
+-------------------+----------------------+
| Leo Tolstoy       | War and Peace        |
| Leo Tolstoy       | Martin Eden          |
| Honore de Balzac  | Old Goriot           |
| Honore de Balzac  | Cousin Bette         |
| Lion Feuchtwanger | Jew Suess            |
| Emile Zola        | Nana                 |
| Emile Zola        | The Belly of Paris   |
| Truman Capote     | In Cold blood        |
| Truman Capote     | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.00 sec)

再次引发异常。 列夫·托尔斯泰没有写马丁·伊甸园。 数据已损坏。

Tweet

这是带有 MySQL 连接器的 MySQL C# 教程。 您可能也对 MySQL C API 教程MySQL Python 教程MySQL PHP 教程感兴趣。

SQLite 教程

原文: http://zetcode.com/db/sqlite/

这是 SQLite 教程。 它涵盖了 SQLite 数据库引擎,sqlite3 命令行工具以及数据库引擎涵盖的 SQL 语言。

目录

SQLite

SQLite 是嵌入式关系数据库引擎。 它的开发者称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。

Tweet

相关教程和电子书

SQLite Python 电子书是使用 Python 语言进行 SQLite 编程的深入材料。 MySQL 教程涵盖了 MySQL 数据库系统。 SQLAlchemy 教程涵盖了 SQLAlchemy SQL 工具包和对象关系映射器。 以下教程展示了如何从各种编程语言连接到 SQLite: SQLite C 教程SQLite Python 教程PHP SQLite3 教程SQLite Perl 教程SQLite Ruby 教程SQLite C# 教程SQLite Visual Basic 教程

SQLite 简介

原文: http://zetcode.com/db/sqlite/introduction/

这是 SQLite 教程。 它涵盖了 SQLite 数据库引擎,sqlite3命令行工具和数据库引擎涵盖的 SQL 语言。 它是初学者的入门教程。 它涵盖了 SQLite 3.16.2 版本。

SQLite 数据库

SQLite 是嵌入式关系数据库引擎。 它的开发者称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 它非常受欢迎,当今全球有数亿本使用。 SQLite 用于 Solaris 10 和 Mac OS 操作系统以及 iPhone 和 Skype。 Qt4 库具有对 SQLite 以及 Python 和 PHP 语言的内置支持。 许多流行的应用内部都使用 SQLite,例如 Firefox,Google Chrome 或 Amarok。

SQLite 实现了 SQL 的大多数 SQL-92 标准。 SQLite 引擎不是独立的进程。 而是将其静态或动态链接到应用。 SQLite 库很小。 它可能需要少于 300 KiB。 SQLite 数据库是单个普通磁盘文件,可以位于目录层次结构中的任何位置。 这是一个跨平台文件。 它可以在 32 位和 64 位架构的各种操作系统上使用。 SQLite 是用 C 编程语言编写的。 它具有许多语言的绑定,包括 C++ ,Java,C# ,Python,Perl,Ruby,Visual Basic 和 Tcl。 SQLite 的源代码在公共领域。

定义

关系数据库是表中组织的数据的集合。 表之间存在关系。 这些表是正式描述的。 它们由行和列组成。 _SQL(结构化查询语言)是一种数据库计算机语言,旨在管理关系数据库管理系统中的数据。 表是使用垂直列和水平行的模型组织的一组值。 列由其名称标识。 数据库系统的模式是用正式语言描述的结构。 它定义了表,字段,关系,视图,索引,过程,函数,队列,触发器和其他元素。 数据库行代表表中的单个隐式结构化数据项。 它也称为元组或记录。

列是一组特定简单类型的数据值,该数据值对应于表的每一行。 列提供了构成行所依据的结构。 字段是单个项目,存在于一行和一列之间的交点处。 主键唯一标识表中的每个记录。 外键是两个表之间的引用约束。 外键标识一个(引用)表中的一列或一组列,该列或表引用另一(引用)表中的一列或一组列。 触发器是响应于数据库中特定表上的某些事件而自动执行的过程代码。 视图是对来自一个或多个表的数据的特定外观。 它可以按特定顺序排列数据,突出显示或隐藏某些数据。 视图由存储的查询组成,该查询可作为由查询结果集组成的虚拟表访问。 与普通表不同,视图不构成物理模式的一部分。 它是根据数据库中的数据计算或整理的动态虚拟表。

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 SQL 结果集是SELECT语句返回的数据库中的一组行。 它还包含有关查询的元信息,例如列名以及每列的类型和大小。 索引是一种数据结构,可提高对数据库表的数据检索操作的速度。

从源代码安装 SQLite

为了获得最新版本的 SQLite,我们可以从源代码安装 SQLite。 安装很容易,并且只需要一段时间。 以下说明在 Linux 上构建和安装 SQLite。

$ sudo apt-get install libreadline-dev

要在 sqlite 命令行工具中启用命令历史记录,我们需要安装readline开发库。

$ wget https://www.sqlite.org/2017/sqlite-autoconf-3160200.tar.gz

从 SQLite 下载页面,我们可以获得最新资源。 这些是 SQLite 版本 3.16.2 的来源。

$ tar -xzvf sqlite-autoconf-3160200.tar.gz

我们解压缩压缩文件。

$ cd sqlite-autoconf-3160200/

我们转到sqlite-autoconf-3160200目录。

$ ./configure

我们运行configure脚本。 它告诉我们是否已经准备好构建 SQLite 的所有内容。

$ make

使用make,我们构建 SQLite。

$ sudo make install

使用sudo make install,我们在系统上安装了 SQLite。

$ which sqlite3
/usr/local/bin/sqlite3

默认情况下,SQLite 命令行工具安装在/usr/local/bin目录中。

$ sqlite3
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> 

/usr/local/bin目录位于PATH变量中,因此我们可以在没有完整路径的情况下运行sqlite3

使用的表

在这里,我们将列出整个教程中使用的最重要的表。 sqlite3工具的.read命令用于从文件执行 SQL 语句。

sqlite> .read cars.sql

在这里,我们执行cars.sql文件中的 SQL 语句。

cars.sql

-- SQL for the Cars table

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Cars;

CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);
INSERT INTO Cars VALUES(1, 'Audi', 52642);
INSERT INTO Cars VALUES(2, 'Mercedes', 57127);
INSERT INTO Cars VALUES(3, 'Skoda', 9000);
INSERT INTO Cars VALUES(4, 'Volvo', 29000);
INSERT INTO Cars VALUES(5, 'Bentley', 350000);
INSERT INTO Cars VALUES(6, 'Citroen', 21000);
INSERT INTO Cars VALUES(7, 'Hummer', 41400);
INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);
COMMIT;

这是Cars表。

orders.sql

-- SQL for the Orders table

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Orders;

CREATE TABLE Orders(Id INTEGER PRIMARY KEY, 
                 OrderPrice INTEGER CHECK(OrderPrice>0), Customer TEXT);
INSERT INTO Orders(OrderPrice, Customer) VALUES(1200, "Williamson");
INSERT INTO Orders(OrderPrice, Customer) VALUES(200, "Robertson");
INSERT INTO Orders(OrderPrice, Customer) VALUES(40, "Robertson");
INSERT INTO Orders(OrderPrice, Customer) VALUES(1640, "Smith");
INSERT INTO Orders(OrderPrice, Customer) VALUES(100, "Robertson");
INSERT INTO Orders(OrderPrice, Customer) VALUES(50, "Williamson");
INSERT INTO Orders(OrderPrice, Customer) VALUES(150, "Smith");
INSERT INTO Orders(OrderPrice, Customer) VALUES(250, "Smith");
INSERT INTO Orders(OrderPrice, Customer) VALUES(840, "Brown");
INSERT INTO Orders(OrderPrice, Customer) VALUES(440, "Black");
INSERT INTO Orders(OrderPrice, Customer) VALUES(20, "Brown");
COMMIT;

这是Orders表。

friends.sql

-- SQL for the Friends table

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Friends;

CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT UNIQUE NOT NULL, 
                     Sex TEXT CHECK(Sex IN ('M', 'F')));
INSERT INTO Friends VALUES(1, 'Jane', 'F');
INSERT INTO Friends VALUES(2, 'Thomas', 'M');
INSERT INTO Friends VALUES(3, 'Franklin', 'M');
INSERT INTO Friends VALUES(4, 'Elisabeth', 'F');
INSERT INTO Friends VALUES(5, 'Mary', 'F');
INSERT INTO Friends VALUES(6, 'Lucy', 'F');
INSERT INTO Friends VALUES(7, 'Jack', 'M');
COMMIT;

这是Friends表。

customers_reservations.sql

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Reservations;
DROP TABLE IF EXISTS Customers;

CREATE TABLE IF NOT EXISTS Customers(CustomerId INTEGER PRIMARY KEY, Name TEXT);
INSERT INTO Customers(Name) VALUES('Paul Novak');
INSERT INTO Customers(Name) VALUES('Terry Neils');
INSERT INTO Customers(Name) VALUES('Jack Fonda');
INSERT INTO Customers(Name) VALUES('Tom Willis');

CREATE TABLE IF NOT EXISTS Reservations(Id INTEGER PRIMARY KEY, 
    CustomerId INTEGER, Day TEXT);
INSERT INTO Reservations(CustomerId, Day) VALUES(1, '2009-22-11');
INSERT INTO Reservations(CustomerId, Day) VALUES(2, '2009-28-11');
INSERT INTO Reservations(CustomerId, Day) VALUES(2, '2009-29-11');
INSERT INTO Reservations(CustomerId, Day) VALUES(1, '2009-29-11');
INSERT INTO Reservations(CustomerId, Day) VALUES(3, '2009-02-12');
COMMIT;

这些是CustomersReservations表。

authors_books.sql

-- SQL for the Authors & Books tables

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Books;
DROP TABLE IF EXISTS Authors;

CREATE TABLE Authors(AuthorId INTEGER PRIMARY KEY, Name TEXT);
INSERT INTO Authors VALUES(1, 'Jane Austen');
INSERT INTO Authors VALUES(2, 'Leo Tolstoy');
INSERT INTO Authors VALUES(3, 'Joseph Heller');
INSERT INTO Authors VALUES(4, 'Charles Dickens');

CREATE TABLE Books(BookId INTEGER PRIMARY KEY, Title TEXT, AuthorId INTEGER, 
    FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId));
INSERT INTO Books VALUES(1,'Emma',1);
INSERT INTO Books VALUES(2,'War and Peace',2);
INSERT INTO Books VALUES(3,'Catch XII',3);
INSERT INTO Books VALUES(4,'David Copperfield',4);
INSERT INTO Books VALUES(5,'Good as Gold',3);
INSERT INTO Books VALUES(6,'Anna Karenia',2);
COMMIT;

这些是AuthorsBooks表。

数据来源

SQLite 的文档用于创建本教程。

本章是对 SQLite 数据库的介绍。

sqlite3 命令行工具

原文: http://zetcode.com/db/sqlite/tool/

在 SQLite 教程的这一部分中,我们介绍了sqlite3命令行工具。

sqlite3 工具

sqlite3工具是 SQLite 库的基于终端的前端,可以交互地求值查询并以多种格式显示结果。 它也可以在脚本中使用。

在终端屏幕上,我们看到sqlite3工具的以下提示:

$ sqlite3 
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

.help命令是sqlite3工具的元命令之一; 它列出了所有元命令。 .exit.quit命令退出sqlite3会话。 我们还可以使用Ctrl + D组合键退出sqlite3.databases命令显示附加的数据库。 .tables命令列出了可用的表。

Ctrl + L清除屏幕,Ctrl + U清除当前行。 (使用readline库构建时。)

使用 sqlite3 创建数据库

完整的 SQLite 数据库存储在单个跨平台磁盘文件中。 我们使用sqlite3命令行工具创建一个新的数据库文件。

$ sqlite3 test.db

在这里,我们创建一个新的test.db数据库。 如果该文件存在,则将其打开。

基本的 sqlite3 元命令

接下来,我们描述sqlite3工具的一些元命令。

sqlite> .tables
Authors       Cars          Friends       Reservations
Books         Customers     Orders 

.tables命令显示可用表。

sqlite> SELECT * FROM Friends;
1|Jane|F
2|Thomas|M
3|Franklin|M
4|Elisabeth|F
5|Mary|F
6|Lucy|F
7|Jack|M

在这里,我们得到SELECT语句的输出。 默认情况下,输出模式为line,分隔符为|

sqlite> .separator :
sqlite> SELECT * FROM Friends;
1:Jane:F
2:Thomas:M
3:Franklin:M
4:Elisabeth:F
5:Mary:F
6:Lucy:F
7:Jack:M

在这里,我们使用了一个新的冒号分隔符。

还有其他几种输出模式可用。 以下示例显示column输出模式。

sqlite> .mode column
sqlite> .headers on
sqlite> SELECT * FROM Friends;
Id          Name        Sex       
----------  ----------  ----------
1           Jane        F         
2           Thomas      M         
3           Franklin    M         
4           Elisabeth   F         
5           Mary        F         
6           Lucy        F         
7           Jack        M 

本示例说明如何在 sqlite 的列模式下格式化数据。 .headers命令也已用于显示列标题。 默认情况下,标题是隐藏的。

.width命令调整列的大小。 (此元命令仅与列模式下的表有关。)

sqlite> SELECT Name, Title FROM Authors NATURAL JOIN Books;
Name         Title     
-----------  ----------
Jane Austen  Emma      
Leo Tolstoy  War and Pe
Joseph Hell  Catch XII 
Charles Dic  David Copp
Joseph Hell  Good as Go
Leo Tolstoy  Anna Karen

列宽不足以正确显示所有数据。

sqlite> .width 15 18
sqlite> SELECT Name, Title FROM Authors NATURAL JOIN Books;
Name             Title             
---------------  ------------------
Jane Austen      Emma              
Leo Tolstoy      War and Peace     
Joseph Heller    Catch XII         
Charles Dickens  David Copperfield 
Joseph Heller    Good as Gold      
Leo Tolstoy      Anna Karenia 

在这里,我们更改列宽。 第一列为 15 个字符,第二列为 18 个字符。

sqlite> .shell clear

.shell命令执行系统程序。 在本例中,我们使用clear命令清除屏幕。 (Windows 上的等效值为cls。)

sqlite> .show
        echo: off
         eqp: off
     explain: auto
     headers: on
        mode: column
   nullvalue: ""
      output: stdout
colseparator: "|"
rowseparator: "\n"
       stats: off
       width: 15 18 
    filename: testdb

.show命令列出了各种设置。 其中包括输出模式,列表模式中使用的分隔符以及标题是否打开。

sqlite> .schema Cars
CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);

.schema命令显示表的结构。 它提供了 DDL SQL 来创建表。

可以使用.prompt命令更改sqlite3的提示。

sqlite> .prompt "> " ". "
> SELECT * FROM Cars
. LIMIT 2;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
> 

有两个提示。 一个是主提示,另一个是继续提示。 默认的主提示是sqlite>,默认的继续提示是...>

从 Shell 执行 SQL

我们可以从 Shell 执行 SQL 命令。

$ sqlite3 test.db "SELECT * FROM Cars;"
1|Audi|52642
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000
5|Bentley|350000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600  

在这里,我们非交互地执行了SELECT语句; 从Cars表中选择了所有汽车。

转储表

可以将 SQL 格式的表转储到磁盘上。 这样,我们可以轻松地保存数据库表的结构和数据。

我们有汽车表。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

我们将使用.dump命令转储该表。

sqlite> .dump Cars
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);
INSERT INTO "Cars" VALUES(1,'Audi',52642);
INSERT INTO "Cars" VALUES(2,'Mercedes',57127);
INSERT INTO "Cars" VALUES(3,'Skoda',9000);
INSERT INTO "Cars" VALUES(4,'Volvo',29000);
INSERT INTO "Cars" VALUES(5,'Bentley',350000);
INSERT INTO "Cars" VALUES(6,'Citroen',21000);
INSERT INTO "Cars" VALUES(7,'Hummer',41400);
INSERT INTO "Cars" VALUES(8,'Volkswagen',21600);
COMMIT;

.dump命令向我们显示了创建Cars表所需的 SQL。

sqlite> .output cars2.sql
sqlite> .dump Cars

我们还可以将输出重定向到文件。 .output命令会将输出重定向到cars2.sql文件。

$ cat cars2.sql 
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);
INSERT INTO "Cars" VALUES(1,'Audi',52642);
...

我们使用cat命令显示cars2.sql文件的内容。

读取 SQL

我们可以使用.read命令从文件名读取 SQL 语句。

sqlite> .tables
Authors       Cars          Friends       Reservations
Books         Customers     Orders  
sqlite> DROP TABLE Cars;
sqlite> .tables
Authors       Customers     Orders      
Books         Friends       Reservations
sqlite> .read cars.sql
sqlite> .tables
Authors       Cars          Friends       Reservations
Books         Customers     Orders     
sqlite> SELECT * FROM Cars WHERE Id=1;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642   

在这里,我们执行了一系列命令。 我们放下表并从cars.sql中读取它。

.sqlite_history文件

命令和语句存储在.sqlite_history文件中。 该文件位于主目录中。

$ tail -5 ~/.sqlite_history 
.tables
SELECT * FROM Cars;
.mode column
.headers on
.show

使用tail命令,显示最后五个条目。

资源文件

sqlite3工具具有一个名为.sqliterc的资源文件。 它位于主目录中。 如果没有这样的文件,我们可以简单地创建它。 资源文件可以包含元命令或常规 SQL 语句。 但是,我们应该避免在文件中使用 SQL。

$ cat .sqliterc 
.mode column
.headers on
.nullvalue NULL  

这是资源文件的简单示例。 它具有三个元命令。 使用资源文件,当我们启动sqlite3工具时,我们不必重新执行元命令。 它们将在工具启动时自动执行。

$ sqlite3 test.db 
-- Loading resources from /home/janbodnar/.sqliterc
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.

我们有一条消息说该工具从一开始就加载资源。

命令行选项

该工具有几个命​​令行选项。 他们大多复制元命令。 请注意,命令行选项将覆盖资源文件元命令。

$ sqlite3 --help
-- Loading resources from /home/janbodnar/.sqliterc
Usage: sqlite3 [OPTIONS] FILENAME [SQL]
FILENAME is the name of an SQLite database. A new database is created
if the file does not previously exist.
OPTIONS include:
   -ascii               set output mode to 'ascii'
   -bail                stop after hitting an error
   -batch               force batch I/O
   -column              set output mode to 'column'
   -cmd COMMAND         run "COMMAND" before reading stdin
   -csv                 set output mode to 'csv'
   -echo                print commands before execution
   -init FILENAME       read/process named file
   -[no]header          turn headers on or off
   -help                show this message
   -html                set output mode to HTML
   -interactive         force interactive I/O
   -line                set output mode to 'line'
   -list                set output mode to 'list'
   -lookaside SIZE N    use N entries of SZ bytes for lookaside memory
   -mmap N              default mmap size set to N
   -newline SEP         set output row separator. Default: '\n'
   -nullvalue TEXT      set text string for NULL values. Default ''
   -pagecache SIZE N    use N slots of SZ bytes each for page cache memory
   -scratch SIZE N      use N slots of SZ bytes each for scratch memory
   -separator SEP       set output column separator. Default: '|'
   -stats               print memory stats before each finalize
   -version             show SQLite version
   -vfs NAME            use NAME as the default VFS

--help选项为我们提供了所有可用选项的列表,并带有简要说明。

$ sqlite3 -echo -line -noheader test.db 
-- Loading resources from /home/janbodnar/.sqliterc
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
sqlite> SELECT * FROM Cars LIMIT 2;
SELECT * FROM Cars LIMIT 2;
   Id = 1
 Name = Audi
Price = 52642

   Id = 2
 Name = Mercedes
Price = 57127

我们使用-echo-line-noheader选项启动sqlite3工具。 SELECT语句在启动后会重复/回显。 输出处于行模式,并且没有标题显示。

$ sqlite3 -version
-- Loading resources from /home/janbodnar/.sqliterc
3.16.2 2017-01-06 16:32:41 a65a62893ca8319e89e48b8a38cf8a59c69a8209

使用-version选项,我们得到 sqlite3 的版本。

$ sqlite3 -html test.db
-- Loading resources from /home/janbodnar/.sqliterc
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
sqlite> SELECT * FROM Cars LIMIT 2;
<TR><TH>Id</TH>
<TH>Name</TH>
<TH>Price</TH>
</TR>
<TR><TD>1</TD>
<TD>Audi</TD>
<TD>52642</TD>
</TR>
<TR><TD>2</TD>
<TD>Mercedes</TD>
<TD>57127</TD>
</TR>

使用-html选项,我们可以将结果输出为简单的 HTML 表。

在 SQLite 教程的这一部分中,我们使用了sqlite3命令行工具。 我们已经描述了各种元命令,展示了如何转储表以及从文件中读取 SQL。 我们提到了 sqlite 的历史和资源文件。

MySQL 安装

原文: http://zetcode.com/databases/mysqltutorial/installation/

在 MySQL 教程的这一部分中,我们将介绍 MySQL 数据库管理系统的安装。 在本章中,我们将在 Linux 上安装 MySQL。

我们有几种方法可以在系统上安装 MySQL。 我们可以从包,二进制文件或源代码中安装 MySQL。

从包安装 MySQL

安装 MySQL 的最简单方法是通过包系统。

$ sudo apt-get install mysql-server

在 Ubuntu 和其他基于 Debian 的发行版上,我们可以使用apt-get工具轻松地从包中安装 MySQL。 此命令将安装 MySQL 服务器和其他各种包。 在安装包时,提示我们输入 MySQL 根帐户的密码。

$ sudo yum install mysql-server

在 CentOS 上,我们使用上述命令安装 MySQL 服务器。

从源代码安装 MySQL

从源代码安装 MySQL 为我们提供了根据我们的偏好构建 MySQL 的最多选择。 我们可以自定义安装位置,各种构建参数或编译器优化。

安装必要的工具

在开始构建 MySQL 之前,我们需要安装一些先决条件。

$ sudo apt-get install g++

如果不存在,我们必须安装 C++ 编译器。

$ sudo apt-get install libncurses5-dev

我们还需要 Curses 库的开发版本。

$ sudo apt-get install cmake bison
$ which cmake bison perl
/usr/bin/cmake
/usr/bin/bison
/usr/bin/perl

另外,我们需要在系统上安装以下三个工具:cmakebisonperl。 在我们的情况下,我们必须安装cmake工具。 cmake工具已取代configure工具,因为它更便于携带。

安装 Boost

我们需要安装 Boost C++ 库。 MySQL 5.7.17 需要 Boost 1.59.0。

$ wget http://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.tar.gz

我们下载 Boost 库的源代码。

$  tar xzvf boost_1_59_0.tar.gz 
$ cd boost_1_59_0/

我们解压缩档案并进入boost_1_59_0目录。

$ ./bootstrap.sh 
$ sudo ./b2 install

使用这两个命令,我们安装 Boost。

预配置设置

我们创建一个 mysql 组和用户。

$ sudo addgroup --system mysql
$ sudo adduser --system mysql --no-create-home -ingroup mysql

我们在计算机上创建一个 mysql 系统组和一个 mysql 系统用户。 Linux 中的每个进程均由特定用户拥有。 MySQL 守护程序将由用户 mysql 拥有。 注意,mysql 不是普通的用户帐户。 它是系统用户。

获取 MySQL 源

https://www.mysql.com/downloads/ 中,我们选择 MySQL Community Edition,然后选择 MySQL Community Server,以及一般可用的 MySQL Community Release。 从“选择平台”组合框中,选择“源代码”选项。 我们选择针对 64 位 Ubuntu Linux 的源。

$ wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-community-source_5.7.17-1ubuntu16.10_amd64.deb

确定存档的位置后,可以使用wget工具下载源。

$ ls -sh mysql-community-source_5.7.17-1ubuntu16.10_amd64.deb 
136M mysql-community-source_5.7.17-1ubuntu16.10_amd64.deb

我们已通过deb包下载了适用于 64 位架构的 Ubuntu Linux 的 MySQL 5.7.17 源。

$ md5sum mysql-community-source_5.7.17-1ubuntu16.10_amd64.deb

我们使用md5sum工具验证来源,并将生成的哈希值与网站上的哈希值进行比较。

$ expr 0b966bc6434d8a8020b9c4f32c93a1e7 == 0b966bc6434d8a8020b9c4f32c93a1e7
1

我们可以使用expr命令快速比较两个哈希。

$ sudo dpkg -i mysql-community-source_5.7.17-1ubuntu16.10_amd64.deb

我们安装deb包。 这些文件将安装到/usr/src/mysql目录。

$ mkdir build_mysql
$ cd build_mysql
$ cp /usr/src/mysql/mysql-community_5.7.17.orig.tar.gz .

我们创建一个构建目录,更改为该目录,然后将源代码复制到该目录中。

$ tar xzvf mysql-community_5.7.17.orig.tar.gz

现在我们解压缩源。

$ cd mysql-5.7.17/

我们转到mysql-5.7.17目录,这里有源。

$ ls
BUILD            Docs                 libmysqld    README         unittest
client           Doxyfile-perfschema  libservices  regex          VERSION
cmake            extra                man          scripts        vio
CMakeLists.txt   include              mysql-test   sql            win
cmd-line-utils   INSTALL              mysys        sql-common     zlib
config.h.cmake   libbinlogevents      mysys_ssl    storage
configure.cmake  libbinlogstandalone  packaging    strings
COPYING          libevent             plugin       support-files
dbug             libmysql             rapid        testclients

我们显示源目录。

$ cmake -L
-- Running cmake version 3.5.1
-- Could NOT find Git (missing:  GIT_EXECUTABLE) 
-- Configuring with MAX_INDEXES = 64U
-- SIZEOF_VOIDP 8
-- MySQL 5.7.17
-- Packaging as: mysql-5.7.17-Linux-x86_64
-- Found /usr/local/include/boost/version.hpp 
-- BOOST_VERSION_NUMBER is #define BOOST_VERSION 105900
-- BOOST_INCLUDE_DIR /usr/local/include
-- Found Curses: /usr/lib/x86_64-linux-gnu/libcurses.so  
-- Looking for tputs in /usr/lib/x86_64-linux-gnu/libcurses.so
-- Looking for tputs in /usr/lib/x86_64-linux-gnu/libcurses.so - found
-- Performing Test HAVE_DECL_TGOTO
-- Performing Test HAVE_DECL_TGOTO - Success
...

-L选项显示一些默认的配置选项。 该系统将被安装到/usr/loca/mysql目录。 对我们来说,重要的是要配置 InnoDB 存储引擎。

$ cmake .

我们配置构建。 我们保留所有默认设置。 如果我们还希望拥有 MySQL 嵌入式系统,我们将提供-DWITH_EMBEDDED_SERVER=1选项。

$ make
$ sudo make install

我们制作系统并安装它。

安装后的设置

在我们的系统上安装 MySQL 之后,需要其他步骤。

$ cd /usr/local/mysql
$ sudo chown -R mysql .
$ sudo chgrp -R mysql .

我们位于/usr/local/mysql目录中。 我们更改位于上述目录中所有文件的组和所有者。 -R选项表示递归操作。 这意味着这两个命令对所有文件和目录以及目录的内容起作用。

$ ls -l
total 56
drwxr-xr-x  2 mysql mysql  4096 Jan 26 15:54 bin
-rw-r--r--  1 mysql mysql 17987 Nov 28 14:32 COPYING
drwxr-xr-x  2 mysql mysql  4096 Jan 26 15:53 docs
drwxr-xr-x  3 mysql mysql  4096 Jan 26 15:53 include
drwxr-xr-x  4 mysql mysql  4096 Jan 26 15:54 lib
drwxr-xr-x  4 mysql mysql  4096 Jan 26 15:53 man
drwxr-xr-x 10 mysql mysql  4096 Jan 26 15:55 mysql-test
-rw-r--r--  1 mysql mysql  2478 Nov 28 14:32 README
drwxr-xr-x 28 mysql mysql  4096 Jan 26 15:55 share
drwxr-xr-x  2 mysql mysql  4096 Jan 26 15:55 support-files

我们更改了 MySQL 文件的所有者和组。

$ sudo bin/mysqld --initialize --user=mysql

我们使用mysqld初始化 MySQL 数据目录。 该命令还将创建一个临时根密码。 在 MySQL 5.7.6 之前,此任务是通过mysql_install_db command完成的。

$ sudo bin/mysql_ssl_rsa_setup

mysql_ssl_rsa_setup工具会创建 SSL 证书和键文件以及 RSA 键对文件,以支持使用 SSL 进行安全连接以及使用 RSA 通过未加密连接进行安全密码交换(如果这些文件丢失)。

$ sudo chown -R root .
$ sudo chown -R mysql data

我们将所有文件的所有者更改回用户根目录,但数据目录除外。 mysql用户拥有的 MySQL 服务器必须有权访问数据目录。 数据库文件存储在此目录中。

启动和停止 MySQL 服务器

以下命令可用于启动和停止 MySQL 服务器。

$ sudo /usr/local/mysql/support-files/mysql.server start

此命令启动 MySQL 服务器。

$ sudo /usr/local/mysql/support-files/mysql.server stop

该命令停止 MySQL 服务器。

其他设定

在系统上安装 MySQL 并更改root帐户的密码后,还有一些修改要做。

MySQL 有一个名为my.cnf的配置文件,该文件位于/etc目录中。 通过编辑此文件中的选项,我们可以根据需要配置服务器。

$ sudo cp /usr/local/mysql/support-files/my-default.cnf /etc/my.cnf
$ cp /usr/local/mysql/support-files/my-default.cnf ~/.my.cnf

support-files目录中有配置模板。 在第一个命令中,我们创建 MySQL 全局配置文件。 在第二个示例中,我们在用户的主目录中创建一个个人文件。

$ export PATH=$PATH:/usr/local/mysql/bin/
$ export MANPATH=$MANPATH:/usr/local/mysql/man/

另一有用的操作是将bin目录添加到PATH变量。 这样,我们可以启动 MySQL 命令和脚本而无需指定完整路径。 另外,我们将 MySQL 工具手册页面的路径和命令添加到MANPATH变量。 现在,我们可以使用man命令查看 MySQL 手册页。 将两个命令都放在您的 Shell 配置文件中。 这可以是.bashrc.profile

修改root密码

以前,我们已经获得了过期的root密码。 现在该为根设置新密码了。

$ /usr/local/mysql/bin/mysql -u root -p

我们启动mysql命令行工具。 (服务器必须正在运行。)我们以 root 用户身份连接。

mysql> SET PASSWORD = PASSWORD('newpassowrd');

我们设置了一个新密码。

加固 MySQL

我们可以使用mysql_secure_installation来提高 MySQL 服务器的安全性。

$ /usr/local/mysql/bin/mysql_secure_installation

我们可以选择改进 MySQL 的root密码,删除匿名用户帐户,禁用 localhost 外部的root登录以及删除测试数据库。

在 MySQL 教程的这一部分中,我们介绍了 MySQL 数据库系统的安装。

在 SQLite 中创建,删除和更改表

原文: http://zetcode.com/db/sqlite/tables/

在 SQLite 教程的这一部分中,我们将介绍 SQLite 数据库的数据定义语言(DDL)。 DDL 由定义数据库架构的 SQL 语句组成。 模式是以正式语言描述的数据库结构。 在关系数据库中,模式定义表,视图,索引,关系和触发器。

SQLite 支持以下三个 DDL 语句:

  • CREATE
  • ALTER
  • DROP

在 SQLite 中,CREATE语句用于创建表,索引,视图和触发器。 ALTER TABLE语句更改表的结构。 DROP语句删除表,索引,视图或触发器。

SQLite 创建表

CREATE语句用于创建表。 它还用于创建索引,视图和触发器。

要创建表,我们给表及其列命名。 每列可以具有以下数据类型之一:

  • NULL — 该值为NULL
  • INTEGER — 有符号整数
  • REAL — 浮点值
  • TEXT- 文本字符串
  • BLOB — 数据块
sqlite> CREATE TABLE Testing(Id INTEGER);
sqlite> .schema Testing
CREATE TABLE Testing(Id INTEGER);

我们使用CREATE TABLE语句创建一个简单的Testing表。 .schema命令显示该表的形式定义。

sqlite> CREATE TABLE Testing(Id INTEGER);
Error: table Testing already exists

如果我们尝试创建一个已经存在的表,则会出现错误。 因此,CREATE TABLE语句具有可选的IF NOT EXISTS子句。 使用此子句不会做任何事情,我们不会收到任何错误。

sqlite> CREATE TABLE IF NOT EXISTS Testing(Id INTEGER);

对于尝试创建一个已经存在的表,我们没有收到错误消息。

CREATE TABLE ... AS语句可基于SELECT语句创建新表。

sqlite> CREATE TABLE Cars2 AS SELECT * FROM Cars;

上面的语句使用特定的SELECT语句创建与Cars表相同的表。

sqlite> ATTACH DATABASE 'test2.db' AS test2;
sqlite> .databases
main: /home/janbodnar/tmp/test.db
test2: /home/janbodnar/tmp/test2.db

我们使用ATTACH DATABASE语句将新数据库添加到当前数据库连接中。 第一个数据库称为main,新数据库称为test2

sqlite> CREATE TABLE test2.Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);
sqlite> INSERT INTO test2.Cars VALUES(1, 'Porsche', 107699);
sqlite> SELECT * FROM main.Cars WHERE Id=1;
1           Audi        52642     
sqlite> SELECT * FROM test2.Cars WHERE Id=1;
1           Porsche     107699   

由于我们有两个数据库,因此如果要在test2数据库中创建表,则必须指定数据库名称。 数据库名称位于表名称之前。 在这种情况下,如果未指定数据库名称,则将自动选择main

sqlite> CREATE TEMPORARY TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);
sqlite> INSERT INTO temp.Cars VALUES (1, 'Kia', 24300);
sqlite> .databases
main: /home/janbodnar/tmp/test.db
temp: 
test2: /home/janbodnar/tmp/test2.db
sqlite> SELECT * FROM temp.Cars WHERE Id=1;
Id          Name        Price     
----------  ----------  ----------
1           Kia         24300     
sqlite> 

使用TEMPORARY关键字,我们创建一个临时数据库。 每次关闭数据库连接时,都会破坏一个临时数据库。 临时数据库的名称为temp

SQLite 删除表

DROP语句用于从数据库中删除表。

sqlite> .tables
Authors       Cars2         Orders        temp.Cars   
Books         Customers     Reservations  test2.Cars  
Cars          Friends       Testing     
sqlite> DROP TABLE Testing;
sqlite> .tables
Authors       Cars          Customers     Orders        temp.Cars   
Books         Cars2         Friends       Reservations  test2.Cars  

我们使用.tables命令显示可用表。 DROP TABLE语句从数据库中删除Testing表。

sqlite> DROP TABLE Testing;
Error: no such table: Testing

尝试删除不存在的表会导致错误。 使用IF EXISTS子句,我们可以避免此错误。

sqlite> DROP TABLE IF EXISTS Testing;

该语句仅在存在Testing表时才会删除。

sqlite> DROP TABLE IF EXISTS test2.Cars;

该 SQL 语句从test2数据库中删除Cars表。

SQLite ALTER TABLE

SQLite 支持ALTER TABLE语句的有限子集。 SQLite 中的此语句允许用户重命名表或向现有表添加新列。 无法重命名列,删除列或从表中添加或删除约束。

sqlite> CREATE TABLE Names(Id INTEGER, Name TEXT);

让我们创建一个要重命名的表Names

sqlite> ALTER TABLE Names RENAME TO NamesOfFriends;

我们将表重命名为NamesOfFriends

sqlite> .schema NamesOfFriends
CREATE TABLE "NamesOfFriends"(Id INTEGER, Name TEXT);

我们验证重命名表的架构。

假设我们要向表中添加一个新列。

sqlite> ALTER TABLE NamesOfFriends ADD COLUMN Email TEXT;

SQL 语句将名为Email的新列添加到表中。

sqlite> .schema NamesOfFriends
CREATE TABLE "NamesOfFriends"(Id INTEGER, Name TEXT, Email TEXT);

在这里,我们看到了表的新结构。

在 SQLite 教程的这一部分中,我们将创建,删除和更改表。

SQLite 表达式

原文: http://zetcode.com/db/sqlite/expressions/

在 SQLite 教程的这一部分中,我们介绍了 SQLite 运算符和表达式。

编程语言中的表达式是值,变量,运算符和函数的组合,这些值,变量,运算符和函数根据特定的优先级规则和特定编程语言的关联规则进行解释(求值),然后计算并生成(返回) 有状态的环境)的另一个值。 据说该表达式可以计算出该值。

字面值

字面值是某种常量。 字面值可以是整数,浮点数,字符串,BLOBNULL

sqlite> SELECT 3, 'Wolf', 34.5;
3|Wolf|34.5

在这里,我们返回三个字面值:即整数,字符串和浮点常量。

sqlite> .nullvalue NULL
sqlite> SELECT NULL;
NULL

.nullvalue命令告诉 SQLite 将NULL值显示为NULL。 默认情况下,SQLite 显示NULL值的空字符串。 NULL值也是字面值。

sqlite> SELECT quote(x'345eda2348587aeb');
X'345EDA2348587AEB'

BLOB字面值是包含十六进制数据并以单个"x""X"字符开头的字符串字面值。

运算符

运算符用于构建表达式。 SQL 运算符与数学运算符非常相似。 SQLite 支持一元和二进制运算符。 二元运算符使用两个操作数,一元运算符使用一个。 一个运算符可以有一个或两个操作数。 操作数是运算符的输入(参数)之一。

SQLite 支持五种主要的运算符:

  • 算术运算符
  • 布尔运算符
  • 关系运算符
  • 按位运算符
  • 其他运算符

SQLite 支持以下二进制运算符:

||
*    /    %
+    -
<<   >>   &    |
<    <=   >    >=
=    ==   !=   <>   IS  IS NOT  IN  LIKE  GLOB  BETWEEN  REGEXP
AND   
OR

运算符按照优先级排列。 ||运算符的优先级最高,OR运算符的优先级最低。

这些是一元前缀运算符:

-    +    ~    NOT

一元+运算符是空操作。 它什么也没做。 一元-运算符将正值更改为负值,反之亦然。

sqlite> SELECT -(3-44);
41

结果为 41。其他两个运算符将在后面讨论。

算术运算符

SQLite 理解的算术运算符是乘法,除法,加法,减法和模运算。

sqlite> SELECT 3*3/9;
1

这些是我们从数学中学到的乘法和除法运算符。

sqlite> SELECT 9/2;
4

与 C 语言类似,这是整数除法。

sqlite> SELECT 9/2.0;
4.5

为了获得浮点值,操作数之一必须是浮点数。

sqlite> .nullvalue NULL
sqlite> SELECT 9 / 0;
NULL

不允许被零除,表达式返回NULL

sqlite> SELECT 3 + 4 - 1 + 5;
11

我们显示加法和减法运算符。

sqlite> SELECT 11 % 3;
2

%运算符称为模运算符。 它找到一个数除以另一个的余数。 11 % 3的 11 模 3 为 2,因为 3 乘以 3 变成 11,余数为 2。

布尔运算符

使用布尔运算符,我们可以执行逻辑运算。 SQLite 具有三个布尔运算符:ANDORNOT。 布尔运算符返回truefalse。 在 SQLite 中,1 为true,0 为false

如果两个操作数均为true,则AND运算符的计算结果为true

sqlite> SELECT 0 AND 0, 0 AND 1, 1 AND 0, 1 AND 1;
0|0|0|1

前三个操作求值为 false,最后一个求值为 true。

sqlite> SELECT 3=3 AND 4=4;
1

两个操作数都为true,因此结果为true(1)。

如果至少一个操作数为true,则 OR 运算符的计算结果为 true。

sqlite> SELECT 0 OR 0, 0 OR 1, 1 OR 0, 1 OR 1;
0|1|1|1

第一个操作求值为false,其他操作求值为true

NOT运算符是一个否定运算符。 它使真假成为假。

sqlite> SELECT NOT 1, NOT 0;
0|1
sqlite> SELECT NOT (3=3);
0

关系运算符

关系运算符用于比较值。

符号 含义
< 严格小于
<= 小于或等于
> 比...更棒
>= 大于或等于
=== 等于
!=<> 不等于

这些运算符总是产生布尔值。

sqlite> SELECT 3*3 == 9, 9 = 9;
1|1

===都是相等运算符。

sqlite> SELECT 3 < 4, 3 <> 5, 4 >= 4, 5 != 5;
1|1|1|0

关系运算符的用法从数学上是已知的。

按位运算符

小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制或十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。 我们有二进制逻辑运算符和移位运算符。

按位且运算符在两个数字之间进行逐位比较。 仅当操作数中的两个对应位均为 1 时,位位置的结果才为 1。

    00110
  & 00011
  = 00010

第一个数字是二进制符号 6,第二个数字是 3,结果是 2。

sqlite> SELECT 6 & 3;
2
sqlite> SELECT 3 & 6;
2

按位或运算符在两个数字之间进行逐位比较。 如果操作数中的任何对应位为 1,则位位置的结果为 1。

     00110
  |  00011
   = 00111

结果为 00110 或十进制 7。

sqlite> SELECT 6 | 3;
7

按位移位运算符向右或向左移位。

number << n : multiply number 2 to the nth power
number >> n : divide number by 2 to the nth power

这些运算符也称为算术移位。

     00110
 >>  00001
   = 00011

我们将数字 6 的每个位向右移动。 等于将六除以 2。结果为 00011 或十进制 3。

sqlite> SELECT 6 >> 1;
3

     00110
  << 00001
   = 01100

我们将数字 6 的每个位向左移动。 等于将数字 6 乘以 2。结果为01100或十进制 12。

sqlite> SELECT 6 << 1;
12

按位取反运算符分别将 1 更改为 0,将 0 更改为 1。它也称为波浪运算符。

sqlite> SELECT ~7;
-8
sqlite> SELECT ~-8;
7

运算符将数字 7 的所有位取反。其中一位还确定数字是否为负。 如果我们再一次对所有位取反,我们将再次得到 7。

字符串连接

||运算符是字符串连接运算符。 它只是连接字符串。

sqlite> SELECT 'wolf' || 'hound';
wolfhound

我们添加两个字符串。

sqlite> SELECT 'star' || 3;
star3

可以连接字符串和数字。

IN运算符

INNOT IN运算符的左侧为表达式,右侧为值列表或子查询。 他们检查列表中是否存在值。

sqlite> SELECT 'Tom' IN ('Tom', 'Frank', 'Jane');
1

在这里,我们检查[Tom]是否在IN运算符后面的名称列表中。 返回值是一个布尔值。

对于以下示例,我们概括了Cars表中的内容。

sqlite> SELECT * FROM Cars;
1|Audi|52642
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000
5|Bentley|350000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600

IN运算符允许我们在WHERE子句中指定多个值。

sqlite> SELECT * FROM Cars WHERE Name IN ('Audi', 'Hummer');
1|Audi|52642
7|Hummer|41400

Cars表中,我们选择在IN运算符之后列出的汽车。

sqlite> SELECT * FROM Cars WHERE Name NOT IN ('Audi', 'Hummer');
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000
5|Bentley|350000
6|Citroen|21000
8|Volkswagen|21600

使用NOT IN运算符,我们可以进行反向操作:未列出的所有汽车名称。

sqlite> SELECT * FROM Cars WHERE Name IN (SELECT Name FROM Cars WHERE Price < 30000);
3|Skoda|9000
4|Volvo|29000
6|Citroen|21000
8|Volkswagen|21600

IN运算符的右侧可以是子查询。

LIKE运算符

WHERE子句中使用LIKE运算符在列中搜索指定的模式。LIKE模式中的百分号(%)与字符串中任何零个或多个字符的序列匹配。 模式中的下划线(_)与字符串中的任何单个字符匹配。

sqlite> SELECT * FROM Cars WHERE Name LIKE 'Vol%';
4|Volvo|29000
8|Volkswagen|21600

在这里,我们选择名称以"Vol"开头的汽车。 百分号(%)匹配任意数量的字符(包括零个字符)。

sqlite> SELECT * FROM Cars WHERE Name LIKE '____';
1|Audi|52642

下划线字符(_)匹配任何单个字符。 在这里,我们选择正好有四个字符的汽车名称; 有四个下划线。

sqlite> SELECT * FROM Cars WHERE Name LIKE '%EN';
6|Citroen|21000
8|Volkswagen|21600

默认情况下,LIKE运算符不区分大小写。

sqlite> PRAGMA case_sensitive_like = 1;
sqlite> SELECT * FROM Cars WHERE Name LIKE '%EN';

使用PRAGMA case_sensitive_like = 1语句,可以使其区分大小写。

GLOB运算符

GLOB运算符类似于LIKE,但它使用 Unix 文件通配符语法作为通配符。 此外,与LIKE(默认)不同,GLOB区分大小写。

*通配符与任意数量的任意字符匹配,包括空,?匹配任何单个字符。

sqlite> SELECT * FROM Cars WHERE Name GLOB '*en';
6|Citroen|21000
8|Volkswagen|21600

在这里,我们有一些车名以"en"字符结尾的汽车。

sqlite> SELECT * FROM Cars WHERE Name GLOB '????';
1|Audi|52642

在这里,我们选择正好有四个字符的汽车名称。

sqlite> SELECT * FROM Cars WHERE Name GLOB '*EN';
sqlite> SELECT * FROM Cars WHERE Name LIKE '%EN';
6|Citroen|21000
8|Volkswagen|21600

这两个语句表明LIKE不区分大小写,GLOB不区分大小写。

[abc]模式匹配括号中给定的一个字符。

sqlite> SELECT * FROM Cars WHERE Name GLOB '[VHS]*';
3|Skoda|9000
4|Volvo|29000
7|Hummer|41400
8|Volkswagen|21600

在示例中,我们选择所有名称以 V,H 或 S 字符开头的汽车。

BETWEEN运算符

BETWEEN运算符等效于一对比较。 BETWEEN b AND c等同于a>=b AND a<=c

sqlite> SELECT * FROM Cars WHERE Price BETWEEN 20000 AND 55000;
1|Audi|52642
4|Volvo|29000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600

在此 SQL 语句中,我们选择了价格在 20000 至 55000 单位之间的汽车。

sqlite> SELECT * FROM Cars WHERE Price > 20000 AND Price > 55000;
1|Audi|52642
4|Volvo|29000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600

此表达式与上一个表达式相同。

REGEXP运算符

对于REGEXP运算符,我们需要安装其他包。

$ sudo apt-get install sqlite3-pcre

我们安装sqlite3-pcre,它是 SQLite 的 Perl 兼容正则表达式库。

sqlite> .load /usr/lib/sqlite3/pcre.so

我们加载扩展库。

sqlite> SELECT * FROM Cars WHERE Name REGEXP '^.{5}$';
3|Skoda|9000
4|Volvo|29000

'^.{5}$'正则表达式查找正好包含五个字符的汽车名称。

ISIS NOT运算符

ISIS NOT运算符的工作方式类似于=!=,除非其中一个或两个操作数均为NULL时。

如果两个操作数均为NULL,则IS运算符的计算结果为 1(true),而IS NOT运算符的计算结果为 0(false)。 如果一个操作数是NULL,而另一个不是,则IS运算符的值为 0(假),IS NOT运算符的值为 1(真)。

sqlite> .nullvalue NULL
sqlite> SELECT NULL = 0;
NULL
sqlite> SELECT NULL IS 0;
0
sqlite> SELECT NULL IS NOT 0;
1

使用NULL值时,ISIS NOT运算符很有用。

CASE表达式

使用CASE WHEN ELSE可以创建条件表达式。 该表达式以END关键字结尾。 SQLite 中的CASE WHEN ELSE表达式类似于编程语言中的if-elseif-else表达式。

sqlite> CREATE TEMP TABLE Numbers(Val INTEGER);
sqlite> INSERT INTO Numbers VALUES (1), (-3), (3), (0), (-5), (6);

我们用一些整数值创建一个临时表。

sqlite> SELECT Val, CASE WHEN Val>0 THEN 'positive'
   ...> WHEN Val < 0 THEN 'negative'
   ...> ELSE 'zero' END FROM Numbers;
1|positive
-3|negative
3|positive
0|zero
-5|negative
6|positive

CASE WHEN ELSE表达式用于描述值。

优先级

运算符优先级的规则指定首先求值哪些运算符。 优先级对于避免表达式中的歧义是必要的。

以下表达式 28 或 40 的结果是什么?

3 + 5 * 5

像数学中一样,乘法运算符的优先级高于加法运算符。 结果是 28。

(3 + 5) * 5

要更改求值的顺序,可以使用括号。 括号内的表达式始终首先被求值。

sqlite> SELECT 3+5*5, (3+5)*5;
28|40

第一个表达式的计算值为 28,因为乘法运算符的优先级高于加法器。 在第二个示例中,我们使用括号来更改求值顺序。 因此第二个表达式的值为 40。

在这里,我们再次将操作符列表放到 SQLite 中。

unary + - ~ NOT
||
*    /    %
+    -
<<   <>   &    |
<    <=   >    >=
=    ==   !=   <>   IS  IS NOT  IN  LIKE  GLOB  BETWEEN  REGEXP
AND   
OR

同一行上的运算符具有相同的优先级。 优先级从低到高。

关联性

有时,优先级不足以确定表达式的结果。 第二组规则,称为关联性规则,确定具有相同优先级的运算符的求值顺序。

9 / 3 * 3

此表达式的结果是 9 还是 1? 乘法,删除和模运算符从左到右关联。 因此,该表达式的计算方式为:(9 / 3) * 3,结果为 9。

sqlite> SELECT 9 / 3 * 3;
9

关联规则是从左到右。

算术,布尔,关系和按位运算符都是从左到右关联的。

在 SQLite 教程的这一部分中,我们介绍了 SQLite 运算符和表达式。 我们已经在表达式中介绍了优先级和关联性规则。

SQLite 插入,更新,删除数据

原文: http://zetcode.com/db/sqlite/datamanipulation/

在 SQLite 教程的这一部分中,我们将插入,更新和删除 SQLite 表中的数据。 我们将使用INSERTDELETEUPDATE语句。 这些语句是 SQL 数据操作语言(DML)的一部分。

SQLite 插入数据

INSERT语句用于将数据插入表中。 我们将创建一个新表来执行示例。

sqlite> DROP TABLE IF EXISTS Cars;
sqlite> CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT,
   ...> Price INTEGER DEFAULT 'Not available');

我们使用IdNamePrice列创建一个新表Cars

sqlite> INSERT INTO Cars(Id, Name, Price) VALUES(1, 'Audi', 52642);

这是经典的INSERT语句。 我们在表名之后指定了所有列名,并在VALUES关键字之后指定了所有值。 第一行添加到表中。

sqlite> INSERT INTO Cars(Name, Price) VALUES('Mercedes', 57127);

我们将新车添加到Cars表中。 我们省略了Id列。 Id列定义为INTEGER PRIMARY KEY,并且这些列在 SQLite 中自动增加。 这意味着 SQLite 库本身将添加一个新的Id

sqlite> .headers on
sqlite> SELECT * FROM Cars;
Id|Name|Price
1|Audi|52642
2|Mercedes|57127

这是目前Cars表中的内容。

sqlite> INSERT INTO Cars VALUES(3, 'Skoda', 9000);

在此 SQL 语句中,我们没有在表名之后指定任何列名。 在这种情况下,我们必须提供所有值。

sqlite> .nullvalue NULL

.nullvalue命令告诉 SQLite 将NULL值显示为NULL。 默认情况下,SQLite 显示NULL值的空字符串。

sqlite> INSERT INTO Cars(Id) VALUES(4);

INSERT语句省略了最后两列。 这些列将填充默认值,如果没有默认值,则填充NULLName列没有默认值,因此有一个NULL值。 在CREATE TABLE语句中,我们已指定Price列具有"Not available"默认值。

sqlite> SELECT * FROM Cars WHERE Id=4;
Id|Name|Price
4|NULL|Not available

在第二列中,我们有一个NULL值。 第三个具有默认的"Not available"字符串。

sqlite> INSERT INTO Cars VALUES(4, 'Volvo', 29000);
Error: UNIQUE constraint failed: Cars.Id

假设我们想将所有信息放入第四列。 尝试将新数据插入现有行会产生以下错误:UNIQUE约束失败:Cars.Id

sqlite> INSERT OR REPLACE INTO Cars VALUES(4, 'Volvo', 29000);

在这种情况下,我们可以使用INSERT OR REPLACE语句。 使用UPDATE语句可以完成相同的操作。

sqlite> SELECT * FROM Cars WHERE Id=4;
Id|Name|Price
4|Volvo|29000 

现在,所有信息都在第四行。

sqlite> INSERT OR FAIL INTO Cars VALUES(4, 'Bentley', 350000);
Error: UNIQUE constraint failed: Cars.Id

INSET OR FAIL INTO语句等于INSERT INTO语句。 它只是更具体一点,即在发生错误时失败。

sqlite> INSERT OR IGNORE INTO Cars VALUES(4, 'Bentley', 350000);
sqlite> SELECT * FROM Cars WHERE Id=4;
Id|Name|Price
4|Volvo|29000

INSERT OR IGNORE INTO语句将忽略该错误消息。 SELECT语句显示最后两个语句没有修改第四行。

从 SQLite 3.7.11 版本开始,可以使用一个INSERT语句插入多行。

sqlite> CREATE TEMP TABLE Ints(Id INTEGER PRIMARY KEY, Val INTEGER);

我们将使用单列Ints表来显示多行INSERT语句。 该表的孤行列存储整数。

sqlite> INSERT INTO Ints(Val) VALUES (1), (3), (5), (6), (7), (8), (6), (4), (9);

我们一口气将九行插入表中。 这些行紧跟VALUES关键字,并以逗号分隔。

sqlite> SELECT * FROM Ints;
Id|Val
1|1
2|3
3|5
4|6
5|7
6|8
7|6
8|4
9|9

这些是Ints表的内容。

我们可以在一个语句中同时使用INSERTSELECT语句。

sqlite> CREATE TABLE Cars2(Id INTEGER PRIMARY KEY, Name TEXT, Price INTEGER);

首先,我们创建一个名为Cars2的新表。

sqlite> INSERT INTO Cars2 SELECT * FROM Cars;

在这里,我们将Cars表中的所有数据插入Cars2表中。

sqlite> SELECT * FROM Cars2;
Id|Name|Price
1|Audi|52642
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000

我们验证。

SQLite 删除数据

DELETE关键字用于从表中删除数据。 首先,我们将从表中删除一行。 我们将使用之前创建的Cars2表。

sqlite> DELETE FROM Cars2 WHERE Id=1;

我们用Id 1 删除一行。

sqlite> SELECT * FROM Cars2;
Id|Name|Price
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000

我们验证第一行是否丢失。

sqlite> DELETE FROM Cars2;

该 SQL 语句删除表中的所有数据。

sqlite> SELECT Count(Id) AS '# of cars' FROM Cars2;
# of cars      
---------------
0  

此 SQL 语句确认Cars2表中现在没有行。

sqlite> .read cars.sql
sqlite> SELECT * FROM Cars;
1|Audi|52642
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000
5|Bentley|350000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600

使用.read元命令,我们创建一个新的Cars表。 (表的 SQL 可以在本教程的第一章中找到。)

SQLite 更新数据

UPDATE语句用于修改存储在数据库表的零行或更多行中的值的子集。

假设我们想在Cars表中将'Skoda'更改为'Skoda Octavia'。 以下语句显示了如何完成此操作:

sqlite> .read cars.sql
sqlite> UPDATE Cars SET Name='Skoda Octavia' WHERE Id=3;

SQL 语句将Id=3列的汽车名称设置为'Skoda Octavia'。

sqlite> SELECT * FROM Cars WHERE Id=3;
3|Skoda Octavia|9000

该行已正确更新。

在 SQLite 教程的这一部分中,我们已经在数据库表中插入,删除和更新了数据。

SQLite SELECT语句

原文: http://zetcode.com/db/sqlite/select/

SQLite 教程的这一部分详细介绍了 SQLite SELECT语句的实现。

SQLite 检索所有数据

以下 SQL 语句是最常见的语句之一。 它也是最昂贵的之一。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

在这里,我们从Cars表中检索所有数据。

SQLite 选择特定列

我们可以使用SELECT语句来检索特定的列。 列名紧随SELECT字。

sqlite> SELECT Name, Price FROM Cars;
Name        Price     
----------  ----------
Audi        52642     
Mercedes    57127     
Skoda       9000      
Volvo       29000     
Bentley     350000    
Citroen     21000     
Hummer      41400     
Volkswagen  21600    

我们检索NamePrice列。 列名用逗号分隔。

SQLite 重命名列名

我们可以重命名返回结果集的列名。 为此,我们使用AS子句。

sqlite> SELECT Name, Price AS 'Price of car' FROM Cars;
Name        Price of car
----------  ------------
Audi        52642       
Mercedes    57127       
Skoda       9000        
Volvo       29000       
Bentley     350000      
Citroen     21000       
Hummer      41400       
Volkswagen  21600  

通过上面的 SQL 语句,我们将Price列重命名为Price of car

SQLite 限制数据输出

如上所述,在处理大量数据时,检索所有数据非常昂贵。 我们可以使用LIMIT子句来限制该语句返回的数据量。

sqlite> SELECT * FROM Cars LIMIT 4;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000    

LIMIT子句将返回的行数限制为 4。

sqlite> SELECT * FROM Cars LIMIT 2, 4;
Id          Name        Price     
----------  ----------  ----------
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000  

该语句选择四行,跳过前两行。

LIMIT之后的OFFSET子句指定在结果集的开头要跳过多少行。 这是上一个解决方案的替代方案。

sqlite> SELECT * FROM Cars LIMIT 4 OFFSET 2;
Id          Name        Price     
----------  ----------  ----------
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000  

在这里,我们从最多四行中选择所有数据,然后从第三行开始。 OFFSET子句跳过前两行。

SQLite 排序数据

我们使用ORDER BY子句对返回的数据集进行排序。 ORDER BY子句后面是进行排序的列。 ASC关键字以升序对数据进行排序,DESC则以降序对数据进行排序。

sqlite> SELECT * FROM Cars ORDER BY Price;
Id          Name        Price     
----------  ----------  ----------
3           Skoda       9000      
6           Citroen     21000     
8           Volkswagen  21600     
4           Volvo       29000     
7           Hummer      41400     
1           Audi        52642     
2           Mercedes    57127     
5           Bentley     350000  

默认排序为升序。 ASC子句可以省略。

sqlite> SELECT Name, Price FROM Cars ORDER BY Price DESC;
Name        Price     
----------  ----------
Bentley     350000    
Mercedes    57127     
Audi        52642     
Hummer      41400     
Volvo       29000     
Volkswagen  21600     
Citroen     21000     
Skoda       9000  

在上面的 SQL 语句中,我们从Cars表中选择NamePrice列,并按汽车的Price降序对其进行排序。 因此,最昂贵的汽车排在第一位。

SQLite 按更多列排序数据

可以按多于一列的顺序订购数据。

sqlite> INSERT INTO Cars(Name, Price) VALUES('Fiat', 9000);
sqlite> INSERT INTO Cars(Name, Price) VALUES('Tatra', 9000);

在此示例中,我们添加了两辆价格为 9000 的汽车。

sqlite> SELECT * FROM Cars ORDER BY Price, Name DESC;
Id          Name        Price     
----------  ----------  ----------
10          Tatra       9000      
3           Skoda       9000      
9           Fiat        9000      
6           Citroen     21000     
8           Volkswagen  21600     
4           Volvo       29000     
7           Hummer      41400     
1           Audi        52642     
2           Mercedes    57127     
5           Bentley     350000  

在语句中,我们按两列对数据进行排序:价格和名称。 名称按降序排列。

SQLite 使用WHERE选择特定行

下一组示例使用Orders表。

sqlite> SELECT * FROM Orders;
Id          OrderPrice  Customer  
----------  ----------  ----------
1           1200        Williamson
2           200         Robertson 
3           40          Robertson 
4           1640        Smith     
5           100         Robertson 
6           50          Williamson
7           150         Smith     
8           250         Smith     
9           840         Brown     
10          440         Black     
11          20          Brown  

在这里,我们看到Orders表中的所有数据。

接下来,我们要选择一个特定的行。

sqlite> SELECT * FROM Orders WHERE Id=6;
Id          OrderPrice  Customer  
----------  ----------  ----------
6           50          Williamson

上面的 SQL 语句选择具有 ID 6 的行。

sqlite> SELECT * FROM Orders WHERE Customer="Smith";
Id          OrderPrice  Customer  
----------  ----------  ----------
4           1640        Smith     
7           150         Smith     
8           250         Smith     

上面的 SQL 语句选择Smith客户的所有订单。

我们可以使用LIKE子句在数据中查找特定的模式。

sqlite> SELECT * FROM Orders WHERE Customer LIKE 'B%';
Id          OrderPrice  Customer  
----------  ----------  ----------
9           840         Brown     
10          440         Black     
11          20          Brown 

该 SQL 语句从名称以字母 B 开头的客户中选择所有订单。

SQLite 删除重复项

DISTINCT子句用于从结果集中仅选择唯一项。

sqlite> SELECT Customer FROM Orders WHERE Customer LIKE 'B%';
Customer  
----------
Brown     
Black     
Brown  

这次,我们选择了名称以 B 开头的客户。我们可以看到 Brown 出现了两次。 要删除重复项,我们使用DISTINCT关键字。

sqlite> SELECT DISTINCT Customer FROM Orders WHERE Customer LIKE 'B%';
Customer  
----------
Black     
Brown   

这是正确的解决方案。

SQLite 组数据

GROUP BY子句用于将具有相同值的数据库记录组合到单个记录中。 它通常与聚合函数一起使用。

假设我们想找出每个客户的订单总和。

sqlite> SELECT sum(OrderPrice) AS Total, Customer FROM Orders GROUP BY Customer;
Total       Customer  
----------  ----------
440         Black     
860         Brown     
340         Robertson 
2040        Smith     
1250        Williamson

sum()函数返回数字列的总和。 GROUP BY子句将总金额分配给客户。 因此,我们可以看到Black订购了 440,Smith订购了 2040 个商品。

使用聚合函数时,不能使用WHERE子句。 我们改用HAVING子句。

sqlite> SELECT sum(OrderPrice) AS Total, Customer FROM Orders 
        GROUP BY Customer HAVING sum(OrderPrice)>1000;
Total       Customer  
----------  ----------
2040        Smith     
1250        Williamson

上面的 SQL 语句选择总订单量大于 1000 个单位的客户。

在 SQLite 教程的这一部分中,我们更详细地描述了 SQL SELECT语句。

SQLite 约束

原文: http://zetcode.com/db/sqlite/constraints/

在 SQLite 教程的这一部分中,我们将处理约束。

约束被放置在列上。 它们限制了可以插入表中的数据。

在 SQLite 中,我们具有以下约束:

  • 非空
  • 唯一
  • 主键
  • 外键
  • 校验
  • 默认

SQLite NOT NULL约束

具有NOT NULL约束的列不能具有NULL值。

sqlite> CREATE TABLE People(Id INTEGER, LastName TEXT NOT NULL, 
   ...> FirstName TEXT NOT NULL, City TEXT);

我们创建两个具有NOT NULL约束的列。

sqlite> INSERT INTO People VALUES(1, 'Hanks', 'Robert', 'New York');
sqlite> INSERT INTO People VALUES(2, NULL, 'Marianne', 'Chicago');
Error: People.LastName may not be NULL

第一个INSERT语句成功,而第二个失败。 该错误表明LastName列可能不是NULL

SQLite UNIQUE约束

UNIQUE约束确保所有数据在列中都是唯一的。

sqlite> CREATE TABLE Brands(Id INTEGER, BrandName TEXT UNIQUE);

在这里,我们创建一个表BrandsBrandName列设置为UNIQUE。 不能有两个名称相同的品牌。

sqlite> INSERT INTO Brands VALUES(1, 'Coca Cola');
sqlite> INSERT INTO Brands VALUES(2, 'Pepsi');
sqlite> INSERT INTO Brands VALUES(3, 'Pepsi');
Error: column BrandName is not unique

我们收到错误消息column BrandName is not unique。 只能有一个百事可乐品牌。

注意,PRIMARY KEY约束自动在其上定义了UNIQUE约束。

SQLite 主键约束

PRIMARY KEY约束唯一地标识数据库表中的每个记录。 可以有更多UNIQUE列,但一个表中只有一个主键。 在设计数据库表时,主键很重要。 主键是唯一的 ID。 我们使用它们来引用表行。 在表之间创建关系时,主键成为其他表中的外键。 由于“长期的编码监督”,因此在 SQLite 中主键可以为NULL。 其他数据库则不是这种情况。

sqlite> DROP TABLE Brands;
sqlite> CREATE TABLE Brands(Id INTEGER PRIMARY KEY, BrandName TEXT);

Brands表的Id列变为PRIMARY KEY

sqlite> INSERT INTO Brands(BrandName) VALUES('Coca Cola');
sqlite> INSERT INTO Brands(BrandName) VALUES('Pepsi');
sqlite> INSERT INTO Brands(BrandName) VALUES('Sun');
sqlite> INSERT INTO Brands(BrandName) VALUES('Oracle');
sqlite> SELECT * FROM Brands;
Id          BrandName 
----------  ----------
1           Coca Cola 
2           Pepsi     
3           Sun       
4           Oracle   

在 SQLite 中,如果列为INTEGERPRIMARY KEY,则该列也会自动递增。

SQLite 外键约束

一个表中的FOREIGN KEY指向另一表中的PRIMARY KEY。 它是两个表之间的引用约束。 外键标识一个(引用)表中的一列或一组列,该列或表引用另一(引用)表中的一列或一组列。

SQLite 文档将引用表称为父表,并将引用表称为子表。 父键是外键约束所引用的父表中的一列或一组列。 这通常是(但并非总是)父表的主键。 子键是子表中受外键约束约束并包含REFERENCES子句的列或列集。

我们使用两个表来演示此约束:AuthorsBooks

-- SQL for the Authors & Books tables

BEGIN TRANSACTION;
DROP TABLE IF EXISTS Books;
DROP TABLE IF EXISTS Authors;

CREATE TABLE Authors(AuthorId INTEGER PRIMARY KEY, Name TEXT);
INSERT INTO Authors VALUES(1, 'Jane Austen');
INSERT INTO Authors VALUES(2, 'Leo Tolstoy');
INSERT INTO Authors VALUES(3, 'Joseph Heller');
INSERT INTO Authors VALUES(4, 'Charles Dickens');

CREATE TABLE Books(BookId INTEGER PRIMARY KEY, Title TEXT, AuthorId INTEGER, 
    FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId));
INSERT INTO Books VALUES(1,'Emma',1);
INSERT INTO Books VALUES(2,'War and Peace',2);
INSERT INTO Books VALUES(3,'Catch XII',3);
INSERT INTO Books VALUES(4,'David Copperfield',4);
INSERT INTO Books VALUES(5,'Good as Gold',3);
INSERT INTO Books VALUES(6,'Anna Karenia',2);
COMMIT;

这是用于创建BooksAuthors表的 SQL。 Books表的AuthorId列具有外键约束。 它引用Authors表的主键。

在 SQLite 中,默认情况下不强制使用外键。 要强制使用外键,必须使用适当的标志编译该库,该库必须至少为 3.6.19 版,并且必须设置外键的编译指示。

sqlite> PRAGMA foreign_keys=1;

外键通过PRAGMA语句强制执行。

sqlite> DELETE FROM Authors WHERE AuthorId=1;
Error: foreign key constraint failed

尝试删除仍在Books表中有书的作者会导致错误。 作者未被删除。

sqlite> DELETE FROM Books WHERE AuthorId=1;
sqlite> DELETE FROM Authors WHERE AuthorId=1;
sqlite> SELECT * FROM Authors;
AuthorId         Name              
---------------  ------------------
2                Leo Tolstoy       
3                Joseph Heller     
4                Charles Dickens 

为了删除作者,我们必须在Books表中删除他的书。

可以定义当必须强制执行外部约束时将采取什么措施。 默认操作为RESTRICT,这表示不允许删除或更新。

CREATE TABLE Books(BookId INTEGER PRIMARY KEY, Title TEXT, AuthorId INTEGER, 
    FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId) ON DELETE CASCADE);

我们修改Books表的架构,并在其中添加ON DELETE CASCADE操作。 此操作意味着该操作将从父表(Authors)传播到子表(Books)。

sqlite> SELECT Name, Title FROM Authors NATURAL JOIN Books;
Name             Title             
---------------  ------------------
Jane Austen      Emma              
Leo Tolstoy      War and Peace     
Joseph Heller    Catch XII         
Charles Dickens  David Copperfield 
Joseph Heller    Good as Gold      
Leo Tolstoy      Anna Karenia      
sqlite> DELETE FROM Authors WHERE AuthorId=2;
sqlite> SELECT Name, Title FROM Authors NATURAL JOIN Books;
Name             Title             
---------------  ------------------
Jane Austen      Emma              
Joseph Heller    Catch XII         
Charles Dickens  David Copperfield 
Joseph Heller    Good as Gold   

删除作者也会删除他的书。

SQLite CHECK约束

CHECK子句对关系数据库的数据施加了有效性约束。 在向相关列添加或更新数据时执行检查。

sqlite> .schema Orders
CREATE TABLE Orders(Id INTEGER PRIMARY KEY, OrderPrice INTEGER CHECK(OrderPrice>0), 
Customer TEXT);

我们看一下Orders表的定义。 我们看到OrderPrice列强加了CHECK约束。 自然,订单价格必须为正值。

sqlite> INSERT INTO Orders(OrderPrice, Customer) VALUES(-10, 'Johnson');
Error: constraint failed

如果尝试插入无效值,则会收到一条错误消息,提示constraint failed

SQLite 默认约束

如果没有可用值,则DEFAULT约束将默认值插入到列中。

sqlite> CREATE TABLE Hotels(Id INTEGER PRIMARY KEY, Name TEXT, 
   ...> City TEXT DEFAULT 'not available');

为了演示DEFAULT约束,我们创建了Hotels表。 City列具有默认的'not available'值。

sqlite> INSERT INTO Hotels(Name, City) VALUES('Kyjev', 'Bratislava');
sqlite> INSERT INTO Hotels(Name) VALUES('Slovan');
sqlite> .width 3 8 17
sqlite> SELECT * FROM Hotels;
Id   Name      City             
---  --------  -----------------
1    Kyjev     Bratislava       
2    Slovan    not available 

在第一个语句中,我们同时提供酒店名称和城市名称。 在第二个语句中,我们仅提供酒店名称。 SQLite 将默认值('not available'文本)放在此处。

在 SQLite 教程的这一部分中,我们介绍了 SQLite 数据库支持的约束。

SQLite 连接表

原文: http://zetcode.com/db/sqlite/joins/

在 SQLite 教程的这一部分中,我们将在 SQLite 中连接表。

关系数据库的真正力量和收益来自于连接表。 SQL JOIN子句合并了数据库中两个或多个表中的记录。 基本上有两种连接类型:INNEROUTER

在本教程的这一部分中,我们将使用CustomersReservations表。

sqlite> SELECT * FROM Customers;
CustomerId  Name       
----------  -----------
1           Paul Novak 
2           Terry Neils
3           Jack Fonda 
4           Tom Willis 

这些是Customers表中的值。

sqlite> SELECT * FROM Reservations;
Id  CustomerId  Day       
--  ----------  ----------
1   1           2009-22-11
2   2           2009-28-11
3   2           2009-29-11
4   1           2009-29-11
5   3           2009-02-12

这些是Reservations表中的值。

SQLite 内连接

内连接是最常见的连接类型。 这也是默认的连接。 内连接仅从数据库表中选择具有匹配值的记录。 我们有INNER JOINS的三种类型:INNER JOINNATURAL INNER JOINCROSS INNER JOININNER关键字可以省略。

内连接

sqlite> SELECT Name, Day FROM Customers AS C JOIN Reservations
   ...> AS R ON C.CustomerId=R.CustomerId;
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Paul Novak   2009-29-11 
Jack Fonda   2009-02-12 

在此SELECT语句中,我们选择了所有已进行预订的客户。 请注意,我们省略了INNER关键字。

该语句等效于以下语句:

sqlite> SELECT Name, Day FROM Customers, Reservations
   ...> WHERE Customers.CustomerId = Reservations.CustomerId;
Name        Day        
----------  -----------
Paul Novak  2009-22-11 
Terry Neil  2009-28-11 
Terry Neil  2009-29-11 
Paul Novak  2009-29-11 
Jack Fonda  2009-02-12

我们得到相同的数据。

自然内连接

NATURAL INNER JOIN自动使用所有匹配的列名进行连接。 在我们的表中,两个表中都有一个名为CustomerId的列。

sqlite> SELECT Name, Day FROM Customers NATURAL JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Paul Novak   2009-29-11
Jack Fonda   2009-02-12

我们得到相同的数据。 SQL 语句不太冗长。

交叉内连接

CROSS INNER JOIN将一个表中的所有记录与另一表中的所有记录组合在一起。 这种连接几乎没有实用价值。 它也称为记录的笛卡尔积。

sqlite> SELECT Name, Day FROM Customers CROSS JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Paul Novak   2009-28-11
Paul Novak   2009-29-11
Paul Novak   2009-29-11
Paul Novak   2009-02-12
Terry Neils  2009-22-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Terry Neils  2009-29-11
Terry Neils  2009-02-12
...

使用以下 SQL 语句可以达到相同的结果:

sqlite> SELECT Name, Day FROM Customers, Reservations;

SQLite 外连接

外连接不需要两个连接表中的每个记录都具有匹配的记录。 外连接共有三种类型:左外连接,右外连接和全外连接。 SQLite 仅支持左外连接。

左外连接

即使与右表不匹配,LEFT OUTER JOIN也将从左表返回所有值。 在此类行中,将有NULL值。 换句话说,左外连接返回左表中的所有值,以及右表中的匹配值。 注意,可以省略OUTER关键字。

sqlite> SELECT Name, Day FROM Customers LEFT JOIN Reservations
   ...> ON Customers.CustomerId = Reservations.CustomerId;
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Paul Novak   2009-29-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Jack Fonda   2009-02-12 
Tom Willis   NULL  

在这里,我们有所有保留客户的客户和没有保留客户的客户。 他的行中有一个NULL值。

我们可以使用USING关键字获得相同的结果。 SQL 语句将不再那么冗长。

sqlite> SELECT Name, Day FROM Customers LEFT JOIN Reservations
   ...> USING (CustomerId);
Name         Day        
-----------  -----------
Paul Novak   2009-22-11 
Paul Novak   2009-29-11 
Terry Neils  2009-28-11 
Terry Neils  2009-29-11 
Jack Fonda   2009-02-12 
Tom Willis   NULL

使用较短的 SQL 语句,我们得到的结果相同。

自然左外连接

NATURAL LEFT OUTER JOIN自动使用所有匹配的列名进行连接。

sqlite> SELECT Name, Day FROM Customers NATURAL LEFT OUTER JOIN Reservations;
Name         Day       
-----------  ----------
Paul Novak   2009-22-11
Paul Novak   2009-29-11
Terry Neils  2009-28-11
Terry Neils  2009-29-11
Jack Fonda   2009-02-12
Tom Willis   NULL  

我们具有相同的输出,但是使用的按键更少。

在 SQLite 教程的这一部分中,我们将连接表。

SQLite 函数

原文: http://zetcode.com/db/sqlite/sqlitefunctions/

在 SQLite 教程的这一部分中,我们将介绍 SQLite 内置函数。 SQLite 数据库中有三种类型的函数:核心函数,聚合函数和日期&时间函数。

我们将介绍每组 SQLite 函数中的一些函数。

SQLite 核心函数

在这一组中,我们具有各种函数。 有些是数字函数,有些是文本处理。 其他人则做一些非常具体的事情。

sqlite> SELECT sqlite_version() AS 'SQLite Version';
SQLite Version
--------------
3.16.2

sqlite_version()函数返回 SQLite 库的版本。

sqlite> SELECT random() AS Random;
Random             
-------------------
1056892254869386643   

random()函数返回 -9223372036854775808 和 +9223372036854775807 之间的伪随机整数。

sqlite> SELECT abs(11), abs(-5), abs(0), abs(NULL);
abs(11)             abs(-5)      abs(0)      abs(NULL) 
------------------  -----------  ----------  ----------
11                  5            0           NULL  

abs()函数返回数字参数的绝对值。

sqlite> SELECT max(Price), min(Price) FROM Cars;
max(Price)  min(Price)
----------  ----------
350000      9000  

在我们的示例中,max()min()函数从Cars表中返回最昂贵和最便宜的汽车。

sqlite> .width 18
sqlite> SELECT upper(Name) AS 'Names in capitals' FROM Friends;
Names in capitals 
------------------
JANE              
THOMAS            
FRANK             
ELISABETH         
MARY              
LUCY              
JACK  

upper()函数将字符转换为大写字母。

sqlite> SELECT lower(Name) AS 'Names in lowercase' FROM Friends
   ...> WHERE Id IN (1, 2, 3);
Names in lowercase
------------------
jane              
thomas            
frank  

使用lower()函数,我们将前三行的名称更改为小写字母。

sqlite> SELECT length('ZetCode');
length('ZetCode') 
------------------
7 

length()函数返回字符串的长度。

sqlite> SELECT total_changes() AS 'Total changes';
Total changes
-------------
3    

自打开当前数据库连接以来,total_changes()函数返回由INSERTUPDATEDELETE语句引起的行更改数。 在当前的数据库连接中,我们已经完成了三个INSERT语句,所以总更改等于三个。

sqlite> .width 5
sqlite> SELECT sqlite_compileoption_used('SQLITE_DEFAULT_FOREIGN_KEYS') AS 'FK';
FK   
-----
0  

sqlite_compileoption_used()函数返回一个布尔值,具体取决于在构建过程中是否使用了该选项。 在我们的例子中,我们检查默认情况下是否强制执行FOREIGN KEY约束。 该函数返回 0,这意味着默认情况下不强制执行约束。 我们使用PRAGMA语句进行更改。 (PRAGMA foreign_keys = 1;

sqlite> SELECT typeof(12), typeof('ZetCode'), typeof(33.2), typeof(NULL), 
   ...> typeof(x'345edb');
typeof(12)    typeof('ZetCode')   typeof(33.2)  typeof(NULL)  typeof(x'345edb')
------------  ------------------  ------------  ------------  -----------------
integer       text                real          null          blob  

typeof()函数返回参数的数据类型。

SQLite 聚合函数

通过聚合函数,我们可以获得一些统计数据。 带有单个参数的聚合函数可以在DISTINCT关键字之后。 在这种情况下,重复元素将在传递到聚合函数之前被过滤。

我们总结一下Cars表中的内容。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

请注意,没有重复的记录。

sqlite> SELECT count(*) AS '# of cars' FROM Cars;
# of cars 
----------
8     

count()函数返回表中的行数-共有八辆汽车。

Orders表中,我们确实有重复的客户记录。

sqlite> SELECT * FROM Orders;
Id          OrderPrice  Customer  
----------  ----------  ----------
1           1200        Williamson
2           200         Robertson 
3           40          Robertson 
4           1640        Smith     
5           100         Robertson 
6           50          Williamson
7           150         Smith     
8           250         Smith     
9           840         Brown     
10          440         Black     
11          20          Brown    

从逻辑上讲,每个客户可以下多个订单。 我们如何计算订单数量以及如何计算客户数量?

sqlite> SELECT count(Customer) AS '# of orders'  FROM Orders;
# of orders
-----------
11   

该 SQL 语句返回订单数。 要计算唯一客户的数量,我们必须利用DISTINCT子句。

sqlite> SELECT count(DISTINCT Customer) AS '# of customers' FROM Orders;
# of customers
--------------
5   

Orders表中有 5 个客户。 他们下了 11 个订单。

接下来,我们将演示count(*)count(ColumnName)函数之间的区别。 区别在于它们处理NULL值的方式。

sqlite> .nullvalue NULL

首先,我们更改sqlite3显示NULL值的方式。 默认情况下,NULL值显示为空字符串。

sqlite> CREATE TABLE TESTING(Id INTEGER);
sqlite> INSERT INTO Testing VALUES (1), (2), (3), (NULL), (NULL);

在这里,我们创建具有 3 个数字值和 2 个NULL值的表Testing

sqlite> SELECT last_insert_rowid();
5    

last_insert_rowid()函数返回最后插入的行的 ID。

sqlite> SELECT count(*) AS '# of rows' FROM Testing;
# of rows 
----------
5  

count(*)返回表中的行数。 它考虑了NULL值。

sqlite> SELECT count(Id) AS '# of non NULL values' FROM Testing;
# of non NULL values
--------------------
3 

count(Id)仅计算非NULL值。

sqlite> SELECT avg(Price) AS 'Average price' FROM Cars;
Average price
-------------
72721.125   

avg()函数返回所有非NULL记录的平均值。 在我们的示例中,我们在Cars表中显示了汽车的平均价格。

最后,我们提到sum()函数。 它将所有非NULL值相加。

sqlite> SELECT sum(OrderPrice) AS Sum FROM Orders;
Sum     
--------
4930   

在这里,我们计算客户下的订单数量。

SQLite 日期和时间函数

SQLite 具有处理日期和时间的函数。 这些函数采用各种时间字符串,修饰符和格式。

sqlite> .header OFF
sqlite> SELECT date('now');
2014-11-17    

带有now字符串的date()函数返回当前日期。

sqlite> SELECT datetime('now');
2018-07-20 09:57:38

datetime()函数返回当前日期和时间。

sqlite> SELECT time('now');
09:57:56

time()函数给出当前时间。

sqlite> SELECT time(), time('now');
09:58:30    09:58:30  
sqlite> SELECT date(), date('now');
2018-07-20  2018-07-20 

now字符串可以省略。

date()time()datetime()函数的第一个参数是时间字符串。 可以紧跟一个或多个修饰符。

sqlite> SELECT date('now', '2 months');
2018-09-20

在此示例中,'2 months'是修饰语。 它会将当前日期增加两个月。 因此该函数返回从今天起两个月的日期。

sqlite> SELECT date('now', '-55 days');
2018-05-26

也可以使用负修饰符。 在此示例中,我们从今天开始提取 55 天。

sqlite> SELECT date('now', 'start of year');
2018-01-01

使用start of year修饰符,我们可以得到年初的日期,例如 1 月 1 日。

sqlite> SELECT datetime('now', 'start of day');
2018-07-20 00:00:00 

借助start of day修饰符,我们可以了解当天的开始时间。

sqlite> SELECT date('now', 'weekday 6');
2018-07-21  

weekday修饰符前进到下一个日期,其中星期日是 0,星期一 1,...,星期六 6。在本示例中,我们获得最近的星期六的日期。

修饰符可以组合。

sqlite> SELECT date('now', 'start of year', '10 months', 'weekday 4');
2018-11-01 

该 SQL 语句返回当年 11 月的第一个星期四。 在此示例中,我们使用了三个修饰符:start of year+x monthsweekday xnow时间字符串给出当前日期。 start of year将日期向后移动到年初。 10 months在当月(1 月)增加了 10 个月。 最后,weekday 4修饰符将日期提前到第一个星期四。

strftime()函数返回根据指定为第一个参数的格式字符串格式化的日期和时间。 第二个参数是时间字符串。 可以紧跟一个或多个修饰符。

sqlite> SELECT strftime('%d-%m-%Y');
20-07-2018  

我们可以使用strftime()函数以其他格式返回日期。

sqlite> SELECT 'Current day: ' || strftime('%d');
Current day: 20   

该 SQL 语句返回每月的当前日期。 我们使用了strftime()函数。

sqlite> SELECT 'Days to XMas: ' || (strftime('%j', '2018-12-24') - strftime('%j', 'now'));
Days to XMas: 157

在这里,我们计算了到圣诞节为止的天数。 %j修饰符给出时间字符串的年份。

在 SQLite 教程的这一部分中,我们使用了内置的 SQLite 函数。

SQLite 视图,触发器,事务

原文: http://zetcode.com/db/sqlite/viewstriggerstransactions/

在 SQLite 教程的这一部分中,我们将提到视图,触发器和事务。

SQLite 视图

视图是对来自一个或多个表的数据的特定外观。 它可以按特定顺序排列数据,或突出显示或隐藏某些数据。 视图由存储的查询组成,该查询可作为由查询结果集组成的虚拟表访问。 与普通表不同,视图不构成物理模式的一部分。 它是根据数据库中的数据计算或整理的动态虚拟表。

在下一个示例中,我们创建一个简单的视图。

sqlite> SELECT * FROM Cars;
Id           Name         Price     
-----------  -----------  ----------
1            Audi         52642     
2            Mercedes     57127     
3            Skoda        9000      
4            Volvo        29000     
5            Bentley      350000    
6            Citroen      21000     
7            Hummer       41400     
8            Volkswagen   21600  

这是我们的数据,我们在该数据上创建视图。

sqlite> CREATE VIEW CheapCars AS SELECT Name FROM Cars WHERE Price < 30000;
sqlite> SELECT * FROM CheapCars;
Name       
-----------
Skoda      
Volvo      
Citroen    
Volkswagen 

CREATE VIEW语句用于创建视图。

sqlite> .tables
Books         CheapCars     Friends       Names         Reservations
Cars          Customers     Log           Orders        Testing     
sqlite> DROP VIEW CheapCars;
sqlite> .tables
Books         Customers     Log           Orders        Testing     
Cars          Friends       Names         Reservations

从技术上讲,视图是虚拟表。 因此,我们可以使用.tables命令列出所有视图。 要删除视图,我们使用DROP VIEW语句。

SQLite 触发器

触发器是在发生指定的数据库事件时自动执行的数据库操作。

在下面的示例中,我们将使用Friends表并创建一个新的Log表。

sqlite> CREATE TABLE Log(Id INTEGER PRIMARY KEY, OldName TEXT, 
   ...> NewName TEXT, Date TEXT);

Log表的一列中包含朋友的旧名称和新名称。 它还有一个时间戳列。

CREATE TRIGGER mytrigger UPDATE OF Name ON Friends
BEGIN
INSERT INTO Log(OldName, NewName, Date) VALUES (old.Name, new.Name, datetime('now'));
END;

我们使用CREATE TRIGGER语句创建一个名为mytrigger的触发器。 每当我们更新Friends表的Name列时,此触发器将启动INSERT语句。 INSERT语句会将旧名称,新名称和时间戳插入Log表中。 oldnew是对要修改的行的引用。

sqlite> SELECT name, tbl_name FROM sqlite_master WHERE type='trigger';
name         tbl_name   
-----------  -----------
mytrigger    Friends   

为了列出可用的触发器及其对应的表,我们查询sqlite_master表。

sqlite> SELECT * FROM Friends;
Id          Name        Sex       
----------  ----------  ----------
1           Jane        F         
2           Thomas      M         
3           Franklin    M         
4           Elisabeth   F         
5           Mary        F         
6           Lucy        F         
7           Jack        M  

这是我们的数据。

接下来,我们将更新Friends表的一行。

sqlite> UPDATE Friends SET Name='Frank' WHERE Id=3;

我们更新表的第三行。 触发器启动。

sqlite> SELECT * FROM Log;
Id           OldName      NewName     Date               
-----------  -----------  ----------  -------------------
1            Franklin     Frank       2014-11-18 10:58:46

我们检查Log表。 此日志确认我们执行的更新操作。

SQLite 事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

BEGIN TRANSACTION;
CREATE TABLE Test(Id INTEGER NOT NULL);
INSERT INTO Test VALUES(1);
INSERT INTO Test VALUES(2);
INSERT INTO Test VALUES(3);
INSERT INTO Test VALUES(NULL);
COMMIT;

在这里,我们有一个示例事务。 事务以BEGIN TRANSACTION开头,以COMMIT结束。

我们在 ID 列上设置了NOT NULL约束。 因此,第四次插入将不会成功。 SQLite 会逐案管理事务。 对于某些错误,它将还原所有更改。 对于其他用户,它仅还原最后一条语句,而其他更改保持不变。 在我们的情况下,将创建表并将前三个插入内容写入表中。 第四个不是。

说,我们已经有一个名为Test的空表。 执行上述事务将完全失败。 不会写入任何更改。 如果我们将CREATE TABLE语句更改为CREATE TABLE IF NOT EXISTS,,则前三个语句将执行。

BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS Test(Id INTEGER NOT NULL);
INSERT INTO Test VALUES(11);
INSERT INTO Test VALUES(12);
INSERT INTO Test VALUES(13);
INSERT INTO Test VALUES(NULL);
ROLLBACK;

事务可以以COMMITROLLBACK语句结束。 ROLLBACK恢复所有更改。

在 SQLite 教程的这一部分中,我们使用了 SQLite 中的视图,触发器和事务。

SQLite C 教程

原文: http://zetcode.com/db/sqlitec/

这是 SQLite 数据库的 C 编程教程。 它涵盖了使用 C 语言进行 SQLite 编程的基础。 您可能还想查看 ZetCode 上的 SQLite 教程MySQL C 教程PostgreSQL C 教程

SQLite 数据库

SQLite 是嵌入式关系数据库引擎。 它的开发者称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 它目前非常流行,当今全球使用着数亿册。 SQLite 在 Solaris 10,Mac OS,Android 或 iPhone 中使用。 Qt4 库内置了对 SQLite 以及 Python 和 PHP 的支持。 许多流行的应用内部都使用 SQLite,例如 Firefox,Google Chrome 或 Amarok。

sqlite3 工具

sqlite3 工具是 SQLite 库的基于终端的前端。 它以交互方式求值查询,并以多种格式显示结果。 它也可以在脚本中使用。 它具有自己的元命令集,包括.tables.load.databases.dump。 要获取所有指令的列表,我们键入.help命令。

现在,我们将使用sqlite3工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.8.2 2013-12-06 14:53:30
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

我们为sqlite3 tool提供了一个参数; test.db是数据库名称。 这是我们磁盘上的文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供test.db数据库中的表的列表。 当前没有表。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

C99

本教程使用 C99。 对于 GNU C 编译器,我们需要使用-std=c99选项。 对于 Windows 用户,强烈建议使用 Pelles C IDE。 (MSVC 不支持 C99。)

int rc = sqlite3_open("test.db", &db);

在 C99 中,我们可以将声明与代码混合使用。 在较早的 C 程序中,我们需要将这一行分成两行。

SQLite 版本

在第一个代码示例中,我们将获得 SQLite 数据库的版本。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    printf("%s\n", sqlite3_libversion()); 

    return 0;
}

sqlite3_libversion()函数返回指示 SQLite 库的字符串。

#include <sqlite3.h>

该头文件定义了 SQLite 库提供给客户端程序的接口。 它包含定义,函数原型和注释。 它是 SQLite API 的权威来源。

$ gcc -o version version.c -lsqlite3 -std=c99

我们使用 GNU C 编译器编译程序。

$ ./version
3.8.2

这是示例的输出。

在第二个示例中,我们再次获得 SQLite 数据库的版本。 这次我们将使用 SQL 查询。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    sqlite3_stmt *res;

    int rc = sqlite3_open(":memory:", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &res, 0);    

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }    

    rc = sqlite3_step(res);

    if (rc == SQLITE_ROW) {
        printf("%s\n", sqlite3_column_text(res, 0));
    }

    sqlite3_finalize(res);
    sqlite3_close(db);

    return 0;
}

SQLITE_VERSION()查询用于获取 SQLite 库的版本。

sqlite3 *db;

sqlite3结构定义数据库句柄。 每个打开的 SQLite 数据库均由数据库句柄表示。

sqlite3_stmt *res;

sqlite3_stmt结构表示单个 SQL 语句。

int rc = sqlite3_open(":memory:", &db);

sqlite3_open()函数打开一个新的数据库连接。 它的参数是数据库名称和数据库句柄。 :memory:是一个特殊的数据库名称,使用它可以打开内存数据库。 该函数的返回代码指示数据库是否已成功打开。 成功建立连接后,将返回SQLITE_OK

if (rc != SQLITE_OK) {

    fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);

    return 1;
}

如果返回代码指示错误,我们将消息打印到控制台,关闭数据库句柄,然后终止程序。 sqlite3_errmsg()函数返回错误的描述。 无论打开时是否发生错误,都应通过将其传递给sqlite3_close()函数来释放与数据库连接句柄关联的资源。

rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &res, 0);

在执行 SQL 语句之前,必须先使用sqlite3_prepare*函数之一将其编译为字节码。 (不推荐使用sqlite3_prepare()函数。)

sqlite3_prepare_v2()函数具有五个参数。 第一个参数是从sqlite3_open()函数获得的数据库句柄。 第二个参数是要编译的 SQL 语句。 第三个参数是 SQL 语句的最大长度,以字节为单位。 传递 -1 将导致 SQL 字符串被读取到第一个零终止符,即第一个零终止符。 根据文档,通过传递所提供的 SQL 字符串的确切字节数,可以获得一些小的性能优势。 第四个参数是语句句柄。 如果sqlite3_prepare_v2()成功运行,它将指向预编译的语句。 最后一个参数是指向 SQL 语句未使用部分的指针。 只编译 SQL 字符串的第一条语句,因此该参数指向未编译的内容。 我们传递 0,因为参数对我们而言并不重要。

成功时,sqlite3_prepare_v2()返回SQLITE_OK; 否则返回错误代码。

if (rc != SQLITE_OK) {

    fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);

    return 1;
}    

这是sqlite3_prepare_v2()函数调用的错误处理代码。

rc = sqlite3_step(res);

sqlite3_step()运行 SQL 语句。 SQLITE_ROW返回码表示还有另一行准备就绪。 我们的 SQL 语句仅返回一行数据,因此,我们只调用一次此函数。

sqlite3_finalize(res);

sqlite3_finalize()函数破坏预备语句对象。

sqlite3_close(db);

sqlite3_close()函数关闭数据库连接。

插入数据

我们创建一个Cars表并在其中插入几行。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "DROP TABLE IF EXISTS Cars;" 
                "CREATE TABLE Cars(Id INT, Name TEXT, Price INT);" 
                "INSERT INTO Cars VALUES(1, 'Audi', 52642);" 
                "INSERT INTO Cars VALUES(2, 'Mercedes', 57127);" 
                "INSERT INTO Cars VALUES(3, 'Skoda', 9000);" 
                "INSERT INTO Cars VALUES(4, 'Volvo', 29000);" 
                "INSERT INTO Cars VALUES(5, 'Bentley', 350000);" 
                "INSERT INTO Cars VALUES(6, 'Citroen', 21000);" 
                "INSERT INTO Cars VALUES(7, 'Hummer', 41400);" 
                "INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);        
        sqlite3_close(db);

        return 1;
    } 

    sqlite3_close(db);

    return 0;
}

我们连接到test.db数据库,创建Cars表,然后在创建的表中插入 8 行。

char *err_msg = 0;

如果发生错误,该指针将指向创建的错误消息。

int rc = sqlite3_open("test.db", &db);

test.db数据库的连接已创建。

char *sql = "DROP TABLE IF EXISTS Cars;" 
            "CREATE TABLE Cars(Id INT, Name TEXT, Price INT);" 
            "INSERT INTO Cars VALUES(1, 'Audi', 52642);" 
            "INSERT INTO Cars VALUES(2, 'Mercedes', 57127);" 
            "INSERT INTO Cars VALUES(3, 'Skoda', 9000);" 
            "INSERT INTO Cars VALUES(4, 'Volvo', 29000);" 
            "INSERT INTO Cars VALUES(5, 'Bentley', 350000);" 
            "INSERT INTO Cars VALUES(6, 'Citroen', 21000);" 
            "INSERT INTO Cars VALUES(7, 'Hummer', 41400);" 
            "INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);";

这些 SQL 语句创建一个Cars表并用数据填充它。 语句必须用分号分隔。

rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

sqlite3_exec()函数是sqlite3_prepare_v2()sqlite3_step()sqlite3_finalize()的便利包装,它允许应用运行多个 SQL 语句而无需使用大量 C 代码。

该函数的第三个参数是为从求值的 SQL 语句中出来的每个结果行调用的回调函数。 第四个参数是回调函数的第一个参数。 如果不需要它们,可以将 0 传递给这些参数。

如果发生错误,则最后一个参数指向分配的错误消息。

sqlite3_free(err_msg);

必须使用sqlite3_free()函数调用释放分配的消息字符串。

sqlite> .mode column  
sqlite> .headers on

我们使用sqlite3工具验证写入的数据。 首先,我们修改数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

这是我们已写入Cars表的数据。

最后插入的行 ID

有时,我们需要确定最后插入的行的 ID。 为此,我们具有sqlite3_last_insert_rowid()函数。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open(":memory:", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
    "INSERT INTO Friends(Name) VALUES ('Tom');"
    "INSERT INTO Friends(Name) VALUES ('Rebecca');"
    "INSERT INTO Friends(Name) VALUES ('Jim');"
    "INSERT INTO Friends(Name) VALUES ('Roger');"
    "INSERT INTO Friends(Name) VALUES ('Robert');";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "Failed to create table\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);

    } else {

        fprintf(stdout, "Table Friends created successfully\n");
    }

    int last_id = sqlite3_last_insert_rowid(db);
    printf("The last Id of the inserted row is %d\n", last_id);

    sqlite3_close(db);

    return 0;
}

在内存中创建一个Friends表。 其Id列会自动增加。

char *sql = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friends(Name) VALUES ('Roger');"
"INSERT INTO Friends(Name) VALUES ('Robert');";

在 SQLite 中,INTEGER PRIMARY KEY列自动增加。 还有一个AUTOINCREMENT关键字。 当在INTEGER PRIMARY KEY AUTOINCREMENT中应用时,会使用稍微不同的 ID 创建算法。

使用自动递增的列时,除了自动递增的列(省略了该列)外,我们需要明确声明列名。

int last_id = sqlite3_last_insert_rowid(db);
printf("The last Id of the inserted row is %d\n", last_id);

sqlite3_last_insert_rowid()返回表中最近一次成功插入的行 ID。

$ ./last_row_id 
Table Friends created successfully
The last Id of the inserted row is 5

我们看到了程序的输出。

检索数据

我们已经将一些数据插入test.db数据库。 在下面的示例中,我们从数据库中检索数据。

#include <sqlite3.h>
#include <stdio.h>

int callback(void *, int, char **, char **);

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", 
                sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "SELECT * FROM Cars";

    rc = sqlite3_exec(db, sql, callback, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);
        sqlite3_close(db);

        return 1;
    } 

    sqlite3_close(db);

    return 0;
}

int callback(void *NotUsed, int argc, char **argv, 
                    char **azColName) {

    NotUsed = 0;

    for (int i = 0; i < argc; i++) {

        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }

    printf("\n");

    return 0;
}

我们使用SELECT * FROM Cars SQL 语句从Cars表中检索所有行。

int callback(void *, int, char **, char **);

这是与sqlite3_exec()函数结合使用的回调函数的函数原型。

int rc = sqlite3_open("test.db", &db);

我们连接到test.db数据库。

char *sql = "SELECT * FROM Cars";

在这里,我们定义 SQL 语句以从Cars表中选择所有数据。

rc = sqlite3_exec(db, sql, callback, 0, &err_msg);

sqlite3_exec()函数对 SQL 语句进行赋值。 对于从求值的 SQL 语句出来的每个结果行,都会调用其回调函数。

int callback(void *NotUsed, int argc, char **argv, 
                    char **azColName) {

    NotUsed = 0;

    for (int i = 0; i < argc; i++) {

        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }

    printf("\n");

    return 0;
}

回调函数的第一个参数是sqlite3_exec()的第 4 个参数中提供的数据; 它通常不被使用。 第二个参数是结果中的列数。 第三个参数是表示行中字段的字符串数组。 最后一个参数是代表列名的字符串数组。

在函数主体中,我们遍历所有列并打印其名称和内容。

$ ./select_all 
Id = 1
Name = Audi
Price = 52642

Id = 2
Name = Mercedes
Price = 57127

Id = 3
Name = Skoda
Price = 9000
...

这是示例的部分输出。

参数化查询

现在我们将提到参数化查询。 参数化查询(也称为预准备语句)可提高安全性和性能。 当使用参数化查询时,我们使用占位符,而不是直接将值写入语句。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;
    sqlite3_stmt *res;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "SELECT Id, Name FROM Cars WHERE Id = ?";

    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);

    if (rc == SQLITE_OK) {

        sqlite3_bind_int(res, 1, 3);
    } else {

        fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
    }

    int step = sqlite3_step(res);

    if (step == SQLITE_ROW) {

        printf("%s: ", sqlite3_column_text(res, 0));
        printf("%s\n", sqlite3_column_text(res, 1));

    } 

    sqlite3_finalize(res);
    sqlite3_close(db);

    return 0;
}

在该示例中,问号(?)用作占位符,稍后将其替换为实际值。

char *sql = "SELECT Id, Name FROM Cars WHERE Id = ?";

问号用于为 SQL 查询提供 ID。

rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);

sqlite3_prepare_v2()函数编译 SQL 查询。

sqlite3_bind_int(res, 1, 3);

sqlite3_bind_int()将整数值绑定到预备语句。 占位符将替换为整数 3。该函数的第二个参数是要设置的 SQL 参数的索引,第三个参数是要绑定到该参数的值。

int step = sqlite3_step(res);

sqlite3_step()函数求值 SQL 语句。

if (step == SQLITE_ROW) {

    printf("%s: ", sqlite3_column_text(res, 0));
    printf("%s\n", sqlite3_column_text(res, 1));

} 

如果有可用的数据行,则可以使用sqlite3_column_text()函数获得两列的值。

$ ./parameterized 
3: Skoda

该示例返回 ID 和汽车的名称。

第二个示例使用带有命名占位符的参数化语句。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;
    sqlite3_stmt *res;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "SELECT Id, Name FROM Cars WHERE Id = @id";

    rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);

    if (rc == SQLITE_OK) {

        int idx = sqlite3_bind_parameter_index(res, "@id");
        int value = 4;
        sqlite3_bind_int(res, idx, value);

    } else {

        fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
    }

    int step = sqlite3_step(res);

    if (step == SQLITE_ROW) {

        printf("%s: ", sqlite3_column_text(res, 0));
        printf("%s\n", sqlite3_column_text(res, 1));

    } 

    sqlite3_finalize(res);
    sqlite3_close(db);

    return 0;
}

我们使用命名的占位符选择汽车的名称和价格。

char *sql = "SELECT Id, Name FROM Cars WHERE Id = @id";

命名的占位符以冒号(:)或符号(@)字符为前缀。

int idx = sqlite3_bind_parameter_index(res, "@id");

sqlite3_bind_parameter_index()函数返回给定名称的 SQL 参数的索引。

插入图像

在本节中,我们将图像插入到 SQLite 数据库中。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论是否将图像保存在数据库中的技术问题。

sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,表示二进制大型对象。

#include <sqlite3.h>
#include <stdio.h>

int main(int argc, char **argv) {

    FILE *fp = fopen("woman.jpg", "rb");

    if (fp == NULL) {

        fprintf(stderr, "Cannot open image file\n");    

        return 1;
    }

    fseek(fp, 0, SEEK_END);

    if (ferror(fp)) {

        fprintf(stderr, "fseek() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");          
        }    

        return 1;
    }  

    int flen = ftell(fp);

    if (flen == -1) {

        perror("error occurred");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }

        return 1;     
    }

    fseek(fp, 0, SEEK_SET);

    if (ferror(fp)) {

        fprintf(stderr, "fseek() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }    

        return 1;
    }

    char data[flen+1];

    int size = fread(data, 1, flen, fp);

    if (ferror(fp)) {

        fprintf(stderr, "fread() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }

        return 1;     
    }

    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "Cannot close file handler\n");
    }    

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    sqlite3_stmt *pStmt;

    char *sql = "INSERT INTO Images(Data) VALUES(?)";

    rc = sqlite3_prepare(db, sql, -1, &pStmt, 0);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));

        return 1;
    }    

    sqlite3_bind_blob(pStmt, 1, data, size, SQLITE_STATIC);    

    rc = sqlite3_step(pStmt);

    if (rc != SQLITE_DONE) {

        printf("execution failed: %s", sqlite3_errmsg(db));
    }

    sqlite3_finalize(pStmt);    

    sqlite3_close(db);

    return 0;
}

在此程序中,我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的Images表中。

FILE *fp = fopen("woman.jpg", "rb");

if (fp == NULL) {

    fprintf(stderr, "Cannot open image file\n");    

    return 1;
}

我们从文件系统读取二进制数据。 我们有一个名为woman.jpg的 JPG 图像。 fopen()函数打开指定的文件以供读取。 如果操作失败,它将返回一个指向FILE对象的指针或NULL

fseek(fp, 0, SEEK_END);

if (ferror(fp)) {

    fprintf(stderr, "fseek() failed\n");
    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "Cannot close file handler\n");          
    }    

    return 1;
}  

我们使用fseek()函数将文件指针移到文件末尾。 我们需要确定图像的大小。 如果发生错误,则设置错误指示器。 我们使用fseek()函数检查指示器。 如果发生错误,将关闭打开的文件处理器。

int flen = ftell(fp);

if (flen == -1) {

    perror("error occurred");
    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "Cannot close file handler\n");
    }

    return 1;     
}

对于二进制流,ftell()函数返回文件开头的字节数,例如图像文件的大小。 发生错误时,函数将返回 -1 并设置errnoperror()函数将errno的值解释为错误消息,并将其打印到标准错误输出流。

char data[flen+1];

该数组将存储图像数据。

int size = fread(data, 1, flen, fp);

fread()函数从文件指针读取数据并将其存储在数据数组中。 该函数返回成功读取的元素数。

int r = fclose(fp);

if (r == EOF) {
    fprintf(stderr, "Cannot close file handler\n");
}    

读取数据后,我们可以关闭文件处理器。

char *sql = "INSERT INTO Images(Data) VALUES(?)";

该 SQL 语句用于将映像插入数据库。

rc = sqlite3_prepare(db, sql, -1, &pStmt, 0);

SQL 语句已编译。

sqlite3_bind_blob(pStmt, 1, data, size, SQLITE_STATIC);

sqlite3_bind_blob()函数将二进制数据绑定到已编译的语句。 SQLITE_STATIC参数表示指向内容信息的指针是静态的,不需要释放。

rc = sqlite3_step(pStmt);

执行该语句,并将图像写入表中。

读取图像

在本节中,我们将执行相反的操作。 我们将从数据库表中读取图像。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    FILE *fp = fopen("woman2.jpg", "wb");

    if (fp == NULL) {

        fprintf(stderr, "Cannot open image file\n");    

        return 1;
    }    

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "SELECT Data FROM Images WHERE Id = 1";

    sqlite3_stmt *pStmt;
    rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "Failed to prepare statement\n");
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));

        sqlite3_close(db);

        return 1;
    } 

    rc = sqlite3_step(pStmt);

    int bytes = 0;

    if (rc == SQLITE_ROW) {

        bytes = sqlite3_column_bytes(pStmt, 0);
    }

    fwrite(sqlite3_column_blob(pStmt, 0), bytes, 1, fp);

    if (ferror(fp)) {            

        fprintf(stderr, "fwrite() failed\n");

        return 1;      
    }  

    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "Cannot close file handler\n");
    }       

    rc = sqlite3_finalize(pStmt);   

    sqlite3_close(db);

    return 0;
}    

我们从Images表中读取图像数据,并将其写入另一个文件woman2.jpg中。

FILE *fp = fopen("woman2.jpg", "wb");

if (fp == NULL) {

    fprintf(stderr, "Cannot open image file\n");    

    return 1;
}    

我们以写入模式打开一个二进制文件。 来自数据库的数据被写入文件。

char *sql = "SELECT Data FROM Images WHERE Id = 1";

该 SQL 语句从Images表中选择数据。 我们从第一行获取二进制数据。

if (rc == SQLITE_ROW) {

    bytes = sqlite3_column_bytes(pStmt, 0);
}

sqlite3_column_bytes()函数返回BLOB中的字节数。

fwrite(sqlite3_column_blob(pStmt, 0), bytes, 1, fp);

使用fwrite()函数将二进制数据写入文件。 sqlite3_column_blob()函数返回指向所选二进制数据的指针。

if (ferror(fp)) {            

    fprintf(stderr, "fwrite() failed\n");

    return 1;      
}  

ferror()函数检查与流相关的错误指示符是否已设置。

元数据

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

#include <sqlite3.h>
#include <stdio.h>

int callback(void *, int, char **, char **);

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", 
                sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "PRAGMA table_info(Cars)";

    rc = sqlite3_exec(db, sql, callback, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);
        sqlite3_close(db);

        return 1;
    } 

    sqlite3_close(db);

    return 0;
}

int callback(void *NotUsed, int argc, char **argv, 
                    char **azColName) {

    NotUsed = 0;

    for (int i = 0; i < argc; i++) {

        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }

    printf("\n");

    return 0;
}

在此示例中,我们发出PRAGMA table_info(tableName)命令,以获取有关Cars表的一些元数据信息。

char *sql = "PRAGMA table_info(Cars)";

PRAGMA table_info(tableName)命令为Cars表中的每一列返回一行。 结果集中的列包括列顺序号,列名称,数据类型,该列是否可以为NULL以及该列的默认值。

$ ./column_names 
cid = 0
name = Id
type = INT
notnull = 0
dflt_value = NULL
pk = 0
...

这是示例的输出。

在与元数据有关的下一个示例中,我们将列出test.db数据库中的所有表。

#include <sqlite3.h>
#include <stdio.h>

int callback(void *, int, char **, char **);

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", 
                sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "SELECT name FROM sqlite_master WHERE type='table'";

    rc = sqlite3_exec(db, sql, callback, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "Failed to select data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);
        sqlite3_close(db);

        return 1;
    } 

    sqlite3_close(db);

    return 0;
}

int callback(void *NotUsed, int argc, char **argv, 
                    char **azColName) {

    NotUsed = 0;

    for (int i = 0; i < argc; i++) {

        printf("%s\n", argv[i] ? argv[i] : "NULL");
    }

    return 0;
}

该代码示例将当前数据库中的所有可用表打印到终端。

char *sql = "SELECT name FROM sqlite_master WHERE type='table'";

表名存储在系统sqlite_master表中。

$ ./list_tables 
Cars
Images

这是一个示例输出。

事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。

手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE

自动提交

默认情况下,SQLite 版本 3 在自动提交模式下运行。 在自动提交模式下,对数据库的所有更改将在与当前数据库连接关联的所有操作完成后立即提交。 自动提交模式由BEGIN语句禁用,并由COMMITROLLBACK重新启用。

#include <sqlite3.h>
#include <stdio.h>

int main() {

    sqlite3 *db;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", 
                sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }    

    printf("Autocommit: %d\n", sqlite3_get_autocommit(db));

    sqlite3_close(db);

    return 0;
}

本示例检查数据库是否处于自动提交模式。

printf("Autocommit: %d\n", sqlite3_get_autocommit(db));

如果数据库未处于自动提交模式,则sqlite3_get_autocommit()函数返回零。 如果处于自动提交模式,它将返回非零值。

$ ./get_ac_mode 
Autocommit: 1

该示例确认默认情况下 SQLite 处于自动提交模式。

下一个示例进一步阐明了自动提交模式。 在自动提交模式下,每个非SELECT语句都是一个立即提交的小事务。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "DROP TABLE IF EXISTS Friends;" 
                "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);" 
                "INSERT INTO Friends(Name) VALUES ('Tom');" 
                "INSERT INTO Friends(Name) VALUES ('Rebecca');" 
                "INSERT INTO Friends(Name) VALUES ('Jim');" 
                "INSERT INTO Friend(Name) VALUES ('Robert');";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);        
        sqlite3_close(db);

        return 1;
    } 

    sqlite3_close(db);

    return 0;
}

我们创建Friends表,并尝试用数据填充它。

char *sql = "DROP TABLE IF EXISTS Friends;" 
            "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);" 
            "INSERT INTO Friends(Name) VALUES ('Tom');" 
            "INSERT INTO Friends(Name) VALUES ('Rebecca');" 
            "INSERT INTO Friends(Name) VALUES ('Jim');" 
            "INSERT INTO Friend(Name) VALUES ('Robert');";

最后一条 SQL 语句有一个错误; 没有朋友表。

$ ./autocommit 
SQL error: no such table: Friend
$ sqlite3 test.db
sqlite> .tables
Cars     Friends  Images 
sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim

创建表并插入三行。

事务

在下一个示例中,我们将一些 SQL 语句放入事务中。

#include <sqlite3.h>
#include <stdio.h>

int main(void) {

    sqlite3 *db;
    char *err_msg = 0;

    int rc = sqlite3_open("test.db", &db);

    if (rc != SQLITE_OK) {

        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);

        return 1;
    }

    char *sql = "DROP TABLE IF EXISTS Friends;"
                "BEGIN TRANSACTION;" 
                "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);" 
                "INSERT INTO Friends(Name) VALUES ('Tom');" 
                "INSERT INTO Friends(Name) VALUES ('Rebecca');" 
                "INSERT INTO Friends(Name) VALUES ('Jim');" 
                "INSERT INTO Friend(Name) VALUES ('Robert');"
                "COMMIT;";

    rc = sqlite3_exec(db, sql, 0, 0, &err_msg);

    if (rc != SQLITE_OK ) {

        fprintf(stderr, "SQL error: %s\n", err_msg);

        sqlite3_free(err_msg);        
        sqlite3_close(db);

        return 1;
    }

    sqlite3_close(db);

    return 0;
}

我们继续使用Friends表。

char *sql = "DROP TABLE IF EXISTS Friends;"
            "BEGIN TRANSACTION;" 
            "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);" 
            "INSERT INTO Friends(Name) VALUES ('Tom');" 
            "INSERT INTO Friends(Name) VALUES ('Rebecca');" 
            "INSERT INTO Friends(Name) VALUES ('Jim');" 
            "INSERT INTO Friend(Name) VALUES ('Robert');"
            "COMMIT;";

第一个语句删除Friends表(如果存在)。 其他语句放在事务内。 事务以全有或全无的方式工作。 要么什么都不做,要么什么都不做。

sqlite> .tables
Cars    Images

由于最后一条语句有错误,因此将回滚事务,并且不会创建Friends表。

数据来源

SQLite 文档用于创建本教程。

Tweet

这是 SQLite C 教程。 ZetCode 拥有用于 SQLite Python 的完整电子书:
SQLite Python 电子书

SQLite Python 教程

原文: http://zetcode.com/db/sqlitepythontutorial/

该教程已被 Python SQLite 教程取代。

这是用于 SQLite 数据库的 Python 编程教程。 它涵盖了使用 Python 语言进行 SQLite 编程的基础。 您可能还需要查看 ZetCode 上的 Python 教程SQLite 教程MySQL Python 教程PostgreSQL Python 教程

要使用本教程,我们必须在系统上安装 Python 语言,SQLite 数据库,pysqlite语言绑定和sqlite3命令行工具。

Tweet

如果我们拥有 Python 2.5+,则只需安装sqlite3命令行工具。 SQLite 库和pysqlite语言绑定都内置在 Python 语言中。

$ python2
Python 2.7.12 (default, Nov 12 2018, 14:36:49)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> import sqlite3
>>> sqlite3.version
'2.6.0'
>>> sqlite3.sqlite_version
'3.16.2'

在外壳程序中,我们启动 Python 交互式解释器。 我们可以看到 Python 版本。 在我们的例子中是 Python 2.7.12。 sqlite.versionpysqlite(2.6.0)的版本,它是 Python 语言与 SQLite 数据库的绑定。 sqlite3.sqlite_version为我们提供了 SQLite 数据库库的版本。 在我们的例子中,版本是 3.16.2。

SQLite

SQLite 是嵌入式关系数据库引擎。 该文档称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 它非常受欢迎,当今全球有数亿本使用。 几种编程语言都内置了对 SQLite 的支持,包括 Python 和 PHP。

创建 SQLite 数据库

现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.16.2 2017-01-06 16:32:41
Enter ".help" for usage hints.
sqlite>

我们为sqlite3 tool提供了一个参数; test.db是数据库名称。 这是我们磁盘上的文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供了test.db数据库中的表列表。 当前没有表。 .exit命令终止 sqlite3 命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

SQLite 版本示例

在第一个代码示例中,我们将获得 SQLite 数据库的版本。

version.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

con = None

try:
    con = lite.connect('test.db')

    cur = con.cursor()
    cur.execute('SELECT SQLITE_VERSION()')

    data = cur.fetchone()[0]

    print "SQLite version: {}".format(data)

except lite.Error, e:

    print "Error {}:".format(e.args[0])
    sys.exit(1)

finally:

    if con:
        con.close()

在上面的 Python 脚本中,我们连接到先前创建的test.db数据库。 我们执行一条 SQL 语句,该语句返回 SQLite 数据库的版本。

import sqlite3 as lite

sqlite3模块用于处理 SQLite 数据库。

con = None

我们将con变量初始化为无。 如果无法创建与数据库的连接(例如磁盘已满),则不会定义连接变量。 这将导致finally子句中的错误。

con = lite.connect('test.db')

在这里,我们连接到test.db数据库。 connect()方法返回一个连接对象。

cur = con.cursor()
cur.execute('SELECT SQLITE_VERSION()')

从连接中,我们得到光标对象。 游标用于遍历结果集中的记录。 我们调用游标的execute()方法并执行 SQL 语句。

data = cur.fetchone()[0]

我们获取数据。 由于只检索一条记录,因此我们称为fetchone()方法。

print "SQLite version: {}".format(data)

我们将检索到的数据打印到控制台。

except lite.Error, e:

    print "Error {}:".format(e.args[0])
    sys.exit(1)

如果发生异常,我们将输出一条错误消息,并以错误代码 1 退出脚本。

finally:

    if con:
        con.close()

在最后一步,我们释放资源。

在第二个示例中,我们再次获得 SQLite 数据库的版本。 这次我们将使用with关键字。

version2.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()
    cur.execute('SELECT SQLITE_VERSION()')

    data = cur.fetchone()[0]

    print "SQLite version: {}".format(data)

该脚本返回 SQLite 数据库的当前版本。 通过使用with关键字。 代码更紧凑。

with con:

使用with关键字,Python 解释器会自动释放资源。 它还提供错误处理。

$ ./version2.py
SQLite version: 3.16.2

这是输出。

SQLite Python 创建表

我们创建一个cars表并在其中插入几行。

create_table.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()

    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.execute("INSERT INTO cars VALUES(1,'Audi',52642)")
    cur.execute("INSERT INTO cars VALUES(2,'Mercedes',57127)")
    cur.execute("INSERT INTO cars VALUES(3,'Skoda',9000)")
    cur.execute("INSERT INTO cars VALUES(4,'Volvo',29000)")
    cur.execute("INSERT INTO cars VALUES(5,'Bentley',350000)")
    cur.execute("INSERT INTO cars VALUES(6,'Citroen',21000)")
    cur.execute("INSERT INTO cars VALUES(7,'Hummer',41400)")
    cur.execute("INSERT INTO cars VALUES(8,'Volkswagen',21600)")

上面的脚本创建一个cars表,并将 8 行插入到该表中。

cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")

该 SQL 语句创建一个新的cars表。 该表有三列。

cur.execute("INSERT INTO cars VALUES(1,'Audi',52642)")
cur.execute("INSERT INTO cars VALUES(2,'Mercedes',57127)")

这两行将两辆车插入表。 使用with关键字,更改将自动提交。 否则,我们将不得不手动提交它们。

sqlite> .mode column
sqlite> .headers on

我们使用sqlite3工具验证写入的数据。 首先,我们修改数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite>  select * from cars;
id          name        price
----------  ----------  ----------
1           Audi        52642
2           Mercedes    57127
3           Skoda       9000
4           Volvo       29000
5           Bentley     350000
6           Citroen     21000
7           Hummer      41400
8           Volkswagen  21600

这是我们已写入cars表的数据。

我们将创建相同的表。 这次使用便捷的executemany()方法。

create_table2.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

cars = (
    (1, 'Audi', 52642),
    (2, 'Mercedes', 57127),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Hummer', 41400),
    (7, 'Volkswagen', 21600)
)

con = lite.connect('test.db')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)

程序将删除cars表(如果存在)并重新创建它。

cur.execute("DROP TABLE IF EXISTS cars")
cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")

如果存在,则第一个 SQL 语句将删除cars表。 第二条 SQL 语句创建cars表。

cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)

我们使用便捷的executemany()方法将 8 行插入到表中。 此方法的第一个参数是参数化的 SQL 语句。 第二个参数是数据,以元组的元组的形式。

我们提供了另一种创建cars表的方法。 我们手动提交更改并提供我们自己的错误处理。

create_table3.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

try:
    con = lite.connect('test.db')

    cur = con.cursor()

    cur.executescript("""
        DROP TABLE IF EXISTS cars;
        CREATE TABLE cars(id INT, name TEXT, price INT);
        INSERT INTO cars VALUES(1,'Audi',52642);
        INSERT INTO cars VALUES(2,'Mercedes',57127);
        INSERT INTO cars VALUES(3,'Skoda',9000);
        INSERT INTO cars VALUES(4,'Volvo',29000);
        INSERT INTO cars VALUES(5,'Bentley',350000);
        INSERT INTO cars VALUES(6,'Citroen',21000);
        INSERT INTO cars VALUES(7,'Hummer',41400);
        INSERT INTO cars VALUES(8,'Volkswagen',21600);
        """)

    con.commit()

except lite.Error, e:

    if con:
        con.rollback()

    print "Error {}:".format(e.args[0])
    sys.exit(1)

finally:

    if con:
        con.close()

在上面的脚本中,我们使用executescript()方法(重新)创建cars表。

cur.executescript("""
    DROP TABLE IF EXISTS cars;
    CREATE TABLE cars(id INT, name TEXT, price INT);
    INSERT INTO cars VALUES(1,'Audi',52642);
    INSERT INTO cars VALUES(2,'Mercedes',57127);
...

executescript()方法允许我们一步执行整个 SQL 代码。

con.commit()

如果没有with关键字,则必须使用commit()方法来提交更改。

except lite.Error, e:

    if con:
        con.rollback()

    print "Error {}:".format(e.args[0])
    sys.exit(1)

发生错误时,所做的更改将回滚,并在终端上显示一条错误消息。

SQLite Python lastrowid

有时,我们需要确定最后插入的行的 ID。 在 Python SQLite 中,我们使用光标对象的lastrowid属性。

lastrowid.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect(':memory:')

with con:

    cur = con.cursor()
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT);")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom');")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca');")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim');")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert');")

    last_row_id = cur.lastrowid
    print "The last Id of the inserted row is {}".format(last_row_id)

我们在内存中创建一个friends表。 ID 会自动递增。

cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT);")

在 SQLite 中,INTEGER PRIMARY KEY列自动增加。 还有一个AUTOINCREMENT关键字。 在INTEGER PRIMARY KEY AUTOINCREMENT中使用时,会使用稍微不同的 ID 创建算法。

cur.execute("INSERT INTO friends(name) VALUES ('Tom');")
cur.execute("INSERT INTO friends(name) VALUES ('Rebecca');")
cur.execute("INSERT INTO friends(name) VALUES ('Jim');")
cur.execute("INSERT INTO friends(name) VALUES ('Robert');")

使用自动增量时,我们必须明确声明列名,而忽略自动增量的列名。 这四个语句在friends表中插入四行。

last_row_id = cur.lastrowid

使用lastrowid获得最后插入的行 ID。

$ ./lastrowid.py
The last Id of the inserted row is 4

我们看到了程序的输出。

SQLite Python 检索数据

现在我们已经将一些数据插入数据库中,我们想要取回它。

select_all.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print row

在此示例中,我们从cars表中检索所有数据。

cur.execute("SELECT * FROM cars")

该 SQL 语句从cars表中选择所有数据。

rows = cur.fetchall()

fetchall()方法获取所有记录。 它返回一个结果集。 从技术上讲,它是一个元组的元组。 每个内部元组代表表中的一行。

for row in rows:
    print row

我们将数据逐行打印到控制台。

$ ./select_all.py
(1, u'Audi', 52642)
(2, u'Mercedes', 57127)
(3, u'Skoda', 9000)
(4, u'Volvo', 29000)
(5, u'Bentley', 350000)
(6, u'Citroen', 21000)
(7, u'Hummer', 41400)
(8, u'Volkswagen', 21600)

这是示例的输出。

一次返回所有数据可能不可行。 我们可以一一读取行。

fetch_one.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    while True:

        row = cur.fetchone()

        if row == None:
            break

        print row[0], row[1], row[2]

在此脚本中,我们连接到数据库,并逐个读取cars表的行。

while True:

我们从while循环访问数据。 当我们读取最后一行时,循环终止。

row = cur.fetchone()

if row == None:
    break

fetchone()方法返回表的下一行。 如果没有剩余数据,则返回None。 在这种情况下,我们打破了循环。

print row[0], row[1], row[2]

数据以元组的形式返回。 在这里,我们从元组中选择记录。 第一个是 ID,第二个是汽车名称,第三个是汽车的价格。

$ ./fetch_one.py
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

这是示例的输出。

SQLite Python 字典游标

默认游标以元组的元组返回数据。 当我们使用字典游标时,数据以 Python 字典的形式发送。 这样,我们可以通过列名称来引用数据。

dictionary_cursor.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    con.row_factory = lite.Row

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print "{} {} {}".format(row["id"], row["name"], row["price"])

在此示例中,我们使用字典光标打印cars表的内容。

con.row_factory = lite.Row

我们选择一个字典光标。 现在,我们可以按列名访问记录。

for row in rows:
    print "{} {} {}".format(row["id"], row["name"], row["price"])

通过列名访问数据。

SQLite Python 参数化查询

现在,我们将关注参数化查询。 当使用参数化查询时,我们使用占位符,而不是直接将值写入语句。 参数化查询可提高安全性和性能。

Python sqlite3模块支持两种类型的占位符:问号和命名占位符。

parameterized_query.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

uId = 1
uPrice = 62300

con = lite.connect('test.db')

with con:

    cur = con.cursor()

    cur.execute("UPDATE cars SET price=? WHERE id=?", (uPrice, uId))

    print "Number of rows updated: {}".format(cur.rowcount)

我们更新了一辆车的价格。 在此代码示例中,我们使用问号占位符。

cur.execute("UPDATE cars SET price=? WHERE id=?", (uPrice, uId))

问号?是值的占位符。 这些值将添加到占位符。

print "Number of rows updated: {}".format(cur.rowcount)

rowcount属性返回更新的行数。 在我们的情况下,一行已更新。

第二个示例使用带有命名占位符的参数化语句。

parameterized_query2.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

uId = 4

con = lite.connect('test.db')

with con:

    cur = con.cursor()

    cur.execute("SELECT name, price FROM cars WHERE Id=:Id", {"Id": uId})

    row = cur.fetchone()
    print row[0], row[1]

我们使用命名的占位符选择汽车的名称和价格。

cur.execute("SELECT name, price FROM cars WHERE Id=:Id", {"Id": uId})

命名的占位符以冒号开头。

SQLite Python 插入图像

在本节中,我们将图像插入到 SQLite 数据库中。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论是否将图像保存在数据库中的技术问题。

sqlite> CREATE TABLE images(id INTEGER PRIMARY KEY, data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,表示二进制大型对象。

insert_image.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

def readImage():

    fin = None

    try:
        fin = open("sid.png", "rb")
        img = fin.read()
        return img

    except IOError, e:

        print e
        sys.exit(1)

    finally:

        if fin:
            fin.close()

try:
    con = lite.connect('test.db')

    cur = con.cursor()

    data = readImage()
    binary = lite.Binary(data)
    cur.execute("INSERT INTO images(data) VALUES (?)", (binary,) )

    con.commit()

except lite.Error, e:

    if con:
        con.rollback()

    print e
    sys.exit(1)

finally:

    if con:
        con.close()

在此脚本中,我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的images表中。

try:
    fin = open("sid.png", "rb")
    img = fin.read()
    return img

我们从文件系统读取二进制数据。 我们有一个名为sid.png的 JPG 图像。

binary = lite.Binary(data)

使用 SQLite Binary对象对数据进行编码。

cur.execute("INSERT INTO images(data) VALUES (?)", (binary,) )

该 SQL 语句用于将映像插入数据库。

SQLite Python 读取图像

在本节中,我们将执行相反的操作:我们从数据库表中读取一个图像。

read_image.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

def writeImage(data):

    fout = None

    try:
        fout = open('sid2.png','wb')
        fout.write(data)

    except IOError, e:

        print e
        sys.exit(1)

    finally:

        if fout:
            fout.close()

try:
    con = lite.connect('test.db')

    cur = con.cursor()
    cur.execute("SELECT data FROM images LIMIT 1")
    data = cur.fetchone()[0]

    writeImage(data)

except lite.Error, e:

    print e
    sys.exit(1)

finally:

    if con:
        con.close()

我们从Images表中读取图像数据,并将其写入另一个文件woman2.jpg中。

try:
    fout = open('sid2.png','wb')
    fout.write(data)

我们以写入模式打开一个二进制文件。 来自数据库的数据被写入文件。

cur.execute("SELECT data FROM images LIMIT 1")
data = cur.fetchone()[0]

这两行从images表中选择并获取数据。 我们从第一行获取二进制数据。

SQLite Python 元数据

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

column_names.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()

    cur.execute('PRAGMA table_info(cars)')

    data = cur.fetchall()

    for d in data:
        print d[0], d[1], d[2]

在此示例中,我们发出PRAGMA table_info(tableName)命令,以获取有关cars表的一些元数据信息。

cur.execute('PRAGMA table_info(cars)')

PRAGMA table_info(tableName)命令为cars表中的每一列返回一行。 结果集中的列包括列顺序号,列名称,数据类型,该列是否可以为NULL以及该列的默认值。

for d in data:
    print d[0], d[1], d[2]

根据提供的信息,我们打印列顺序号,列名称和列数据类型。

$ ./column_names.py
0 id INT
1 name TEXT
2 price INT

示例的输出。

接下来,我们将打印cars表中的所有行及其列名。

column_names2.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()
    cur.execute('SELECT * FROM cars')

    col_names = [cn[0] for cn in cur.description]

    rows = cur.fetchall()

    print "{:3} {:10} {:7}".format(col_names[0], col_names[1], col_names[2])

    for row in rows:
        print "{:<3} {:<10} {:7}".format(row[0], row[1], row[2])

我们将cars表的内容打印到控制台。 现在,我们也包括列的名称。 记录与列名对齐。

col_names = [cn[0] for cn in cur.description]

我们从游标对象的description属性获得列名。

print "{:3} {:10} {:7}".format(col_names[0], col_names[1], col_names[2])

此行打印cars表的三个列名。

for row in rows:
    print "{:<3} {:<10} {:7}".format(row[0], row[1], row[2])

我们使用for循环打印行。 数据与列名对齐。

$  ./column_names2.py 
id  name       price  
1   Audi         62300
2   Mercedes     57127
3   Skoda         9000
4   Volvo        29000
5   Bentley     350000
6   Hummer       41400
7   Volkswagen   21600

这是输出。

在与元数据有关的最后一个示例中,我们将列出test.db数据库中的所有表。

list_tables.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

con = lite.connect('test.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT name FROM sqlite_master WHERE type='table'")

    rows = cur.fetchall()

    for row in rows:
        print row[0]

该代码示例将当前数据库中的所有可用表打印到终端。

cur.execute("SELECT name FROM sqlite_master WHERE type='table'")

表名存储在系统sqlite_master表中。

$ ./list_tables.py
cars
images

这些是我们系统上的表。

SQLite Python 数据导出&导入

我们可以转储 SQL 格式的数据以创建数据库表的简单备份。

export_table.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

cars = (
    (1, 'Audi', 52643),
    (2, 'Mercedes', 57642),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Hummer', 41400),
    (7, 'Volkswagen', 21600)
)

def writeData(data):

    f = open('cars.sql', 'w')

    with f:
        f.write(data)

con = lite.connect(':memory:')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)
    cur.execute("DELETE FROM cars WHERE price < 30000")

    data = '\n'.join(con.iterdump())

    writeData(data)

在上面的示例中,我们在内存中重新创建了cars表。 我们从表中删除一些行,并将表的当前状态转储到cars.sql文件中。 该文件可以用作表的当前备份。

def writeData(data):

    f = open('cars.sql', 'w')

    with f:
        f.write(data)

表中的数据正在写入文件。

con = lite.connect(':memory:')

我们在内存中创建一个临时表。

cur.execute("DROP TABLE IF EXISTS cars")
cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)
cur.execute("DELETE FROM cars WHERE price < 30000")

这些行创建cars表,插入值并删除行,其中price小于 30000 单位。

data = '\n'.join(con.iterdump())

con.iterdump()返回一个迭代器,以 SQL 文本格式转储数据库。 内置的join()函数采用迭代器,并将迭代器中的所有字符串连接在一起,并用新行分隔。 此数据将写入writeData()函数中的cars.sql文件中。

$ cat cars.sql
BEGIN TRANSACTION;
CREATE TABLE cars(id INT, name TEXT, price INT);
INSERT INTO "cars" VALUES(1,'Audi',52643);
INSERT INTO "cars" VALUES(2,'Mercedes',57642);
INSERT INTO "cars" VALUES(5,'Bentley',350000);
INSERT INTO "cars" VALUES(6,'Hummer',41400);
COMMIT;

废弃的内存车表中的内容。

现在,我们将执行反向操作。 我们将转储的表导入回内存。

import_table.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite

def readData():

    f = open('cars.sql', 'r')

    with f:

        data = f.read()

        return data

con = lite.connect(':memory:')

with con:

    cur = con.cursor()

    sql = readData()
    cur.executescript(sql)

    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print row

在此脚本中,我们读取cars.sql文件的内容并执行它。 这将重新创建转储的表。

def readData():

    f = open('cars.sql', 'r')

    with f:

        data = f.read()

        return data

readData()方法内部,我们读取cars.sql表的内容。

cur.executescript(sql)

我们调用executescript()方法来启动 SQL 脚本。

cur.execute("SELECT * FROM cars")

rows = cur.fetchall()

for row in rows:
    print row

我们验证数据。

$ ./import_table.py
(1, u'Audi', 52643)
(2, u'Mercedes', 57642)
(5, u'Bentley', 350000)
(6, u'Hummer', 41400)

输出显示我们已经成功地重新创建了保存的汽车表。

Python SQLite 事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。

手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE。 SQLite Python 模块还支持自动提交模式,该模式下对表的所有更改均立即生效。

no_commit.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

try:
    con = lite.connect('test.db')

    cur = con.cursor()
    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT)")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

    #con.commit()

except lite.Error, e:

    if con:
        con.rollback()

    print e
    sys.exit(1)

finally:

    if con:
        con.close()

我们创建一个friends表,并尝试用数据填充它。 但是,正如我们将看到的,数据未提交。

#con.commit()

注释commit()方法。 如果我们取消注释该行,则数据将被写入表中。

sqlite> .tables
cars     friends  images 
sqlite> SELECT Count(id) FROM friends;
Count(id) 
----------
0         
sqlite>

表已创建,但数据未写入表中。

在第二个示例中,我们演示了一些命令将先前的更改隐式提交到数据库。

implicit_commit.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

try:
    con = lite.connect('test.db')
    cur = con.cursor()
    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT)")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

    cur.execute("CREATE TABLE IF NOT EXISTS temporary(id INT)")

except lite.Error, e:

    if con:
        con.rollback()

    print e
    sys.exit(1)

finally:

    if con:
        con.close()

同样,我们没有显式调用commit()命令。 但是这一次,数据被写入了Friends表。

cur.execute("CREATE TABLE IF NOT EXISTS temporary(id INT)")

该 SQL 语句创建一个新表。 它还会提交以前的更改。

$ ./implicit_commit.py

sqlite> SELECT * FROM friends;
id          name
----------  ----------
1           Tom
2           Rebecca
3           Jim
4           Robert

数据已写入friends表。

在自动提交模式下,将立即执行一条 SQL 语句。

autocommit.py

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import sqlite3 as lite
import sys

try:
    con = lite.connect('test.db', isolation_level=None)

    cur = con.cursor()
    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT)")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

except lite.Error, e:

    print e
    sys.exit(1)

finally:

    if con:
        con.close()

在此示例中,我们以自动提交模式连接到数据库。

con = lite.connect('test.db', isolation_level=None)

当将isolation_level设置为None时,我们具有自动提交模式。

$ ./autocommit.py

sqlite> SELECT * FROM friends;
Id          Name
----------  ----------
1           Tom
2           Rebecca
3           Jim
4           Robert

数据已成功提交到friends表。

这是 SQLite Python 教程。 ZetCode 拥有用于 SQLite Python 的完整电子书:
SQLite Python 电子书

MySQL 的第一步

原文: http://zetcode.com/databases/mysqltutorial/firststeps/

在本章中,我们将使用 MySQL 迈出第一步。 我们将启动服务器,使用客户端工具连接到服务器,创建新用户并发布第一个 SQL 语句。

启动/停止 MySQL 服务器

MySQL 服务器是在后台运行的守护程序。 启动 MySQL 的方式取决于您的系统和完成的安装类型。

$ sudo /etc/init.d/mysqld start
$ sudo /etc/init.d/mysqld stop

在基于传统init的系统上,我们将使用上述命令来启动和停止 MySQL 服务器。

$ sudo service mysql start
$ sudo service mysql stop

Debian 已迁移到 Upstart,这是一个基于事件的守护程序,用于启动任务和服务并进行监督。 在使用 Upstart 的系统上,我们将使用上述命令启动和停止 MySQL 服务器。

$ sudo /usr/local/mysql/support-files/mysql.server start
$ sudo /usr/local/mysql/support-files/mysql.server stop

如果从源代码安装了 MySQL,则可以使用mysql.server命令启动和停止 MySQL。

检查 MySQL 状态

我们将展示如何检查 MySQL 服务器的状态。

$ service mysql status
● mysql.service - MySQL Community Server
   Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
   Active: active (running) since Pi 2017-01-27 16:32:12 CET; 2h 7min ago
 Main PID: 9439 (mysqld)
   CGroup: /system.slice/mysql.service
           └─9439 /usr/sbin/mysqld

jan 27 16:32:10 t400 systemd[1]: Starting MySQL Community Server...
jan 27 16:32:12 t400 systemd[1]: Started MySQL Community Server.
jan 27 16:32:26 t400 systemd[1]: Started MySQL Community Server.

我们使用service mysql status命令检查状态。

$ mysqladmin -u root -p ping
Enter password: 
mysqld is alive

我们使用mysqladmin工具检查 MySQL 服务器是否正在运行。 -u选项指定对服务器执行ping操作的用户。 -p选项是用户的密码。 如果省略密码,则mysqladmin提示输入一个。 在提示符后键入的字符不可见。 这是用于mysqladmin的更安全的解决方案。 这样,任何人都不会看到您键入的密码,并且该密码不会存储在外壳程序的历史记录中。

mysqladmin工具

mysqladmin是用于执行管理操作的客户端。

$ mysqladmin -uroot -p shutdown

我们使用mysqladmin工具关闭 MySQL 服务器。

$ mysqladmin -u root -p version
Enter password: 
...
Server version          5.7.17-0ubuntu0.16.04.1
Protocol version        10
Connection              Localhost via UNIX socket
UNIX socket             /var/run/mysqld/mysqld.sock
Uptime:                 45 sec
...

我们使用mysqladmin检查 MySQL 服务器的版本。

$ mysqladmin -u root -p create testdb

可以使用mysqladmin创建数据库。

$ mysqladmin -u root -p drop testdb

此命令删除数据库。

$ mysqladmin -u root -p password 
Enter password: 
New password: 
Confirm new password: 

我们可以使用mysqladmin更改用户密码。 我们输入旧密码,然后输入两次新密码。

mysql工具

mysql是 MySQL 命令行工具。 这是一个简单的外壳。 它支持交互和非交互使用。

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.7.17-0ubuntu0.16.04.1 (Ubuntu)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

我们使用mysql工具连接到服务器。 请注意,我们在-p选项之后省略了密码。 我们在Enter password:提示符后键入密码。

mysql命令行工具以mysql>作为提示符。 在此提示下,我们可以发出mysql内置命令和 SQL 语句。 我们需要熟悉mysql工具。 Ctrl + L清除屏幕,Ctrl + Dquit命令退出mysql。 我们需要区分mysql命令和 SQL 语句。 SQL 语句以分号终止。

mysql> help

For information about MySQL products and services, visit:
   http://www.mysql.com/
For developer information, including the MySQL Reference Manual, visit:
   http://dev.mysql.com/
To buy MySQL Enterprise support, training, or other products, visit:
   https://shop.mysql.com/

List of all MySQL commands:
Note that all text commands must be first on line and end with ';'
?         (\?) Synonym for `help'.
clear     (\c) Clear the current input statement.
connect   (\r) Reconnect to the server. Optional arguments are db and host.
delimiter (\d) Set statement delimiter.
edit      (\e) Edit command with $EDITOR.
ego       (\G) Send command to mysql server, display result vertically.
exit      (\q) Exit mysql. Same as quit.
go        (\g) Send command to mysql server.
help      (\h) Display this help.
nopager   (\n) Disable pager, print to stdout.
notee     (\t) Don't write into outfile.
pager     (\P) Set PAGER [to_pager]. Print the query results via PAGER.
print     (\p) Print current command.
prompt    (\R) Change your mysql prompt.
quit      (\q) Quit mysql.
...

键入help以获得mysql命令的完整列表。

mysql> system pwd
/home/janbodnar

system命令可以执行 Shell 命令。 我们已经启动了pwd命令来查找当前的工作目录。

mysql> quit
Bye

quit命令终止mysql Shell。

$ mysql --version
mysql  Ver 14.14 Distrib 5.7.17, for Linux (x86_64) using  EditLine wrapper

mysql也可以非交互方式使用。 在这里,我们获得了该工具的版本。

建立数据库

现在,我们将创建数据库。

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0,00 sec)

SHOW DATABASES语句显示我们系统上的所有可用数据库。 请注意,SQL 语句以分号终止。 存在四个数据库。 information_schemamysqlperformance_schema是 MySQL 系统数据库。 sys是用于调整和诊断用例的一组架构对象。 尚无用户定义的数据库。

mysql> CREATE DATABASE mydb;
Query OK, 1 row affected (0,00 sec)

该语句创建一个新的数据库。 在整个教程中,我们将使用mydb数据库。 要创建新数据库,我们需要具有某些特权。 请记住,我们已经以root用户(超级用户并具有所有特权)连接到服务器。

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb               |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0,00 sec)

显示所有数据库,其中有mydb数据库。

mysql> USE mydb;
Database changed

为了使用数据库,我们必须首先选择它。 我们使用USE命令选择一个特定的数据库。

mysql> SHOW TABLES;
Empty set (0.00 sec)

SHOW TABLES语句显示数据库中所有可用的表。 由于它是新创建的数据库,因此找不到表。

mysql> source cars.sql
Database changed
Query OK, 0 rows affected (0.20 sec)

Query OK, 1 row affected (0.08 sec)

...

在第一章中,我们提供了一些 SQL 脚本来创建一些表。 我们使用source命令执行cars.sql脚本,该脚本为我们创建了Cars表。

mysql> SHOW TABLES;
+----------------+
| Tables_in_mydb |
+----------------+
| Cars           |
+----------------+
1 row in set (0,00 sec)

现在SHOW TABLES语句显示一个可用表。

mysql> SELECT * FROM Cars;
+----+------------+--------+
| Id | Name       | Cost   |
+----+------------+--------+
|  1 | Audi       |  52642 |
|  2 | Mercedes   |  57127 |
|  3 | Skoda      |   9000 |
|  4 | Volvo      |  29000 |
|  5 | Bentley    | 350000 |
|  6 | Citroen    |  21000 |
|  7 | Hummer     |  41400 |
|  8 | Volkswagen |  21600 |
+----+------------+--------+
8 rows in set (0,00 sec)

这就是表中的数据。

创建一个新用户

与 Unix 根帐户类似,建议不要在日常工作中使用 MySQL 超级用户根帐户。 我们仅在必要时才使用root帐户。 我们创建一个将要使用的新帐户。 该用户将具有有限的权限。 使用root用户时,我们可能会意外地对我们的数据造成很多损害。

mysql> CREATE USER user12@localhost IDENTIFIED BY '34klq*';

上面的命令创建一个名为user12的新用户。 帐户具有密码34klq*。 用户已创建,但没有特权。

mysql> GRANT ALL ON mydb.* TO user12@localhost;

该语句为mydb数据库上的所有数据库对象授予user12所有特权。 这些特权足以满足本教程中的示例要求。

mysql> quit
Bye
$ mysql -u user12 -p

现在,我们可以使用新的用户帐户连接到 MySQL。

$ mysql -u user12 -p mydb -e "SELECT * FROM Cars"
Enter password: 
+----+------------+--------+
| Id | Name       | Cost   |
+----+------------+--------+
|  1 | Audi       |  52642 |
|  2 | Mercedes   |  57127 |
|  3 | Skoda      |   9000 |
|  4 | Volvo      |  29000 |
|  5 | Bentley    | 350000 |
|  6 | Citroen    |  21000 |
|  7 | Hummer     |  41400 |
|  8 | Volkswagen |  21600 |
+----+------------+--------+

我们非交互地连接到mydb数据库并执行一条 SQL 语句。 在-e选项之后指定要执行的语句。

在本章中,我们使用 MySQL 数据库系统进行了第一步。

SQLite Perl 教程

原文: http://zetcode.com/db/sqliteperltutorial/

这是针对 SQLite 数据库的 Perl 编程教程。 它涵盖了使用 Perl 语言进行 SQLite 编程的基础。

目录

Perl DBI

Perl DBI (数据库接口)是用于 Perl 编程语言的数据库访问模块。 它定义了一组提供标准数据库接口的方法,变量和约定。

其他语言也创建了用于数据库的类似通用接口。 Java 具有 JDBC,PHP PDO。

Tweet

相关教程

SQLite 教程涵盖了 SQLite 嵌入式数据库引擎。 SQLite Ruby 教程是针对 Ruby 语言的 SQLite 编程教程。 SQLite C# 教程是用于 C# 语言的 SQLite 编程教程。 Perl LWP 编程文章介绍了使用 LWP 模块进行 Perl 编程。

Perl DBI

原文: http://zetcode.com/db/sqliteperltutorial/dbi/

在 SQLite Perl 教程的第一章中,我们将介绍 Perl DBI 模块和 SQLite 数据库。 我们将提供一些定义并显示如何安装必要的元素。

先决条件

要使用本教程,我们必须安装 Perl 语言,SQLite 数据库,sqlite3命令行工具,Perl DBIDBD::SQLite模块。 DBI是标准的 Perl 数据库接口。 每个数据库都有其驱动程序。 在我们的例子中,DBD::SQLite是 SQLite 数据库的驱动程序。

$ sudo perl -MCPAN -e shell
cpan> install DBI
cpan[2]> install DBD::SQLite

上面的命令显示了如何安装 Perl DBI 和DBD::SQLite模块。

SQLite 数据库

SQLite 是嵌入式关系数据库引擎。 它是一个自包含,无服务器,零配置和事务型 SQL 数据库引擎。 SQLite 实现了 SQL 的大多数 SQL-92 标准。 SQLite 引擎不是独立的进程。 而是将其静态或动态链接到应用。 SQLite 数据库是单个普通磁盘文件,可以位于目录层次结构中的任何位置。

SQLite 附带sqlite3命令行工具。 它可用于对数据库发出 SQL 命令。 现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

我们为sqlite3工具提供了一个参数。 test.db是数据库名称。 它是我们磁盘上的单个文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供了test.db数据库中的表列表。 当前没有表。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

Perl DBI

Perl DBI(数据库接口)是 Perl 编程语言的数据库访问模块。 它定义了一组提供标准数据库接口的方法,变量和约定。 DBI还负责驱动程序的动态加载,错误检查和处理,提供方法的默认实现以及许多其他非数据库特定的职责。 DBI将方法调用分派到适当的数据库驱动程序。 DBD(数据库驱动程序)是一个 Perl 模块,可为特定的数据库引擎转换DBI方法。 数据库驱动程序由数据库供应商提供。

#!/usr/bin/perl

use strict;
use DBI;

my @ary = DBI->available_drivers();
print join("\n", @ary), "\n";

该代码示例列出了系统上所有可用的驱动程序。

use DBI;

我们为脚本导入 DBI 模块。

my @ary = DBI->available_drivers();

available_drivers()类方法获取系统上所有当前可用的驱动程序。

print join("\n", @ary), "\n";

此行将驱动程序打印到控制台,每个驱动程序都在单独的行上。

$ ./available_drivers.pl
DBM
ExampleP
File
Gofer
Proxy
SQLite
Sponge
mysql

示例输出。

常见的 DBI 方法

下表列出了一些常见的 DBI 方法。

方法名称 描述
available_drivers() 返回所有可用驱动程序的列表
connect() 建立与请求的数据源的连接
disconnect() 与数据库服务器断开连接
prepare() 准备要执行的 SQL 语句
execute() 执行预备语句
do() 准备并执行一条 SQL 语句
bind_param() 在预备语句中将值与占位符关联
bind_col() 将 Perl 变量绑定到SELECT语句的输出字段
begin_work() 开始新事务
commit() 将最新的一系列未提交的数据库更改写入数据库
rollback() 撤消最近一系列未提交的数据库更改
quote() 引用字符串字面值,以用作 SQL 语句中的字面值
dump_results() 获取所有行并打印
fetchrow_array() 获取下一行作为字段数组
fetchrow_arrayref() 获取下一行作为字段的引用数组
fetchrow_hashref() 获取下一行作为对哈希表的引用
fetchall_arrayref() 以数组的形式获取所有数据
finish() 完成声明并让系统释放资源
rows() 返回受影响的行数
column_info() 提供有关列的信息
table_info() 提供有关表的信息
primary_key_info() 提供有关表中主键的信息
foreign_key_info() 提供有关表中外键的信息

约定

在使用 Perl DBI 时,Perl 程序员通常使用以下变量名。 在本教程中,我们也将遵守这些约定。

变量名 描述
$dbh 数据库句柄对象
$sth 语句句柄对象
$drh 驱动程序句柄对象(在应用中很少见或使用)
$h | 上面的任何句柄类型($dbh$sth$drh
$rc 通用返回码(布尔值:true = okfalse = error
$rv 一般返回值(通常为整数)
@ary 从数据库返回的值列表,通常是一行数据
$rows 处理的行数(如果可用,否则为 -1)
$fh 文件句柄
undef NULL 值由 Perl 中的未定义值表示
\%attr 引用传递给方法的属性值的哈希

SQLite Perl 教程的这一章介绍了 Perl DBI 模块和 SQLite 数据库。

使用 Perl 连接到 SQLite 数据库

原文: http://zetcode.com/db/sqliteperltutorial/connect/

SQLite Perl 教程的这一部分将展示如何创建与数据库的数据库连接。

第一步是连接到数据库。 我们使用connect() DBI 方法建立连接。 disconnect()方法用于关闭数据库连接。

$dbh = DBI->connect($dsn, $username, $password)
    or die $DBI::errstr;
$dbh = DBI->connect($dsn, $username, $password, \%attr)
    or die $DBI::errstr;

connect()方法建立与请求的数据源的数据库连接。 如果连接成功,它将返回数据库句柄对象。 我们使用disconnect()方法终止连接。

$dsn是数据源名称。 它是一个字符串,告诉 Perl DBI 模块,应加载哪种驱动程序以及将要建立连接的数据库的位置。

dbi:DriverName:database_name
dbi:DriverName:database_name@hostname:port
dbi:DriverName:database=database_name;host=hostname;port=port

上面的字符串是 Perl DBI 中数据源名称的示例。

dbi:SQLite:dbname=test.db

我们将使用此数据源名称。 dsn始终以dbi:子字符串开头。 然后我们有驱动程序名称。 在我们的例子中,驱动程序名称为 SQLite。 第三部分是数据库名称。 在本教程中,我们将使用test.db

dbi:SQLite:dbname=:memory:

我们还可以使用上述数据源名称在内存中创建数据库。

对于 SQLite 数据库,我们不提供$username$password。 数据库不支持它。 我们在那儿留下了两个空弦。 最后一个参数是对哈希的引用,在哈希中,我们可以设置属性以更改连接的默认设置。 例如,RaiseError属性可用于强制错误引发异常,而不是返回错误代码。 HandleError属性可用于提供子程序,以防发生错误。 AutoCommit属性设置或取消设置自动提交模式。

$DBI::errstr是一个 DBI 动态属性,它返回本地数据库引擎错误消息。 如果连接失败,则会显示此消息,并且脚本将中止。

版本

在第一个代码示例中,我们将获得 SQLite 数据库的版本。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare("SELECT SQLITE_VERSION()");
$sth->execute();

my $ver = $sth->fetch();

print @$ver;
print "\n";

$sth->finish();
$dbh->disconnect();

在上面的 Perl 脚本中,我们连接到先前创建的test.db数据库。 我们执行一条 SQL 语句,该语句返回 SQLite 数据库的版本。

use DBI;

我们使用 Perl DBI 模块连接到 SQLite 数据库。

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

在这里,我们连接到test.db数据库。 第一个参数是数据源名称。 在字符串中,我们指定数据库驱动程序和数据库名称。 第二个和第三个参数为空。 在其他情况下,我们在那里提供用户名和密码。 最后一个参数是数据库选项。 我们将RaiseError选项设置为 1。这将导致引发异常,而不是返回错误代码。

my $sth = $dbh->prepare("SELECT SQLITE_VERSION()");
$sth->execute();

prepare()方法准备一条 SQL 语句供以后执行。 execute()方法执行 SQL 语句。

my $ver = $sth->fetch();

我们获取数据。

print @$ver;
print "\n";

我们将检索到的数据打印到控制台。

$sth->finish();

在这里,我们指示将不再从此语句句柄中获取任何数据。

$dbh->disconnect();

我们关闭与数据库的连接。

$ ./version.pl
3.7.9

执行verion.pl脚本,我们得到 SQLite 数据库的版本。

插入数据

我们将创建一个Cars表并在其中插入几行。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",
    "",
    { RaiseError => 1}
) or die $DBI::errstr;

$dbh->do("DROP TABLE IF EXISTS Cars");
$dbh->do("CREATE TABLE Cars(Id INT PRIMARY KEY, Name TEXT, Price INT)");
$dbh->do("INSERT INTO Cars VALUES(1,'Audi',52642)");
$dbh->do("INSERT INTO Cars VALUES(2,'Mercedes',57127)");
$dbh->do("INSERT INTO Cars VALUES(3,'Skoda',9000)");
$dbh->do("INSERT INTO Cars VALUES(4,'Volvo',29000)");
$dbh->do("INSERT INTO Cars VALUES(5,'Bentley',350000)");
$dbh->do("INSERT INTO Cars VALUES(6,'Citroen',21000)");
$dbh->do("INSERT INTO Cars VALUES(7,'Hummer',41400)");
$dbh->do("INSERT INTO Cars VALUES(8,'Volkswagen',21600)");

$dbh->disconnect();

上面的脚本创建一个Cars表,并将 8 行插入到该表中。

$dbh->do("CREATE TABLE Cars(Id INT PRIMARY KEY, Name TEXT, Price INT)");

do()方法执行 SQL 语句。 它将两个方法调用prepare()execute()组合为一个调用。 do()方法用于非选择语句。

$dbh->do("INSERT INTO Cars VALUES(1,'Audi',52642)");
$dbh->do("INSERT INTO Cars VALUES(2,'Mercedes',57127)");

这两行将两辆车插入表。 请注意,默认情况下,我们处于自动提交模式,其中对表的所有更改均立即生效。

sqlite> .mode column  
sqlite> .headers on

我们使用sqlite3工具验证写入的数据。 首先,我们修改数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

这是我们已写入Cars表的数据。

最后插入的行 ID

有时,我们需要确定最后插入的行的 ID。 在 Perl DBI 中,我们使用last_insert_id()方法进行查找。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=:memory:", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");

my $id = $dbh->last_insert_id("", "", "Friends", "");
print "The last Id of the inserted row is $id\n";

$dbh->disconnect();

我们在内存中创建一个Friends表。 Id自动递增。

$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");

在 SQLite 中,INTEGER PRIMARY KEY列自动增加。 还有一个AUTOINCREMENT关键字。 在INTEGER PRIMARY KEY AUTOINCREMENT中使用时,会使用稍微不同的算法来创建Id

$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");

这五个 SQL 语句将五行插入到Friends表中。

my $id = $dbh->last_insert_id("", "", "Friends", "");

使用last_insert_id()方法,我们获得最后插入的行 ID。

$ ./lastrowid.pl
The last Id of the inserted row is 5

我们看到了脚本的输出。

取得数据

在本章的最后一个示例中,我们获取一些数据。 有关数据获取的更多信息将在“查询”一章中进行讨论。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",               
    { RaiseError => 1 } 
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars WHERE Id=1" );  
$sth->execute();

my ($id, $name, $price) = $sth->fetchrow();
print "$id $name $price\n";

my $fields = $sth->{NUM_OF_FIELDS};
print "We have selected $fields field(s)\n";

my $rows = $sth->rows();
print "We have selected $rows row(s)\n";

$sth->finish();
$dbh->disconnect();

在示例中,我们从Cars表中获取一行。 我们还将找出我们选择了多少字段&行。

my $sth = $dbh->prepare( "SELECT * FROM Cars WHERE Id=1" );  
$sth->execute();

我们使用prepare()方法准备一条 SQL 语句。 SQL 字符串被发送到 SQLite 数据库引擎进行处理。 检查其语法和有效性。 该方法返回一个语句句柄。 然后执行 SQL 语句。 准备将数据发送到客户端程序。

my ($id, $name, $price) = $sth->fetchrow();
print "$id $name $price\n";

使用fetchrow()方法从数据库中检索数据。 该方法以 Perl 列表的形式从表中返回一行。

my $fields = $sth->{NUM_OF_FIELDS};

NUM_OF_FIELDS是一个语句句柄属性,它为我们提供了返回字段的数量。 在我们的情况下,我们返回了三个字段:IdNamePrice

my $rows = $sth->rows();

我们得到选定的行数。 我们仅从表中检索到一行。 rows()方法返回受影响的行数。 它可以用于SELECTUPDATEDELETE SQL语句。

$ ./fetchrow.pl 
1 Audi 52642
We have selected 3 field(s)
We have selected 1 row(s)

fetchrow.pl脚本的输出。

在 SQLite Perl 教程的这一章中,我们展示了如何建立与 SQLite 数据库的数据库连接。 我们已经解释了对数据库进行一些基本工作的脚本。

SQLite Perl 错误处理

原文: http://zetcode.com/db/sqliteperltutorial/err/

在本章中,我们将展示如何处理错误。

方法名称 描述
$h->err() 从上次调用的驱动程序方法返回本机数据库引擎错误代码。
$h->errstr() 从最后一个调用的 DBI 方法返回本机数据库引擎错误消息。
$h->state() 返回标准SQLSTATE五字符格式的状态码

以上三种方法处理错误消息。

DBI 动态属性 描述
$DBI::err | 相当于$ h->err()
$DBI::errstr | 相当于$h->errstr()
$DBI::state | 相当于$h->state()

第二张表列出了与错误处理有关的 DBI 动态属性。 这些属性的寿命很短。 应该在可能导致错误的方法之后立即使用它们。

默认错误处理

默认情况下,错误由 Perl DBI 方法返回。

#!/usr/bin/perl

use strict;
use DBI;

my $dsn = "dbi:SQLite:dbname=test.db";
my $user = '';
my $password = '';

my $dbh = DBI->connect($dsn, $user, $password) 
    or die "Can't connect to database: $DBI::errstr";

my $sth = $dbh->prepare( 
    q{ SELECT Id, Name, Price FROM Cars }
    )
    or die "Can't prepare statement: $DBI::errstr";

my $rc = $sth->execute()
    or die "Can't execute statement: $DBI::errstr";

while (my($id, $name, $price) = $sth->fetchrow()) {
    print "$id $name $price\n";
}

# check for problems which may have terminated the fetch early
warn $DBI::errstr if $DBI::err;

$sth->finish();
$dbh->disconnect();

在第一个脚本中,我们处理返回错误代码的默认行为。

my $dbh = DBI->connect($dsn, $user, $password) 
    or die "Can't connect to database: $DBI::errstr";

我们调用connect()方法来创建数据库连接。 如果尝试失败,则该方法返回undef并设置$DBI::err$DBI::errstr属性。 die()方法在失败的情况下会打印错误消息并终止脚本。

my $sth = $dbh->prepare( 
    q{ SELECT Id, Name, Price FROM Cars }
    )
    or die "Can't prepare statement: $DBI::errstr";

我们称为prepare()语句。 如果该方法失败,则die()方法将显示错误消息并终止脚本。

my $rc = $sth->execute()
    or die "Can't execute statement: $DBI::errstr";

再次。 我们调用execute()方法并检查错误。 如果失败,该方法将返回undef

warn $DBI::errstr if $DBI::err;

我们检查可能会提早终止提取方法的问题。

引发异常

每次我们调用 DBI 方法时都要检查错误,这可能很麻烦。 如果我们的脚本较大,我们很容易忘记这样做。 处理可能的错误的首选方法是引发异常。 为了引发异常,我们将RaiseError属性设置为true

#!/usr/bin/perl

use strict;
use DBI;

my $dsn = "dbi:SQLite:dbname=test.db";
my $user = '';
my $password = '';
my %attr = ( RaiseError => 1 );

my $dbh = DBI->connect($dsn, $user, $password, \%attr) 
    or die "Can't connect to database: $DBI::errstr";

my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5");   
$sth->execute();

while (my($id, $name, $price) = $sth->fetchrow()) {
    print "$id $name $price\n";
}

$sth->finish();
$dbh->disconnect();

在连接属性中,我们将RaiseError属性设置为 1。发生错误时,引发异常,而不是返回错误代码。

my %attr = ( RaiseError => 1 );

我们将RaiseError属性设置为 1。

my $dbh = DBI->connect($dsn, $user, $password, \%attr) 
    or die "Can't connect to database: $DBI::errstr";

connect()方法是我们检查返回代码的唯一方法。

my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5");   
$sth->execute();

prepare()execute()方法不检查返回错误代码。 如果失败,将引发异常,Perl DBI 将调用die()方法并打印错误消息。

错误子程序

使用HandleError连接句柄属性,我们可以设置对子例程的引用,该子例程在检测到错误时被调用。 用三个参数调用该子例程:RaiseError"PrintError"将使用的错误消息字符串,正在使用的 DBI 句柄以及失败的方法返回的第一个值(通常为undef)。

如果子例程返回错误值,则将检查RaiseErrorPrintError属性,并按常规操作。

#!/usr/bin/perl

use strict;
use DBI;

my $dsn = "dbi:SQLite:dbname=test.db";
my $user = '';
my $password = '';
my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error );

my $dbh = DBI->connect($dsn, $user, $password, \%attr) 
    or die "Can't connect to database: $DBI::errstr";

$dbh->do("UPDATE Cars SET Price=52000 WHERE Id=1");
$dbh->do("UPDATE Car SET Price=22000 WHERE Id=8");

$dbh->commit();

$dbh->disconnect();

sub handle_error {

    $dbh->rollback();

    my $error = shift;
    print "An error occurred in the script\n";
    print "Message: $error\n";
    return 1;
}

我们自己的子例程将处理该错误。

my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error );

当检测到错误时,HandleError属性提供对所调用的handle_error()子例程的引用。 AutoCommit已关闭,这意味着我们正在处理事务。

$dbh->do("UPDATE Car SET Price=22000 WHERE Id=8");

SQL 语句中有错误。 没有Car表。

sub handle_error {

    $dbh->rollback();

    my $error = shift;
    print "An error occurred in the script\n";
    print "Message: $error\n";
    return 1;
}

这是handle_error()子例程。 我们回滚这些更改。 我们打印错误消息并返回 1。如果返回 0,则会出现其他错误消息。 禁止返回与RaiseError属性相关的 1 条错误消息。

$ ./errsub.pl
An error occurred in the script
Message: DBD::SQLite::db do failed: no such table: Car

示例的输出。

eval的例子

根据 Perl DBI 文档,处理 DBI 错误的最可靠的方法是使用eval()方法。

#!/usr/bin/perl

use strict;
use DBI;
use DBI qw(:sql_types);

my $dsn = "dbi:SQLite:dbname=test.db";
my $user = '';
my $password = '';
my %attr = ( RaiseError => 1, AutoCommit => 0 );

my $dbh = DBI->connect($dsn, $user, $password, \%attr) 
    or die "Can't connect to database: $DBI::errstr";

my @data = (
    [ 1, "Audi", 52642],
    [ 2, "Mercedes", 57127],
    [ 3, "Skoda", 9000], 
    [ 4, "Volvo", 29000], 
    [ 5, "Bentley", 350000], 
    [ 6, "Citroen", 21000],
    [ 7, "Hummer", 41400],
    [ 8, "Volkswagen", 21601] 
);

eval {
    $dbh->do("DROP TABLE IF EXISTS Cars");
    $dbh->do("CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INT)");
};

my $sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) };
my $sth = $dbh->prepare( $sql );

foreach my $row (@data) {

  eval {
      $sth->bind_param( 1, @$row[0], SQL_INTEGER );
      $sth->bind_param( 2, @$row[1], SQL_VARCHAR );
      $sth->bind_param( 3, @$row[2], SQL_INTEGER );
      $sth->execute();
      $dbh->commit();
  };

  if( $@ ) {
      warn "Database error: $DBI::errstr\n";
      $dbh->rollback(); 
  }
}

$sth->finish();
$dbh->disconnect();

上面的代码示例是处理错误的最正确方法。

my %attr = ( RaiseError => 1, AutoCommit => 0 );

我们提出异常,而不是检查返回码。 我们关闭自动提交模式,然后手动提交或回滚更改。

my $sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) };
my $sth = $dbh->prepare( $sql );

我们使用占位符来防止错误和安全问题。

eval {
    $sth->bind_param( 1, @$row[0], SQL_INTEGER );
    $sth->bind_param( 2, @$row[1], SQL_VARCHAR );
    $sth->bind_param( 3, @$row[2], SQL_INTEGER );
    $sth->execute();
    $dbh->commit();
};

eval()方法内部,我们放置了容易出错的代码。 该方法捕获异常,并用错误消息填充$@特殊变量。 我们使用bind_param()方法将变量绑定到占位符。

if( $@ ) {
    warn "Database error: $DBI::errstr\n";
    $dbh->rollback(); 
}

如果发生错误,我们将打印错误消息并回滚更改。

在 SQLite Perl 教程的这一部分中,我们正在讨论错误处理。

使用 Perl 的 SQLite 查询

原文: http://zetcode.com/db/sqliteperltutorial/queries/

我们已经建立了到数据库的连接。 现在我们要修改并从数据库中获取数据。

使用SELECT语句从数据库中检索数据。 在 Perl DBI 中,首先我们使用prepare()方法准备 SQL 语句。 SQL 字符串被发送到数据库引擎,该引擎检查语句的有效性,语法,并在某些数据库中检查执行某些查询的用户权限。 如果一切正常,则对语句句柄的引用将返回到 Perl 脚本。 下一步是对execute()方法的调用。 该方法在数据库内执行查询。 此时,结果保留在数据库中。 Perl 脚本尚不包含数据。 对于非选择语句,execute()方法返回已知的受影响的行数。 在最后一步中,从数据库中获取数据。 数据被逐行提取并填充到 Perl 数据结构中。

Perl DBI 有几种从数据库表中获取数据的方法。

方法 描述
fetchrow_arrayref() 获取下一行数据并返回对数组的引用。
fetchrow_array() 获取下一行数据,并将其作为列表返回。
fetchrow_hashref() 获取下一行数据,并将其返回为对哈希的引用。
fetchall_arrayref() 提取所有数据&返回对每行有一个引用的数组的引用。
fetch() 该方法是fetchrow_arrayref()的别名。
fetchrow() 该方法是fetchrow_array()的别名。

准备并执行完 SQL 语句后,我们调用可用的访存方法之一。

方法 描述
selectrow_arrayref() prepare()execute()fetchrow_arrayref()合并为一个调用
selectrow_hashref() prepare()execute()fetchrow_hashref()合并为一个调用
selectrow_array() prepare()execute()fetchrow_array()合并为一个调用。
selectall_arrayref() prepare()execute()fetchall_arrayref()合并为一个调用。
selectall_hashref() prepare()execute()fetchall_hashref()合并为一个调用。
selectcol_arrayref() 合并prepare()execute()并从所有行中提取一个col到一个调用中。

在第二张表中,我们列出了工具方法,这些方法将三个方法组合为一个调用。 它们是方便的方法。

提取方法

在第一个示例中,我们将演示fetchrow_arrayref()方法的用法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5");
$sth->execute();

my $row;
while ($row = $sth->fetchrow_arrayref()) {
    print "@$row[0] @$row[1] @$row[2]\n";
}

$sth->finish();
$dbh->disconnect();

在示例中,我们从Cars表中选择 5 行。 使用fetchrow_arrayref()方法检索数据。

my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5");
$sth->execute();

这是数据检索过程的前两个阶段。 我们准备并执行SELECT语句。

my $row;
while ($row = $sth->fetchrow_arrayref()) {
    print "@$row[0] @$row[1] @$row[2]\n";
}

现在,我们正在获取数据。 fetchrow_arrayref()方法获取下一行数据,并返回对包含字段值的数组的引用。 当没有更多的行了时,我们将方法放入终止的while循环中。

$ ./fetchrow_arrayref.pl
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

示例输出。

在第二个示例中,我们将使用fetchrow_array()方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 5" );  
$sth->execute();

my @row;
while (@row = $sth->fetchrow_array()) {
    print "@row\n";
}

$sth->finish();
$dbh->disconnect();

在此脚本中,我们连接到数据库,并使用fetchrow_array()方法一张一张地获取 5 行Cars表。

my @row;
while (@row = $sth->fetchrow_array()) {
    print "@row\n";
}

fetchrow_array()方法获取下一行数据,并将其作为包含字段值的列表返回。 我们使用while循环遍历所有 5 行。

在下一个示例中,我们将按列名称获取数据。 为此,我们将使用fetchrow_hashref()方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 5" );  
$sth->execute();

my $row;
while($row = $sth->fetchrow_hashref()) {
    print "$row->{Id} $row->{Name} $row->{Price}\n";
}

$sth->finish();
$dbh->disconnect();

在示例中,数据以对 Perl 哈希的引用形式返回。

my $row;
while($row = $sth->fetchrow_hashref()) {
    print "$row->{Id} $row->{Name} $row->{Price}\n";
}

fetchrow_hashref()方法获取下一行数据,并将其返回为对包含字段名称和字段值对的哈希的引用。 使用此方法,我们可以按列名称检索值。

在本节的最后一个示例中,我们一步一步从SELECT语句中获取所有数据。 我们使用fetchall_arrayref()方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5");
$sth->execute();

my $all = $sth->fetchall_arrayref();

foreach my $row (@$all) {
    my ($id, $name, $price) = @$row;
    print "$id $name $price\n";
}

$sth->finish();
$dbh->disconnect();

该示例从Cars表中选择并打印五行。

my $all = $sth->fetchall_arrayref();

我们通过一个方法调用获取所有数据。 fetchall_arrayref()方法返回对数组的引用,该数组每行包含一个引用。

foreach my $row (@$all) {
    my ($id, $name, $price) = @$row;
    print "$id $name $price\n";
}

我们使用foreach循环遍历检索到的数据。

转储数据

Perl DBI 有一个称为dump_results()的特殊方法。 此方法被设计为用于原型设计和测试查询的便捷工具。 它使用neat_list()方法格式化和编辑字符串以供人类阅读。 不建议将其用于数据传输应用。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",     
    "",
    "",          
    { RaiseError => 1 } 
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 5" );  
$sth->execute();

$sth->dump_results();      

$sth->finish();
$dbh->disconnect();

在示例中,我们将转储结果集中的所有数据。

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 5" );  
$sth->execute();

SQL 语句从Cars表中选择五行。 和所有三列。

$sth->dump_results();  

dump_results()从语句句柄中选择所有行并进行打印。 这是用于原型制作和测试的方法。

$ ./dump.pl 
1, 'Audi', 52642
2, 'Mercedes', 57127
3, 'Skoda', 9000
4, 'Volvo', 29000
5, 'Bentley', 350000
5 rows

这是示例的输出。

便利方法

我们将展示两个使用上述便利方法的示例。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $ary = $dbh->selectrow_arrayref("SELECT * FROM Cars WHERE Id = 5");
print join(" ", @$ary), "\n";

$dbh->disconnect();

在第一个代码示例中,我们将调用selectrow_arrayref()方法。 我们从Cars表中选择第五行。

my $ary = $dbh->selectrow_arrayref("SELECT * FROM Cars WHERE Id = 5");

selectrow_arrayref()方法将prepare()execute()fetchrow_arrayref()合并为一个调用。 它返回对语句第一行数据的引用。 请注意,我们不使用语句句柄。 我们使用$dbh数据库句柄对象。

print join(" ", @$ary), "\n";

我们将行打印到控制台。

下面的示例显示selectall_arrayref()方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $all = $dbh->selectall_arrayref("SELECT * FROM Cars LIMIT 5");

foreach my $row (@$all) {
    my ($id, $name, $price) = @$row;
    print "$id $name $price\n";
}

$dbh->disconnect();

我们再次从Cars表中检索 5 行。

my $all = $dbh->selectall_arrayref("SELECT * FROM Cars LIMIT 5");

selectall_arrayref()方法返回对数组的引用,其中包含对获取的每一行数据的数组的引用。 提供的 SQL 语句从Cars表中选择 5 行。 请注意,我们既没有调用prepare()也没有调用execute()方法。 这是因为selectall_arrayref()方法将prepare()execute()fetchall_arrayref()组合到一个调用中。

foreach my $row (@$all) {
    my ($id, $name, $price) = @$row;
    print "$id $name $price\n";
}

我们遍历提取的数组数组并将数据打印到终端。

$ ./retrieve.pl
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

这是示例的输出。

参数化查询

现在,我们将关注参数化查询。 当使用参数化查询时,我们使用占位符,而不是直接将值写入语句。 参数化查询可提高安全性和性能。

当程序收到用户的输入时,程序员必须始终保持谨慎。 稍后,我们将值绑定到预备语句,而不是从用户输入构建字符串。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                         
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $id = 3;
my $sth = $dbh->prepare( "SELECT * FROM Cars WHERE Id = ?" );  
$sth->execute($id);

my $ret = $sth->fetch();

foreach my $row (@$ret) {
    print "$row ";
} 

print "\n";

$sth->finish();
$dbh->disconnect();

在代码示例中,我们从表中选择特定的行。 SQL 语句具有一个占位符,稍后将在代码中填充该占位符。

my $id = 3;

这可能是用户的输入。

my $sth = $dbh->prepare( "SELECT * FROM Cars WHERE Id = ?" ); 

问号?是值的占位符。 该值稍后添加。

$sth->execute($id);

execute()语句采用一个参数,该参数绑定到占位符。

$ ./parameterized.pl
3 Skoda 9000 

我们已经使用参数化查询从Cars表中检索了一行。

在第二个示例中,我们将使用便利选择方法之一使用参数化查询。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                         
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $id = 2;

my @ary = $dbh->selectrow_array("SELECT * FROM Cars WHERE Id = ?", undef, $id);
print join(" ", @ary), "\n";

$dbh->disconnect();

我们需要在SELECT查询中填充一个占位符。

my @ary = $dbh->selectrow_array("SELECT * FROM Cars WHERE Id = ?", undef, $id);

selectrow_array()方法的第三个参数采用占位符的值。

在 SQLite Perl 教程的这一部分中,我们已经演示了如何使用各种 Perl DBI 方法从数据库中获取数据。

使用 Perl 绑定 SQLite 参数&列

原文: http://zetcode.com/db/sqliteperltutorial/bind/

SQL 语句通常是动态构建的。 用户提供一些输入,并将其内置到语句中。 程序员每次处理用户的输入时都必须谨慎。 它具有一些严重的安全隐患。 动态构建 SQL 语句的推荐方法是使用参数绑定。

绑定参数可以防止 SQL 注入程序。 它会自动转义一些特殊字符并允许正确处理它们。 当我们准备语句并绑定参数时,许多数据库也会显着提高其性能。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $name = "Volkswagen"; 

my $sth = $dbh->prepare("SELECT * FROM Cars WHERE Name = ?");
$sth->execute($name);

my ($id, $name, $price) = $sth->fetchrow();
print "$id $name $price\n";

$sth->finish();
$dbh->disconnect();

该示例从Cars表中为特定的汽车名称选择一行。

my $name = "Volkswagen"; 

这是一个可能来自用户的值。 例如从 HTML 表单。

my $sth = $dbh->prepare("SELECT * FROM Cars WHERE Name = ?");

问号?是值的占位符。 它将稍后添加到脚本中。

$sth->execute($name);

execute()方法中,我们将值绑定到占位符。

以下示例与上一个示例相同; 这次我们使用bind_param()方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $name = "Volkswagen"; 

my $sth = $dbh->prepare("SELECT * FROM Cars WHERE Name = ?");
$sth->bind_param(1, $name);
$sth->execute();

my ($id, $name, $price) = $sth->fetchrow();
print "$id $name $price\n";

$sth->finish();
$dbh->disconnect();

使用bind_param()方法检索带有参数绑定的行。

$sth->bind_param(1, $name);

bind_param()方法采用一个值,并将其与 SQL 语句内的占位符关联。 可以有更多的占位符。 占位符从 1 开始编号。

quote参数

使用占位符并将参数绑定到它们是处理动态 SQL 语句构建的最佳方法。 有时不能使用占位符。 例如,当我们要动态选择一个表名时。 在这种情况下,我们可以连接 SQL 字符串并使用quote()quote_identifier()方法。 我们必须对变量使用这些方法,否则会引入严重的安全性错误。

quote()方法引用字符串字面值,以用作 SQL 语句中的字面值。 它转义包含在字符串中的任何特殊字符(例如引号),并添加所需类型的外部引号。 quote_identifier()方法引用在 SQL 语句中使用的标识符(表名等)。 它转义包含的任何特殊字符(例如双引号),并添加所需类型的外部引号。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $table = "Cars";
my $name = "Volkswagen";

my $sql = sprintf "SELECT * FROM %s WHERE Name = %s", 
    $dbh->quote_identifier($table), $dbh->quote($name);

my $sth = $dbh->prepare($sql);
$sth->execute();

my @row;
while (@row = $sth->fetchrow_array()) {
    print "@row\n";
}

$sth->finish();
$dbh->disconnect();

在该示例中,我们使用quote()quote_identifier()方法动态构建 SQL 语句字符串。

my $table = "Cars";
my $name = "Volkswagen";

这些是在 SQL 语句中使用的 Perl 标量。

my $sql = sprintf "SELECT * FROM %s WHERE Name = %s", 
    $dbh->quote_identifier($table), $dbh->quote($name);

我们有一个更复杂的 SQL 语句。 无法使用占位符构建此语句。 我们使用quote方法来引用提供的标量。

绑定列

使用获取方法时,我们将返回的值复制到 Perl 变量中。 通过绑定列可以简化此过程并使其更快。 Perl DBI 具有bind_col()bind_columns()方法,它们将 Perl 变量与表列相关联。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 4" );  
$sth->execute();

$sth->bind_columns(\my($id, $name, $price));

while ($sth->fetchrow_arrayref()) {
    print "$id $name $price\n";
}

$sth->finish();
$dbh->disconnect();

在示例中,我们将Cars表的三列绑定到$id$name$price变量。

$sth->bind_columns(\my($id, $name, $price));

我们使用bind_columns()方法将变量绑定到Cars表的列。

while ($sth->fetchrow_arrayref()) {
    print "$id $name $price\n";
}

我们遍历返回的数据并将值打印到控制台。

在 SQLite Perl 教程的这一部分中,我们讨论了绑定参数。

使用 Perl 在 SQLite 中处理图像

原文: http://zetcode.com/db/sqliteperltutoriimg/

在 SQLite Perl 教程的这一章中,我们将使用图像文件。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论是否在数据库中保存图像。

sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,代表二进制大对象。

插入图像

在第一个示例中,我们将图像插入 SQLite 数据库。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",                         
    { RaiseError => 1 }
) or die $DBI::errstr;

open IMAGE, "mushrooms.jpg" or die $!;

my ($image, $buff);
while(read IMAGE, $buff, 1024) {
    $image .= $buff;
}

my $stm = $dbh->prepare("INSERT INTO Images(Data) VALUES (?)");
$stm->bind_param(1, $image, DBI::SQL_BLOB);
$stm->execute();

close(IMAGE);
$stm->finish();
$dbh->disconnect();

我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的Images表中。

open IMAGE, "mushrooms.jpg" or die $!;

我们打开一个图像。 这是称为mushrooms.jpg的 JPG 图像。

my ($image, $buff);
while(read IMAGE, $buff, 1024) {
    $image .= $buff;
}

我们从图像文件读取二进制数据。

my $sth = $dbh->prepare("INSERT INTO Images(Data) VALUES (?)");
$sth->bind_param(1, $image, DBI::SQL_BLOB);
$sth->execute();

三行代码准备 SQL 语句,将图像数据绑定到该语句并执行它。

close(IMAGE);
$sth->finish();
$dbh->disconnect();

最后,我们正在释放资源。

读取图像

在本节中,我们将执行相反的操作。 我们将从数据库表中读取图像。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",                    
    { RaiseError => 1 }
) or die $DBI::errstr;

my $stm = $dbh->prepare("SELECT Data FROM Images WHERE Id=1");
$stm->execute();
my $image = $stm->fetch();

print "Image: $image";

open IMAGE, ">mushrooms2.jpg" or die $!;
print IMAGE @$image;
close(IMAGE);

$stm->finish();
$dbh->disconnect();

我们从IMAGE表中读取图像数据,并将其写入另一个文件,我们称为蘑菇2.jpg

my $sth = $dbh->prepare("SELECT Data FROM Images WHERE Id=1");
$sth->execute();
my $image = $sth->fetch();

这三行从表中选择图像数据。

open IMAGE, ">mushrooms2.jpg" or die $!;
print IMAGE @$image;
close(IMAGE);

我们打开一个新的图像文件,并将检索到的数据写入该文件。 然后我们关闭文件。

SQLite Perl 教程的这一部分专门用于读取和写入图像。

使用 Perl 获取 SQLite 元数据

原文: http://zetcode.com/db/sqliteperltutorial/meta/

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

方法名称 描述
column_info() 提供有关列的信息
table_info() 提供有关表的信息
primary_key_info() 提供有关表中主键的信息
foreign_key_info() 提供有关表中外键的信息

上表列出了四种用于检索元数据的 Perl DBI 方法。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",          
    "",
    "",     
    { RaiseError => 1 } 
) or die $DBI::errstr;

my $sth = $dbh->primary_key_info(undef, "main", "Cars");
my @ary = $sth->fetchrow_array();

print join(" ", @ary), "\n";

$sth->finish();
$dbh->disconnect();

在第一个示例中,我们将在Cars表中找到有关主键的信息。

my $sth = $dbh->primary_key_info(undef, "main", "Cars");

primary_key_info()返回一个活动语句句柄,该句柄可用于获取有关构成表主键的列的信息。

my @ary = $sth->fetchrow_array();

从语句句柄,我们检索信息。

$ ./pkinfo.pl
 main Cars Id 1 PRIMARY KEY

从输出中我们可以看到Cars表中有一个主键。 主键是第一列,名为Id

在下一个示例中,我们将找到有关Cars表的一些数据。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db",  
    "",
    "",                  
    { RaiseError => 1 }
) or die $DBI::errstr;

my $sth = $dbh->prepare( "PRAGMA table_info(Cars)" );  
$sth->execute();

my @row;
while (@row = $sth->fetchrow_array()) {
    print "@row\n";
}

$sth->finish();
$dbh->disconnect();

在此示例中,我们发出PRAGMA table_info(tableName)命令,以获取有关Cars表的一些元数据信息。

my $sth = $dbh->prepare( "PRAGMA table_info(Cars)" );  
$sth->execute();

PRAGMA table_info(Cars)命令为Cars表中的每一列返回一行。 结果集中的列包括列顺序号,列名称,数据类型,该列是否可以为NULL以及该列的默认值。

my @row;
while (@row = $sth->fetchrow_array()) {
    print "@row\n";
}

我们打印选定的数据。

$ ./pragma_table.pl 
0 Id INT 0  0
1 Name TEXT 0  0
2 Price INT 0  0

示例的输出。

接下来,我们将打印Cars表中的所有行及其列名。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my $sth = $dbh->prepare( "SELECT * FROM Cars LIMIT 8" );  
my $headers = $sth->{NAME};

my ($id, $name, $price) = @$headers;
printf  "%s %-10s %s\n", $id, $name, $price;

$sth->execute();

my $row;
while($row = $sth->fetchrow_hashref()) {
    printf "%2d %-10s %d\n", $row->{Id}, $row->{Name}, $row->{Price};
}

$sth->finish();
$dbh->disconnect();

我们将Cars表的内容打印到控制台。 现在,我们也包括列的名称。 记录与列名对齐。

my $headers = $sth->{NAME};

我们从语句对象获得列名。

my ($id, $name, $price) = @$headers;
printf "%s %-10s %s\n", $id, $name, $price;

列名将打印到控制台。 我们使用printf函数应用一些格式。

my $row;
while($row = $sth->fetchrow_hashref()) {
    printf "%2d %-10s %d\n", $row->{Id}, $row->{Name}, $row->{Price};
}

数据被检索,格式化并打印到终端。

$ ./columnheaders.pl
Id Name       Price
 1 Audi       52642
 2 Mercedes   57127
 3 Skoda      9000
 4 Volvo      29000
 5 Bentley    350000
 6 Citroen    21000
 7 Hummer     41400
 8 Volkswagen 21600

输出。

在与元数据有关的最后一个示例中,我们将列出test.db数据库中的所有表。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(          
    "dbi:SQLite:dbname=test.db", 
    "",                          
    "",                          
    { RaiseError => 1 },         
) or die $DBI::errstr;

my @tables = $dbh->tables(); 

foreach my $table ( @tables ) {
     print "Table: $table\n"; 
}

$dbh->disconnect();

该代码示例将当前数据库中的所有可用表打印到终端。

my @tables = $dbh->tables();

表名使用tables()方法检索。

$ ./list_tables.pl
Table: "main"."sqlite_master"
Table: "temp"."sqlite_temp_master"
Table: "main"."Cars"
Table: "main"."Friends"
Table: "main"."Images"

这些是我们系统上的表。

在 SQLite Perl 教程的这一部分中,我们使用了数据库元数据。

使用 Perl 进行 SQLite 事务

原文: http://zetcode.com/db/sqliteperltutorial/trans/

在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们介绍 Perl 脚本,该脚本显示如何在 Perl DBI 中处理事务。 我们还将讨论自动提交模式,这对于理解事务是必不可少的。

定义

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 在自动提交模式中,更改立即生效。 要处理事务,我们要么关闭自动提交模式,要么使用begin_work()方法启动事务。 事务以commit()rollback()方法结束。

默认情况下,数据库连接处于自动提交模式。 AutoCommit数据库句柄属性用于设置或读取自动提交模式。

AutoCommit打开时,对begin_work()的调用将AutoCommit关闭。 commit()rollback()方法重新打开AutoCommit。 如果关闭AutoCommit属性,然后再调用begin_work()方法,则会收到一条错误消息,提示我们已经在事务中。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。 手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE

例子

现在,我们将有一些可用于事务处理的脚本。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(         
    "dbi:SQLite:dbname=test.db",
    "",                         
    "",                         
    { RaiseError => 1, AutoCommit => 0 },         
) or die $DBI::errstr;

$dbh->do("DROP TABLE IF EXISTS Friends");
$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");

$dbh->disconnect();

我们创建一个Friends表,并尝试用数据填充它。 但是,正如我们将看到的,数据不会被提交。

{ RaiseError => 1, AutoCommit => 0 }, 

我们已经将AutoCommit参数设置为 0。更改不会自动提交。 而且没有提交语句。 因此,更改不会写入数据库。

$ ./noautocommit.pl
$ sqlite3 test.db 
SQLite version 3.7.7 2011-06-23 19:49:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
Cars    Images

我们的数据库中没有Friends表。

在第二个示例中,我们将使用commit()方法将数据写入数据库。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(         
    "dbi:SQLite:dbname=test.db",
    "",                         
    "",                         
    { RaiseError => 1, AutoCommit => 0},         
) or die $DBI::errstr;

$dbh->do("DROP TABLE IF EXISTS Friends");
$dbh->do("CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Tom')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Rebecca')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Jim')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Robert')");
$dbh->do("INSERT INTO Friends(Name) VALUES ('Julian')");

$dbh->commit();

$dbh->disconnect();

当关闭自动提交模式时,每个语句都在事务内,直到我们调用commit()方法为止。

$dbh->commit();

所有更改都将写入数据库。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian

我们使用sqlite3命令行工具验证是否已写入更改。

当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(         
    "dbi:SQLite:dbname=test.db",
    "",                         
    "",                         
    { RaiseError => 1, AutoCommit => 0},         
) or die $DBI::errstr;

$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

$dbh->commit();

$dbh->disconnect();

在代码示例中,自动提交已关闭。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。

$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

表名称不正确。 数据库中没有Friend表。

$ ./rollingback.pl
DBD::SQLite::db do failed: no such table: Friend at ./rollingback.pl line 15.
DBD::SQLite::db do failed: no such table: Friend at ./rollingback.pl line 15.
Issuing rollback() due to DESTROY without explicit disconnect() 
of DBD::SQLite::db handle dbname=test.db at ./rollingback.pl line 15.

运行示例将显示此错误消息。 事务回滚。

$ sqlite3 test.db 
SQLite version 3.7.7 2011-06-23 19:49:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian
sqlite> 

即使第一个UPDATE语句正确,在Friends表中也没有发生任何变化。

正如我们在教程中已经提到的那样,默认模式是自动提交。 在这种模式下,我们可以使用begin_work()方法开始新的事务,并使用commit()rollback()完成它。 begin_work()方法将关闭自动提交,commit()rollback()方法将重新打开自动提交。

#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect(         
    "dbi:SQLite:dbname=test.db",
    "",                         
    "",                         
    { RaiseError => 1, HandleError=>\&handle_error },         
) or die $DBI::errstr;

$dbh->begin_work();

$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

$dbh->commit();

$dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");

$dbh->disconnect();

sub handle_error {

    my $error = shift;
    print "An error occurred in the script\n";
    print "Message: $error\n";
    return 1;
}

再次,我们有一个不正确的第二条 SQL 语句。 这次,我们没有明确关闭自动提交。

{ RaiseError => 1, HandleError=>\&handle_error },  

我们将把错误处理委托给handle_error()子例程。

$dbh->begin_work();

使用begin_work()方法,我们开始一个新事务。 自动提交已关闭。

$dbh->do("UPDATE Friends SET Name='Thomas' WHERE Id=1");
$dbh->do("UPDATE Friend SET Name='Bob' WHERE Id=4");

这两个语句构成一个事务。 第二个是不正确的。

sub handle_error {

    my $error = shift;
    print "An error occurred in the script\n";
    print "Message: $error\n";
    return 1;
}

当我们遇到错误时,将调用此子例程。 我们打印一条错误消息。 请注意,该脚本不会退出。

$dbh->do("INSERT INTO Friends(Name) VALUES('Ronald')");

事务已回滚,我们没有退出脚本。 它继续。 回滚后,自动提交已重新打开。 在Friends表中添加了新行。

$ ./rollingback2.pl
An error occurred in the script
Message: DBD::SQLite::db do failed: no such table: Friend

我们可以从the handle_error()子例程中看到我们的自定义错误消息。

sqlite> SELECT * FROM Friends;
1|Thomas
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Ronald

一个新朋友被插入了表。

在 SQLite Perl 教程的这一部分中,我们处理了事务。

SQLite Ruby 教程

原文: http://zetcode.com/db/sqliteruby/

这是针对 SQLite 数据库的 Ruby 编程教程。 它涵盖了使用 Ruby 进行 SQLite 编程的基础。 它使用sqlite3模块。 这些示例是在 Linux 上创建并测试的。

目录

SQLite & Ruby

SQLite 是嵌入式关系数据库引擎。 它是一个自包含,无服务器,零配置和事务型 SQL 数据库引擎。 SQLite 实现了 SQL 的大多数 SQL-92 标准。 SQLite 引擎不是独立的进程。 而是将其静态或动态链接到应用。 SQLite 数据库是单个普通磁盘文件,可以位于目录层次结构中的任何位置。

Ruby 是一种动态的,反射性的,通用的面向对象的编程语言。 最近,它在 Web 编程中变得非常流行,这主要归功于 Ruby on Rails 框架的成功。

Tweet

相关教程

SQLite 教程涵盖了 SQLite 嵌入式数据库引擎。 SQLite Perl 教程SQLite PHP 教程SQLite Python 教程提供了 Perl,PHP 和 Python 语言的编程教程。

MySQL 快速教程

原文: http://zetcode.com/databases/mysqltutorial/quick/

MySQL 快速教程将快速向您介绍 MySQL 数据库的基础知识。

世界数据库

MySQL 文档网站提供了一些示例数据库。 我们将使用世界数据库。 数据已过时,但对于我们的测试目的并不重要。

$ wget http://downloads.mysql.com/docs/world.sql.gz

我们使用wget工具下载压缩文件。

$ ls -sh world.sql.gz 
92K world.sql.gz

压缩文件大约有 92KB。

$ gunzip world.sql.gz

我们将文件解压缩。 我们有一个world.sql文件。

$ ls -hs world.sql 
392K world.sql

未压缩的文件有 392KB。

$ mysql -uroot -p

我们使用根帐户连接到服务器。 我们需要root帐户来创建新数据库,并为我们的新数据库的测试帐户授予权限。

mysql> CREATE DATABASE world;

世界数据库已创建。

mysql> USE world;

我们转到世界数据库。 现在,世界数据库是当前数据库。

mysql> source world.sql

我们通过执行world.sql SQL 脚本来构建世界数据库的表。 需要一些时间。

mysql> GRANT ALL ON world.* TO 'user12'@'localhost';

我们向user12授予对世界数据库的所有对象的特权。

mysql> quit
Bye

$ mysql -u user12 -p
Enter password: 

mysql> USE world;

我们退出连接。 重新连接user12测试帐户并更改为world数据库。 我们准备工作。

检查数据库

在本节中,我们将大致看一下world数据库的表。

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0,00 sec)

我们用SHOW TABLES语句显示所有可用表。 有三种。

mysql> DESCRIBE city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
5 rows in set (0,00 sec)

通过DESCRIBE语句,我们可以看到City表的表结构。 我们看到列名及其数据类型。 加上其他重要信息。

mysql> SHOW CREATE TABLE city;

如果我们想找出创建City表的 SQL,我们将发出SHOW CREATE TABLE city语句。

$ mysqldump -u root -p world city > city.sql

在这里,我们使用mysqldump工具备份city表。

mysql> DROP TABLE city;

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| country         |
| countrylanguage |
+-----------------+
2 rows in set (0,00 sec)

我们使用DROP TABLE语句删除city表。 随后的语句验证该表已删除。

mysql> source city.sql

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0,00 sec)

我们从备份中重新创建city表。 source命令执行备份city.sql脚本。

查询

查询用于从数据库表中查找数据。

限制数据输出

数据库表中有数千行。 它们无法全部显示在屏幕上。 我们可以使用LIMIT子句控制要显示的行数。

mysql> SELECT Id, Name, Population FROM city limit 10;
+----+----------------+------------+
| Id | Name           | Population |
+----+----------------+------------+
|  1 | Kabul          |    1780000 |
|  2 | Qandahar       |     237500 |
|  3 | Herat          |     186800 |
|  4 | Mazar-e-Sharif |     127800 |
|  5 | Amsterdam      |     731200 |
|  6 | Rotterdam      |     593321 |
|  7 | Haag           |     440900 |
|  8 | Utrecht        |     234323 |
|  9 | Eindhoven      |     201843 |
| 10 | Tilburg        |     193238 |
+----+----------------+------------+
10 rows in set (0,00 sec)

在上面的查询中,我们显示City表的五个列中的三个。 表中有很多行。 我们将查询限制为前 10 行。

mysql> SELECT Id, Name, Population FROM city limit 15, 5;
+----+-------------------+------------+
| Id | Name              | Population |
+----+-------------------+------------+
| 16 | Haarlem           |     148772 |
| 17 | Almere            |     142465 |
| 18 | Arnhem            |     138020 |
| 19 | Zaanstad          |     135621 |
| 20 | ´s-Hertogenbosch  |     129170 |
+----+-------------------+------------+
5 rows in set (0,00 sec)

LIMIT子句后面可以有两个数字。 第一个是偏移量,第二个是要显示的行数。 我们的查询显示第 16-20 行。

mysql> pager less
PAGER set to 'less'
mysql> SELECT * FROM city;
+------------------------------------+------------+
| Name                               | Population |
+------------------------------------+------------+
| Kabul                              |    1780000 |
| Qandahar                           |     237500 |
| Herat                              |     186800 |
...
:

由于city表具有四千多行,因此我们无法在一个屏幕中看到它们。 我们可以使用pager命令以较少的程序显示数据。 我们可以使用光标键或向下翻页,向上翻页键浏览数据。 如果我们想使用默认设置,只需点击不带任何参数的寻呼机即可。

$ mysql -u user12 -p world -e "SELECT * FROM city" > city
Enter password: 
$ ls -sh city
144K city

mysql命令工具可以非交互方式使用。 我们在-e选项之后指定 SQL 语句,然后将结果重定向到城市文件。 现在我们可以使用任何文本编辑器来显示数据。

COUNT()MAX()MIN()函数

COUNT()MAX()MIN()是 MySQL 聚合函数,可从聚合数据中计算一些值。

mysql> SELECT COUNT(Id) AS '# of cities' FROM city;
+-------------+
| # of cities |
+-------------+
|        4079 |
+-------------+
1 row in set (0,00 sec)

表中有 4079 个城市。 我们使用内置的COUNT()函数找出行数。

mysql> SELECT Name, Population FROM city                                            
    -> WHERE Population = (SELECT Max(Population) FROM city);
+-----------------+------------+
| Name            | Population |
+-----------------+------------+
| Mumbai (Bombay) |   10500000 |
+-----------------+------------+
1 row in set (0,08 sec)

上面的查询显示了表中人口最多的城市。 SQL 是称为子查询的特殊查询类型。 外部查询使用内部查询返回的数据。 内部查询以括号为界。

mysql> SELECT Name, Population FROM city 
    -> WHERE Population = (SELECT Min(Population) FROM city);
+-----------+------------+
| Name      | Population |
+-----------+------------+
| Adamstown |         42 |
+-----------+------------+
1 row in set (0,02 sec)

此子查询显示表中人口最少的城市。

使用WHERE子句选择特定的行

WHERE子句可用于过滤结果。 它提供了选择条件,仅从数据中选择特定的行。

mysql> SELECT Name, Population FROM city
    -> WHERE Population > 1000000;
+--------------------------+------------+
| Name                     | Population |
+--------------------------+------------+
| Kabul                    |    1780000 |
| Alger                    |    2168000 |
| Luanda                   |    2022000 |
| Buenos Aires             |    2982146 |
| La Matanza               |    1266461 |
| Córdoba                  |    1157507 |
| Yerevan                  |    1248700 |
| Sydney                   |    3276207 |
...

上面的 SQL 语句返回人口超过一百万的所有城市。

mysql> SELECT Name FROM city WHERE Name LIKE 'Kal%';
+-------------+
| Name        |
+-------------+
| Kalookan    |
| Kalyan      |
| Kalemie     |
| Kallithea   |
| Kalisz      |
| Kaliningrad |
| Kaluga      |
+-------------+
7 rows in set (0,00 sec)

在这里,我们选择所有以"Kal"开头的城市名称。 我们在表中找到了七个城市。 我们可以使用LIKE子句在列中查找特定的模式。

mysql> SELECT Name, Population FROM city WHERE ID IN (5, 23, 432, 2021);
+------------+------------+
| Name       | Population |
+------------+------------+
| Amsterdam  |     731200 |
| Dordrecht  |     119811 |
| Eunápolis  |      96610 |
| Jining     |     265248 |
+------------+------------+
4 rows in set (0,05 sec)

此 SQL 代码返回 ID 为 5、23、432 和 2021 的行的城市及其人口。

mysql> SELECT * FROM city WHERE Name = 'Bratislava';
+------+------------+-------------+------------+------------+
| ID   | Name       | CountryCode | District   | Population |
+------+------------+-------------+------------+------------+
| 3209 | Bratislava | SVK         | Bratislava |     448292 |
+------+------------+-------------+------------+------------+
1 row in set (0,00 sec)

通过上面的 SQL 语句,我们选择一个特定城市(即布拉迪斯拉发)的所有列。

mysql> SELECT Name, Population FROM city
    -> WHERE Population BETWEEN 670000 AND 700000;
+----------------+------------+
| Name           | Population |
+----------------+------------+
| Teresina       |     691942 |
| Natal          |     688955 |
| Bandar Lampung |     680332 |
| Gwalior        |     690765 |
| Kermanshah     |     692986 |
| Palermo        |     683794 |
| Toronto        |     688275 |
| Huainan        |     700000 |
| Jixi           |     683885 |
| Antananarivo   |     675669 |
| Chihuahua      |     670208 |
| Kano           |     674100 |
| Tunis          |     690600 |
+----------------+------------+
13 rows in set (0,03 sec)

假设我们想找出人口在两个特定值之间的城市。 为此有一个BETWEEN运算符。 我们发现了 13 个城市,人口在 670,000 到 700,000 之间。

排序数据

可以使用ORDER BY子句完成订购数据。

mysql> SELECT Name, Population FROM city
    -> ORDER BY Population DESC LIMIT 10;
+-------------------+------------+
| Name              | Population |
+-------------------+------------+
| Mumbai (Bombay)   |   10500000 |
| Seoul             |    9981619 |
| São Paulo         |    9968485 |
| Shanghai          |    9696300 |
| Jakarta           |    9604900 |
| Karachi           |    9269265 |
| Istanbul          |    8787958 |
| Ciudad de México  |    8591309 |
| Moscow            |    8389200 |
| New York          |    8008278 |
+-------------------+------------+
10 rows in set (0,03 sec)

我们找到了十个人口最多的城市。 我们按人口从人口最多的城市到人口最少的城市排序。 我们使用LIMIT子句限制输出。

mysql> SELECT Name, Population FROM city
    -> ORDER BY Population ASC LIMIT 10;
+---------------------+------------+
| Name                | Population |
+---------------------+------------+
| Adamstown           |         42 |
| West Island         |        167 |
| Fakaofo             |        300 |
| Città del Vaticano  |        455 |
| Bantam              |        503 |
| Yaren               |        559 |
| The Valley          |        595 |
| Alofi               |        682 |
| Flying Fish Cove    |        700 |
| Kingston            |        800 |
+---------------------+------------+
10 rows in set (0,02 sec)

在这里,我们得到了人口最少的城市。 这次我们以升序对数据进行排序。 为此,我们使用ASC关键字。

mysql> SELECT Name, Population FROM city ORDER By Name LIMIT 10;
+------------------------+------------+
| Name                   | Population |
+------------------------+------------+
| A Coruña (La Coruña)   |     243402 |
| Aachen                 |     243825 |
| Aalborg                |     161161 |
| Aba                    |     298900 |
| Abadan                 |     206073 |
| Abaetetuba             |     111258 |
| Abakan                 |     169200 |
| Abbotsford             |     105403 |
| Abeokuta               |     427400 |
| Aberdeen               |     213070 |
+------------------------+------------+
10 rows in set (0.01 sec)

在上面的 SQL 语句中,我们按城市名称排序数据并获得前十个城市。

分组数据

GROUP BY子句用于将具有相同值的数据库记录组合到单个记录中。 它通常与聚合函数一起使用。

mysql> SELECT District, SUM(Population) FROM city
    -> WHERE District = 'New York' GROUP BY District;
+----------+-----------------+
| District | SUM(Population) |
+----------+-----------------+
| New York |         8958085 |
+----------+-----------------+
1 row in set (0,09 sec)

上面的 SQL 语句返回在我们数据库中列出的纽约地区城镇的总人数。

mysql> SELECT Name, District FROM city WHERE District = 'New York';
+-----------+----------+
| Name      | District |
+-----------+----------+
| New York  | New York |
| Buffalo   | New York |
| Rochester | New York |
| Yonkers   | New York |
| Syracuse  | New York |
| Albany    | New York |
+-----------+----------+
6 rows in set (0,00 sec)

先前的数字是这六个城市的总和。

mysql> SELECT District, SUM(Population) FROM city
    -> WHERE CountryCode = 'USA' GROUP BY District
    -> HAVING SUM(Population) > 3000000;
+------------+-----------------+
| District   | SUM(Population) |
+------------+-----------------+
| Arizona    |         3178903 |
| California |        16716706 |
| Florida    |         3151408 |
| Illinois   |         3737498 |
| New York   |         8958085 |
| Texas      |         9208281 |
+------------+-----------------+
6 rows in set (0,28 sec)

我们选择人口超过 300 万的所有地区。 当我们处理数据组时,我们使用HAVING子句而不是WHERE子句。

更新,删除和插入数据

接下来,我们将关注更新,删除和插入数据。

mysql> SELECT Name, HeadOfState FROM country WHERE Name = 'United States';
+---------------+----------------+
| Name          | HeadOfState    |
+---------------+----------------+
| United States | George W. Bush |
+---------------+----------------+
1 row in set (0,12 sec)

如前所述,世界数据库已过时。 乔治·布什(George Bush)不再是美国总统。

mysql> UPDATE country SET HeadOfState = 'Donald Trump'
    -> WHERE Name = 'United States';

使用UPDATE语句,我们将行更改为实际数据。

mysql> SELECT Name, HeadOfState FROM country WHERE Name = 'United States';
+---------------+--------------+
| Name          | HeadOfState  |
+---------------+--------------+
| United States | Donald Trump |
+---------------+--------------+
1 row in set (0,02 sec)

我们已经成功更新了该行。

mysql> CREATE TABLE toptencities engine=MEMORY SELECT * FROM city LIMIT 10;

我们在内存中创建一个临时表。 它将包含city表中的前十个城市。

mysql> SELECT * FROM toptencities;
+----+----------------+-------------+---------------+------------+
| ID | Name           | CountryCode | District      | Population |
+----+----------------+-------------+---------------+------------+
|  1 | Kabul          | AFG         | Kabol         |    1780000 |
|  2 | Qandahar       | AFG         | Qandahar      |     237500 |
|  3 | Herat          | AFG         | Herat         |     186800 |
|  4 | Mazar-e-Sharif | AFG         | Balkh         |     127800 |
|  5 | Amsterdam      | NLD         | Noord-Holland |     731200 |
|  6 | Rotterdam      | NLD         | Zuid-Holland  |     593321 |
|  7 | Haag           | NLD         | Zuid-Holland  |     440900 |
|  8 | Utrecht        | NLD         | Utrecht       |     234323 |
|  9 | Eindhoven      | NLD         | Noord-Brabant |     201843 |
| 10 | Tilburg        | NLD         | Noord-Brabant |     193238 |
+----+----------------+-------------+---------------+------------+
10 rows in set (0,00 sec)

这是toptencities表的内容。

mysql> DELETE FROM toptencities WHERE ID IN (2, 4, 6, 8, 10);

使用DELETE FROM语句和WHERE子句,我们从toptencities表中删除第二行。

mysql> SELECT * FROM toptencities;
+----+-----------+-------------+---------------+------------+
| ID | Name      | CountryCode | District      | Population |
+----+-----------+-------------+---------------+------------+
|  1 | Kabul     | AFG         | Kabol         |    1780000 |
|  3 | Herat     | AFG         | Herat         |     186800 |
|  5 | Amsterdam | NLD         | Noord-Holland |     731200 |
|  7 | Haag      | NLD         | Zuid-Holland  |     440900 |
|  9 | Eindhoven | NLD         | Noord-Brabant |     201843 |
+----+-----------+-------------+---------------+------------+
5 rows in set (0.00 sec)

表中还有五行。

mysql> TRUNCATE TABLE toptencities;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM toptencities;
Empty set (0,00 sec)

我们使用TRUNCATE语句删除表中的所有行。 没有数据了。

mysql> INSERT INTO toptencities VALUES(1, 'Kabul', 'AFG', 'Kabol', 1780000);

mysql> SELECT * FROM toptencities;;
+----+-------+-------------+----------+------------+
| ID | Name  | CountryCode | District | Population |
+----+-------+-------------+----------+------------+
|  1 | Kabul | AFG         | Kabol    |    1780000 |
+----+-------+-------------+----------+------------+
1 row in set (0.00 sec)

使用INSERT INTO语句,我们在表中插入一行。

mysql> DROP TABLE toptencities;
Query OK, 0 rows affected (0,06 sec)

我们从数据库中删除该表。

在本章中,我们快速介绍了 MySQL 数据库的一些基础知识。 我们将在以下各章中详细介绍。

连接到 SQLite 数据库

原文: http://zetcode.com/db/sqliteruby/connect/

SQLite Ruby 教程的这一部分将向您展示如何连接到数据库以及如何对数据库做一些简单的事情。

开始之前

SQLite 附带sqlite3命令行工具。 它可用于对数据库发出 SQL 命令。 现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

我们为sqlite3工具提供了一个参数。 test.db是数据库名称。 它是我们磁盘上的单个文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供了test.db数据库中的表列表。 当前没有表。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

sqlite-ruby接口用于使用 Ruby 语言与 SQLite 数据库进行交互。

$ sudo apt-get install libsqlite3-ruby

上面的命令将模块安装在基于 Debian 的 Linux 系统上。

第一步是创建一个Database对象。 Database类封装了到 SQLite 数据库的单个连接。 使用close方法关闭数据库对象。

SQLite3::Database.new dbname
SQLite3::Database.open dbname

new方法创建一个新的数据库对象,该对象将打开给定的dbname文件。 如果该文件不存在,则将尽可能创建它。 默认情况下,新数据库将结果行作为数组返回。 open方法打开给定文件中包含的数据库。

SQLite3::Database.new ":memory:"

如果我们为文件名提供特殊字符串:memory:,则可以创建一个内存数据库。

数据来源

创建本教程时,请参考 sqlite-ruby.rubyforge.org 网站。

版本

在第一个代码示例中,我们将获得 SQLite 数据库的版本。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new ":memory:"
    puts db.get_first_value 'SELECT SQLITE_VERSION()'

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

在上面的 Ruby 脚本中,我们创建了一个新的内存数据库。 我们执行一条 SQL 语句,该语句返回 SQLite 数据库的版本。

require 'sqlite3'

我们使用sqlite3 Ruby 模块连接到 SQLite 数据库。

db = SQLite3::Database.new ":memory:"

我们创建一个新的数据库对象。 Database类封装了到 SQLite 数据库的单个连接。 数据库在内存中创建。 因此它不是永久的。

puts db.get_first_value 'SELECT SQLITE_VERSION()'

我们将db对象的get_first_value方法调用。 它执行 SQL 语句并获得结果集第一行的第一个值。

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

我们检查错误。 这很重要,因为使用数据库容易出错。

ensure
    db.close if db
end

最后,我们释放资源。

$ ./version.rb
3.7.7

输出可能类似于上面。

插入数据

我们将创建一个Cars表并在其中插入几行。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"
    db.execute "CREATE TABLE IF NOT EXISTS Cars(Id INTEGER PRIMARY KEY, 
        Name TEXT, Price INT)"
    db.execute "INSERT INTO Cars VALUES(1,'Audi',52642)"
    db.execute "INSERT INTO Cars VALUES(2,'Mercedes',57127)"
    db.execute "INSERT INTO Cars VALUES(3,'Skoda',9000)"
    db.execute "INSERT INTO Cars VALUES(4,'Volvo',29000)"
    db.execute "INSERT INTO Cars VALUES(5,'Bentley',350000)"
    db.execute "INSERT INTO Cars VALUES(6,'Citroen',21000)"
    db.execute "INSERT INTO Cars VALUES(7,'Hummer',41400)"
    db.execute "INSERT INTO Cars VALUES(8,'Volkswagen',21600)"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

上面的脚本创建一个Cars表,并将 8 行插入到该表中。

db = SQLite3::Database.open "test.db"

我们连接到test.db数据库。

db.execute "CREATE TABLE IF NOT EXISTS Cars(Id INTEGER PRIMARY KEY, 
    Name TEXT, Price INT)"

execute方法执行给定的 SQL 语句。 如果尚不存在新的Cars表,则会创建该表。

db.execute "INSERT INTO Cars VALUES(1,'Audi',52642)"
db.execute "INSERT INTO Cars VALUES(2,'Mercedes',57127)"

这两行将两辆车插入表。 请注意,默认情况下,我们处于自动提交模式,其中对表的所有更改均立即生效。

sqlite> .mode column  
sqlite> .headers on

我们使用sqlite3工具验证写入的数据。 首先,我们修改数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

这是我们已写入Cars表的数据。

最后插入的行 ID

有时我们需要确定最后插入的行的 ID。 我们使用last_insert_row_id方法找到它。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new ":memory:"

    db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"
    db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Jim')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Robert')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Julian')"

    id = db.last_insert_row_id
    puts "The last id of the inserted row is #{id}"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们在内存中创建一个Friends表。 Id自动递增。

db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"

在 SQLite 中,INTEGER PRIMARY KEY列自动增加。 还有一个AUTOINCREMENT关键字。 在INTEGER PRIMARY KEY AUTOINCREMENT中使用时,会使用稍微不同的算法来创建Id

db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
db.execute "INSERT INTO Friends(Name) VALUES ('Jim')"
db.execute "INSERT INTO Friends(Name) VALUES ('Robert')"
db.execute "INSERT INTO Friends(Name) VALUES ('Julian')"

这五个 SQL 语句将五行插入到Friends表中。

id = db.last_insert_row_id

使用last_insert_row_id方法,我们获得最后插入的行Id

$ ./last_rowid.rb 
The last id of the inserted row is 5

我们看到了脚本的输出。

取得数据

在本章的最后一个示例中,我们获取一些数据。 有关数据获取的更多信息将在“查询”一章中进行讨论。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    stm = db.prepare "SELECT * FROM Cars LIMIT 5" 
    rs = stm.execute 

    rs.each do |row|
        puts row.join "\s"
    end

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

在示例中,我们从Cars表中获取 5 行。

stm = db.prepare "SELECT * FROM Cars LIMIT 5" 
rs = stm.execute 

我们准备要使用prepare方法执行的 SQL 语句。 该方法返回一个语句对象。 然后,使用execute方法执行 SQL 语句。 它返回一个结果集。 ResutlSet对象是查询返回的数据上的简单光标。

rs.each do |row|
    puts row.join "\s"
end

使用each方法,我们遍历结果集中的数据。 在每个循环中,它返回一行。 该行是一个字段数组。 这些字段用空白连接起来形成一条线。

$ ./fetch.rb 
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

这是fetch.rb脚本的输出。

在 SQLite Ruby 教程的这一章中,我们展示了如何建立与 SQLite 数据库的数据库连接。 我们已经解释了对数据库进行一些基本工作的脚本。

在 SQLite 中使用 Ruby 进行 SQL 查询

原文: http://zetcode.com/db/sqliteruby/queries/

我们已经建立了到数据库的连接。 现在我们要修改并从数据库中获取数据。

使用SELECT语句从数据库中检索数据。 在 SQLite Ruby 模块中,首先我们使用prepare方法准备 SQL 语句。 SQL 字符串被发送到数据库引擎,该引擎检查语句的有效性,语法,并在某些数据库中检查执行某些查询的用户权限。 如果一切正常,则将语句对象返回到 Ruby 脚本。 下一步是对execute方法的调用。 该方法在数据库内执行查询。 检索数据。

Ruby SQLite 模块有几种从数据库表中获取数据的方法。 准备并执行 SQL 语句后,我们可以遍历返回的数据。

取得数据

在第一个示例中,我们从Cars表中获取一行。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new "test.db"

    id = 1

    stm = db.prepare "SELECT * FROM Cars WHERE Id=?"
    stm.bind_param 1, id
    rs = stm.execute

    row = rs.next

    puts row.join "\s"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

在示例中,我们执行所有步骤以从Cars表中获取第一行。

stm = db.prepare "SELECT * FROM Cars WHERE Id=?"

SELECT语句是使用prepare方法准备的。 返回一个语句对象。

stm.bind_param 1, id

参数绑定到语句中的占位符。

rs = stm.execute

该语句被执行。 返回ResultSet对象。

row = rs.next

我们从结果集中获得下一行。 因为我们只想获取一行,所以我们只调用一次next方法。

puts row.join "\s"

该行是一个 Ruby 数组。 使用join方法,将三个字段与一个空格字符连接起来以形成一行。

$ ./fetch.rb
1 Audi 52642

这是示例的输出。

在下面的示例中,我们将获取五行。 我们将next方法放入while循环中。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    stm = db.prepare "SELECT * FROM Cars LIMIT 5"
    rs = stm.execute

    while (row = rs.next) do
        puts row.join "\s"
    end

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

在此脚本中,我们连接到数据库并获取Cars表的 5 行。

stm = db.prepare "SELECT * FROM Cars LIMIT 5"

这是用于提取 5 行的 SQL 语句。

while (row = rs.next) do
    puts row.join "\s"
end

next方法放在while循环中。 它返回结果集中的下一行。 如果没有剩余的行,则该方法返回nil,而while循环终止。

我们可以使用each方法从结果集中获取数据。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    stm = db.prepare "SELECT * FROM Cars LIMIT 5"
    rs = stm.execute

    rs.each do |row|
        puts row.join "\s"
    end

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

同样,我们从Cars表中选择五行。

rs.each do |row|
    puts row.join "\s"
end

我们使用each方法来迭代结果集。

下一个示例显示数据库对象的execute方法。 这是一种方便的方法,可以节省一些击键。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    rows = db.execute "SELECT * FROM Cars LIMIT 5"

    for row in rows do
        puts row.join "\s"
    end

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

该示例从Cars表中选择并打印五行。

rows = db.execute "SELECT * FROM Cars LIMIT 5"

在这里,我们一步一步完成了两项工作。 我们准备语句并执行它。 该方法以 Ruby 数组形式返回数据。

for row in rows do
    puts row.join "\s"
end

我们从 Ruby 数组中打印数据。

到目前为止,我们已经看到以ResultSet或数组形式返回的数据。 下一个示例将以哈希数组的形式返回数据。 这样,我们可以通过列名来标识字段值。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"
    db.results_as_hash = true

    ary = db.execute "SELECT * FROM Cars LIMIT 5"    

    ary.each do |row|
        printf "%s %s %s\n", row['Id'], row['Name'], row['Price']
    end

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

在示例中,我们通过字段的列名获取字段。

db.results_as_hash = true

我们将results_as_hash属性设置为true。 所有行将作为哈希对象返回,以列名作为键。

ary.each do |row|
    printf "%s %s %s\n", row['Id'], row['Name'], row['Price']
end

我们通过字段的列名获取字段。

$ ./fetch_hash.rb
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

我们看到示例的输出。

获取行

Ruby SQLite 模块有两种方便的方法来检索行。 在第一个示例中,我们将从表中获得一行。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    row = db.get_first_row "SELECT * FROM Cars WHERE Id=1"       
    puts row.join "\s"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们获得Cars表第一行的数据。

row = db.get_first_row "SELECT * FROM Cars WHERE Id=1" 

get_first_row方法获取第一行,并丢弃所有其他行。

puts row.join "\s"

该行将打印到控制台。

$ ./fetchrow.rb
1 Audi 52642

在这里,我们看到fetchrow.rb示例的输出。

在最后一个示例中,我们选择一个值。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    val = db.get_first_value "SELECT Price FROM Cars WHERE Name='Bentley'"       
    puts val

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们为特定的汽车选择价格。

val = db.get_first_value "SELECT Price FROM Cars WHERE Name='Bentley'"      

使用get_first_value方法,我们选择一行的特定字段。 在我们的案例中,这是宾利汽车的价格。

$ ./fetchvalue.rb
350000    

这是输出。

在 SQLite Ruby 教程的这一部分中,我们已经演示了如何使用各种方法从数据库中获取数据。

绑定参数

原文: http://zetcode.com/db/sqliteruby/bind/

SQL 语句通常是动态构建的。 用户提供一些输入,并且此输入已内置到语句中。 程序员每次处理用户的输入时都必须谨慎。 它具有一些严重的安全隐患。 动态构建 SQL 语句的推荐方法是使用参数绑定。

当我们绑定参数时,我们在语句中创建占位符。 占位符是 SQL 语句中的特殊标记。 它通常是一个问号?。 稍后,使用bind_paramexecutequery等方法将参数绑定到占位符。 绑定参数可以防止 SQL 注入程序。 它会自动转义一些特殊字符并允许正确处理它们。

准备语句并在执行语句之前绑定其参数时,通常可以提高数据库性能。 在sqlite3 Ruby 模块中,始终准备语句。 即使我们不调用数据库对象的prepare方法而直接调用execute方法,该语句也是由sqlite3 Ruby 模块在后台准备的。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new "test.db"

    name = "Volkswagen"

    stm = db.prepare "SELECT * FROM Cars WHERE Name = ?"
    stm.bind_param 1, name
    rs = stm.execute

    row = rs.next    
    puts row.join "\s"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

该示例从Cars表中为特定的汽车名称选择一行。

name = "Volkswagen"

此值可能来自用户:例如,来自 HTML 表单。

stm = db.prepare "SELECT * FROM Cars WHERE Name = ?"

问号?是值的占位符。 它将稍后添加到脚本中。

stm.bind_param 1, name
rs = stm.execute

使用bind_param方法,名称变量与语句中的占位符关联。 execute方法将返回结果集。

$ ./bindparam1.rb 
8 Volkswagen 21600

这是示例的输出。

接下来,我们提出另一种绑定参数的方式。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new "test.db"

    id = 4

    stm = db.prepare "SELECT * FROM Cars WHERE Id = :id"
    rs = stm.execute id

    row = rs.next    
    puts row.join "\s"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

我们从Cars表中为特定 ID 选择一行。

stm = db.prepare "SELECT * FROM Cars WHERE Id = :id"

以前,我们已经看到一个问号作为占位符。 SQLite Ruby 也支持命名占位符。

rs = stm.execute id

该参数绑定在execute方法中。

我们提供了另一种绑定参数的方法。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new "test.db"

    id = 3
    row = db.get_first_row "SELECT * FROM Cars WHERE Id = ?", id       
    puts row.join "\s"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

这次,所有事情(准备语句,绑定参数和执行语句)都使用一种方法完成。

row = db.get_first_row "SELECT * FROM Cars WHERE Id = ?", id    

get_first_row是一种便捷的方法,一步完成三件事。

在最后一个示例中,我们将在一个语句中绑定多个参数。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new ":memory:"

    stm = db.prepare "SELECT 2 + ? + 6 + ? + ?"
    stm.bind_params 3, 4, 6
    rs = stm.execute

    row = rs.next    
    puts row

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

在该示例中,SQL 语句中有更多的占位符。

stm = db.prepare "SELECT 2 + ? + 6 + ? + ?"

SELECT语句中有三个占位符。

stm.bind_params 3, 4, 6

我们用bind_params方法绑定三个值。

$ ./bindparams.rb
21

这是bindparams.rb程序的输出。

在 SQLite Ruby 教程的这一部分中,我们讨论了绑定参数。

处理图像

原文: http://zetcode.com/db/sqliteruimg/

在 SQLite Ruby 教程的这一章中,我们将使用图像文件。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论是否将图像保存在数据库中的技术问题。

sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,代表二进制大对象。

插入图像

在第一个示例中,我们将图像插入 SQLite 数据库。

#!/usr/bin/ruby

require 'sqlite3'

begin

    fin = File.open "woman.jpg" , "rb"
    img = fin.read

rescue SystemCallError => e      
    puts e
ensure
    fin.close if fin 
end

begin

    db = SQLite3::Database.open 'test.db'
    blob = SQLite3::Blob.new img
    db.execute "INSERT INTO Images VALUES(1, ?)", blob

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的Images表中。

fin = File.open "woman.jpg" , "rb"
img = fin.read

我们打开并阅读 JPG 图像。 read方法以字符串形式返回数据。

blob = SQLite3::Blob.new img

我们创建SQLite3::Blob类的实例。 它用于处理二进制数据。

db.execute "INSERT INTO Images VALUES(1, ?)", blob

图像被写入数据库。

读取图像

在本节中,我们将执行相反的操作。 我们将从数据库表中读取图像。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open 'test.db'   
    data = db.get_first_value "SELECT Data FROM Images LIMIT 1"    

    f = File.new "woman2.jpg", "wb"
    f.write data

rescue SQLite3::Exception, SystemCallError => e 

    puts "Exception occurred"
    puts e

ensure
    f.close if f
    db.close if db
end

我们从图像表中读取图像数据,并将其写入另一个文件,我们将其称为woman2.jpg

data = db.get_first_value "SELECT Data FROM Images LIMIT 1"  

该行从表中选择图像数据。

f = File.new "woman2.jpg", "wb"
f.write data

我们打开一个新的图像文件,并将检索到的数据写入该文件。 然后我们关闭文件。

SQLite Ruby 教程的这一部分专门用于读取和写入图像。

使用 Ruby 获取 SQLite 元数据

原文: http://zetcode.com/db/sqliteruby/meta/

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关我们存储数据的表和列的信息。 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也是元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"
    pst = db.prepare "SELECT * FROM Cars LIMIT 6"    

    puts pst.columns
    puts pst.types
    puts pst.column_count

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    pst.close if pst
    db.close if db
end

在上面的示例中,我们获得了预备语句的列名,列类型和列数。

puts pst.columns
puts pst.types
puts pst.column_count

这三种方法返回预备语句的列名,列类型和列数。

$ ./cols_fields.rb
Id
Name
Price
INTEGER
TEXT
INT
3

输出显示三个列名称:IdNamePrice。 类型是INTEGERTEXTINT

以下示例显示如何检索特定 SQL 命令产生的更改数量。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new ":memory:"

    db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"
    db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Jim')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Robert')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Julian')"
    db.execute "DELETE FROM Friends WHERE Id IN (3, 4, 5)"

    n = db.changes
    puts "There has been #{n} changes"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们在内存中创建一个Friends表。 在最后一个 SQL 命令中,我们删除三行。 我们使用changes方法来获取上一次 SQL 操作完成的更改数量。

db.execute "DELETE FROM Friends WHERE Id IN (3, 4, 5)"

在此 SQL 语句中,我们删除三行。

n = db.changes
puts "There has been #{n} changes"

我们找出上一条 SQL 语句完成的更改数量。

$ ./changes.rb
There has been 3 changes

示例输出。

在下一个示例中,我们将找到有关Cars表的一些数据。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db" 

    stm = db.prepare "PRAGMA table_info('Cars')" 
    rs = stm.execute 

    rs.each do |row|
        puts row.join "\s"
    end    

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    stm.close if stm
    db.close if db
end

在此示例中,我们发出PRAGMA table_info(tableName)命令以获取有关Cars表的一些元数据信息。

stm = db.prepare "PRAGMA table_info('Cars')" 
rs = stm.execute 

PRAGMA table_info(Cars)命令为 Cars 表中的每一列返回一行。 结果集中的列包括列顺序号,列名称,数据类型,该列是否可以为NULL以及该列的默认值。

rs.each do |row|
    puts row.join "\s"
end    

我们遍历结果集并打印数据。

$ ./table_info.rb
0 Id INTEGER 0  1
1 Name TEXT 0  0
2 Price INT 0  0

示例的输出。

接下来,我们将打印Cars表中的 5 行及其列名。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    rows = db.execute2 "SELECT * FROM Cars LIMIT 5"

    rows.each do |row|
        puts "%3s %-8s %s" % [row[0], row[1], row[2]]
    end    

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们将Cars表的五行打印到控制台。 现在,我们也包括列的名称。 记录与列名对齐。

rows = db.execute2 "SELECT * FROM Cars LIMIT 5"

execute2方法执行给定的 SQL 语句。 返回的第一行是列的名称。

rows.each do |row|
    puts "%3s %-8s %s" % [row[0], row[1], row[2]]
end 

数据被检索,格式化并打印到终端。

$ ./column_names.rb
 Id Name     Price
  1 Audi     52642
  2 Mercedes 57127
  3 Skoda    9000
  4 Volvo    29000
  5 Bentley  350000

输出。

在与元数据有关的最后一个示例中,我们将列出test.db数据库中的所有表。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"
    rows = db.execute <<SQL
        SELECT name FROM sqlite_master
            WHERE type='table'
            ORDER BY name;"
SQL

    rows.each do |row|
        puts row
    end    

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

该代码示例将当前数据库中的所有可用表打印到终端。

    rows = db.execute <<SQL
        SELECT name FROM sqlite_master
            WHERE type='table'
            ORDER BY name;"
SQL

表名称是从sqlite_master表中检索的。

$ ./list_tables.rb
Cars
Friends
Images

这些是我们系统上的表。

在 SQLite Ruby 教程的这一部分中,我们使用了数据库元数据。

Ruby 的 SQLite 事务

原文: http://zetcode.com/db/sqliteruby/trans/

在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们提供 Ruby 脚本,这些脚本显示了如何在 Ruby sqlite3模块中使用事务。 我们还将讨论自动提交模式,这对于理解事务是必不可少的。

定义

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。 在自动提交模式中,更改立即生效。 为了处理事务,我们使用transaction方法开始事务。 事务以commitrollback方法结束。

默认情况下,数据库连接处于自动提交模式。 请注意,默认模式取决于驱动程序。 在 SQLite Python 驱动程序中,默认情况下自动提交已关闭。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。 手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE

例子

现在,我们将有一些脚本可用于事务和自动提交模式。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    db.execute "DROP TABLE IF EXISTS Friends"
    db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"
    db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Jim')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Robert')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Julian')"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们创建一个Friends表并用数据填充它。 我们没有明确启动事务,也没有调用commitrollback方法。 但是数据已写入表中。 这是因为默认的工作模式是自动提交。 在这种模式下,每个 SQL 语句立即生效。

db.execute "DROP TABLE IF EXISTS Friends"
db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"

如果Friends表已经存在,我们将其删除。 然后,我们使用CREATE TABLE语句创建表。

db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
...

我们插入数据。

$ ./autocommit.rb
$ sqlite3 test.db 
SQLite version 3.7.7 2011-06-23 19:49:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian

我们执行脚本并使用sqlite3命令行工具检查表。 Friends表已成功创建。

在第二个示例中,我们将使用transaction方法启动事务。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    db.transaction
    db.execute "DROP TABLE IF EXISTS Friends"
    db.execute "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT)"
    db.execute "INSERT INTO Friends(Name) VALUES ('Tom')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Rebecca')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Jim')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Robert')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Julian')"
    db.execute "INSERT INTO Friends(Name) VALUES ('Michael')"
    db.commit

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e
    db.rollback

ensure
    db.close if db
end

我们重新创建Friends表。 在transaction方法调用之后,每个语句都在事务内,直到我们调用commit方法为止。 我们要么保存所有更改,要么不保存任何内容。 这是事务背后的基本思想。

db.transaction

transaction方法开始新的事务。 该方法采用可选的mode参数,我们可以在其中指定事务级别。 默认级别DEFERRED

db.commit

所做的更改将写入数据库。 如果我们注释了这一行,则更改将不会保存。

db.rollback

如果发生错误,我们将回滚更改。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Michael

我们使用sqlite3命令行工具验证是否已写入更改。

当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.open "test.db"

    db.transaction
    db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1"
    db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"
    db.commit

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e
    db.rollback

ensure
    db.close if db
end

在代码示例中,我们要更改两个名称。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。

db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"

表名称不正确。 数据库中没有Friend表。

$ ./rollingback.rb
Exception occurred
no such table: Friend

运行示例将显示此错误消息。 事务回滚。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian

即使第一个UPDATE语句正确,Friends表也没有更改。

我们将再次尝试更改两行,这次是在自动提交模式下。

#!/usr/bin/ruby

require 'sqlite3'

begin

    db = SQLite3::Database.new "test.db"

    db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1"
    db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"

rescue SQLite3::Exception => e 

    puts "Exception occurred"
    puts e

ensure
    db.close if db
end

我们尝试更新Friends表中的两个名称,将Tom更改为Thomas并将Robert更改为Bob

db.execute "UPDATE Friends SET Name='Thomas' WHERE Id=1"
db.execute "UPDATE Friend SET Name='Bob' WHERE Id=4"

UPDATE语句的第二个不正确。

$ ./autocommit2.rb
Exception occurred
no such table: Friend

我们收到与上一个示例相同的错误消息。

sqlite> SELECT * FROM Friends;
1|Thomas
2|Rebecca
3|Jim
4|Robert
5|Julian

但是这一次,第一个UPDATE语句被保存。 第二个不是。

在 SQLite Ruby 教程的这一部分中,我们处理了事务。

SQLite C# 教程

原文: http://zetcode.com/db/sqlitecsharp/

这是 SQLite 数据库的 C# 教程。 它涵盖了使用 C# 进行 SQLite 编程的基础。 这些示例是在 Linux 上创建并测试的。

目录

SQLite & C#

SQLite 是嵌入式关系数据库引擎。 它是一个自包含,无服务器,零配置和事务型 SQL 数据库引擎。 SQLite 实现了 SQL 的大多数 SQL-92 标准。 SQLite 引擎不是独立的进程。 而是将其静态或动态链接到应用。 SQLite 数据库是单个普通磁盘文件,可以位于目录层次结构中的任何位置。

C# 是一种现代,高级,通用,面向对象的编程语言。 它是 .NET 框架的主要语言。 该语言的设计目标是软件健壮性,耐用性和程序员生产率。 它可用于在 PC 或嵌入式系统上创建控制台应用,GUI 应用,Web 应用。

Tweet

相关教程

SQLite 教程涵盖了 SQLite 嵌入式数据库引擎。 如果您需要重新学习 C# 语言的知识,请在 ZetCode 上找到完整的 C# 教程SQLite Visual Basic 教程PHP SQLite3 教程SQLite Python 教程提供了 Visual Basic,PHP 和 Python 语言的编程教程。

SQLite C# 简介

原文: http://zetcode.com/db/sqlitecsharp/intro/

在 SQLite C# 教程的第一章中,我们将提供必要的定义。 我们将展示如何安装 Mono。 本教程中的所有示例都将在 Mono 上运行。 稍后,我们创建第一个工作示例。

关于 SQLite 数据库

SQLite 是嵌入式关系数据库引擎。 它的开发者称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 它非常受欢迎,当今全球有数亿本使用。 SQLite 用于 Solaris 10 和 Mac OS 操作系统,iPhone 或 Skype。 Qt4 库对 SQLite 以及 Python 或 PHP 语言提供了内置支持。 许多流行的应用内部都使用 SQLite,例如 Firefox 或 Amarok。

$ sudo apt-get install sqlite3

如果尚未安装sqlite3库,则需要安装它。

SQLite 附带sqlite3命令行工具。 它可用于对数据库发出 SQL 命令。 现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

我们为sqlite3工具提供了一个参数。 test.db是数据库名称。 它是我们磁盘上的单个文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供了test.db数据库中的表列表。 当前没有表。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

Mono

Mono 是基于 C# 和公共语言运行时的 ECMA 标准的 Microsoft .NET Framework 的开源实现。 在本教程中,我们需要安装 Mono 才能编译和运行示例。

可以从 Linux 发行版的包中安装 Mono,也可以从源代码中安装 Mono,以获得更多最新版本。

$ bunzip2 mono-2.10.8.tar.bz2
$ tar -xf mono-2.10.8.tar
$ cd mono-2.10.8/
$ ./configure
$ make
$ sudo make install

我们从 Mono 网站下载mono-2.10.8.tar.bz2 tarball。 解压缩,构建并安装库。 我们安装了 Mono 运行时,C# 语言和 SQLite C# 数据适配器。

$ bunzip2 libgdiplus-2.10.9.tar.bz2 
$ tar -xf libgdiplus-2.10.9.tar 
$ cd libgdiplus-2.10.9/
$ ./configure
$ make
$ sudo make install

对于带有 Winforms 控件的示例,我们还需要libgdiplus库。 它位于单独的文件中。 我们构建并安装它。

$ sudo ldconfig
$ ldconfig -p | grep libgdiplus
        libgdiplus.so.0 (libc6) => /usr/local/lib/libgdiplus.so.0
        libgdiplus.so (libc6) => /usr/local/lib/libgdiplus.so

我们还运行ldconfig工具来更新动态库的数据库。 ldconfig扫描正在运行的系统,并设置用于加载共享库的符号链接。

Mono.Data.Sqlite程序集包含 SQLite 数据库的 ADO.NET 数据供应器。 它用 C# 编写,并且可用于所有 CLI 语言:C# ,Visual Basic,Boo 等。

$ ls /usr/local/lib/mono/4.0/Mono.Data.Sqlite.dll 
/usr/local/lib/mono/4.0/Mono.Data.Sqlite.dll

从技术角度来看,我们需要一个 DLL。 在我们的系统上,它位于上述路径下。 (实际上,以上是指向 DLL 的软链接,该 DLL 位于gac子目录中。)

ADO.NET

ADO.NET是 .NET 框架的重要组成部分。 该规范统一了对关系数据库,XML 文件和其他应用数据的访问。 从程序员的角度来看,它是一组与数据库和其他数据源一起使用的库和类。 Mono.Data.SQLite是 SQLite 数据库的 ADO.NET 规范的实现。 它是用 C# 语言编写的驱动程序,可用于所有.NET 语言。

SqliteConnectionSqliteCommandSqliteDataReaderSqliteDataAdapter是.NET 数据供应器模型的核心元素。 SqliteConnection创建到特定数据源的连接。 SqliteCommand对象针对数据源执行一条 SQL 语句。 SqliteDataReader从数据源读取数据流。 SqliteDataAdapterDataSet和数据源之间的中介。 它填充DataSet并解析数据源的更新。

DataSet对象用于大量数据的离线工作。 它是一种断开连接的数据表示形式,可以保存来自各种不同来源的数据。 SqliteDataReaderDataSet都用于处理数据。 它们在不同的情况下使用。 如果只需要读取查询结果,则SqliteDataReader是更好的选择。 如果我们需要更广泛的数据处理,或者要将 Winforms 控件绑定到数据库表,则首选DataSet

SQLite 版本

如果是第一个程序,我们将检查 SQLite 数据库的版本。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "Data Source=:memory:";

        SqliteConnection con = null;
        SqliteCommand cmd = null;

        try 
        {
            con = new SqliteConnection(cs);
            con.Open();

            string stm = "SELECT SQLITE_VERSION()";   
            cmd = new SqliteCommand(stm, con);

            string version = Convert.ToString(cmd.ExecuteScalar());

            Console.WriteLine("SQLite version : {0}", version);

        } catch (SqliteException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

        } finally 
        {   
            if (cmd != null)
            {
                cmd.Dispose();
            }

            if (con != null) 
            {
                try 
                {
                    con.Close();

                } catch (SqliteException ex)
                { 
                    Console.WriteLine("Closing connection failed.");
                    Console.WriteLine("Error: {0}",  ex.ToString());

                } finally 
                {
                    con.Dispose();
                }
            }                        
        }
    }
}

我们连接到内存数据库并选择一个 SQLite 版本。

using Mono.Data.Sqlite;

Mono.Data.SqliteClient程序集包含 SQLite 数据库引擎的 ADO.NET 数据供应器。 我们导入 SQLite 数据供应器的元素。

string cs = "Data Source=:memory:";

这是连接字符串。 数据提供者使用它来建立与数据库的连接。 我们创建一个内存数据库。

con = new SqliteConnection(cs);

创建一个SqliteConnection对象。 该对象用于打开与数据库的连接。

con.Open();

这行打开数据库连接。

string stm = "SELECT SQLITE_VERSION()";   

这是SQL SELECT语句。 它返回数据库的版本。 SQLITE_VERSION()是内置的 SQLite 函数。

SqliteCommand cmd = new SqliteCommand(stm, con);

SqliteCommand是一个对象,用于在数据库上执行查询。 参数是 SQL 语句和连接对象。

string version = Convert.ToString(cmd.ExecuteScalar());

有些查询仅返回标量值。 在我们的例子中,我们需要一个简单的字符串来指定数据库的版本。 在这种情况下使用ExecuteScalar()。 我们避免了使用更复杂的对象的开销。

Console.WriteLine("SQLite version : {0}", version);

数据库的版本将打印到控制台。

} catch (SqliteException ex) 
{
    Console.WriteLine("Error: {0}",  ex.ToString());

如果发生异常,我们将错误消息打印到控制台。

} finally 
{   
    if (cmd != null)
    {
        cmd.Dispose();
    }

SqliteCommand类实现IDisposable接口。 因此,必须对其进行明确处理。

if (con != null) 
{
    try 
    {
        con.Close();

    } catch (SqliteException ex)
    { 
        Console.WriteLine("Closing connection failed.");
        Console.WriteLine("Error: {0}",  ex.ToString());

    } finally 
    {
        con.Dispose();
    }
}  

关闭连接可能会引发另一个异常。 我们处理这种情况。

$ dmcs version.cs -r:Mono.Data.Sqlite.dll

我们汇编示例。 提供了 SQLite 数据供应器 DLL 的路径。

$ mono ./version.exe 
SQLite version : 3.7.7

这是我们系统上程序的输出。

using语句

C# 语言实现垃圾回收。 这是一个自动释放不再需要的对象的过程。 该过程是不确定的。 我们不能确定 CLR(公共语言运行时)何时决定释放资源。 对于有限的资源,例如文件句柄或网络连接,最好尽快释放它们。 使用using语句,程序员可以控制何时释放资源。 当程序超出了using块时,到达其末尾或引发异常,则资源被释放。

在内部,using语句被转换为tryfinally块,在finally块中调用了Dispose()。 请注意,您可能更喜欢使用trycatchfinally块,而不是using语句。 特别是,如果您想显式地利用catch块。 在本教程中,我们选择了 using 语句。 主要是因为代码较短。

通常,当我们使用IDisposable对象时,应在using语句中声明并实例化它。 (或在finally块中调用Dispose()方法。)对于 SQLite ADO.NET 驱动程序,我们对SqliteConnectionSqliteCommandSqliteDataReaderSqliteCommandBuilderSqliteDataAdapter类使用using语句。 我们不必将其用于DataSetDataTable类。 他们可以留给垃圾收集器。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using (SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            using (SqliteCommand cmd = new SqliteCommand(con))
            {
                cmd.CommandText = "SELECT SQLITE_VERSION()";
                string version = Convert.ToString(cmd.ExecuteScalar());

                Console.WriteLine("SQLite version : {0}", version);
            }             

            con.Close();
        }
    }
}

我们有同样的例子。 这次我们实现了using关键字。

using (SqliteConnection con = new SqliteConnection(cs)) 
{
    con.Open();

    using (SqliteCommand cmd = new SqliteCommand(con))

SqliteConnectionSqliteCommand都实现IDisposable接口。 因此,它们用using关键字包装。

创建并填充表

接下来,我们将创建一个数据库表并用数据填充它。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {

        string cs = "URI=file:test.db";

        using ( SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            using (SqliteCommand cmd = new SqliteCommand(con))
            {
                cmd.CommandText = "DROP TABLE IF EXISTS Cars";
                cmd.ExecuteNonQuery();
                cmd.CommandText = @"CREATE TABLE Cars(Id INTEGER PRIMARY KEY, 
                    Name TEXT, Price INT)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(1,'Audi',52642)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(2,'Mercedes',57127)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(3,'Skoda',9000)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(4,'Volvo',29000)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(5,'Bentley',350000)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(6,'Citroen',21000)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(7,'Hummer',41400)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Cars VALUES(8,'Volkswagen',21600)";
                cmd.ExecuteNonQuery();
            }             

            con.Close();
        }
    }
}

在上面的代码示例中,我们创建具有 8 行的Cars表。

cmd.CommandText = "DROP TABLE IF EXISTS Cars";
cmd.ExecuteNonQuery();

首先,如果该表已经存在,则将其删除。 如果我们不想要结果集,例如对于DROPINSERTDELETE语句,可以使用ExecuteNonQuery()方法。

cmd.CommandText = @"CREATE TABLE Cars(Id INTEGER PRIMARY KEY, 
    Name TEXT, Price INT)";
cmd.ExecuteNonQuery();

Cars表已创建。 INTEGER PRIMARY KEY列在 SQLite 中自动增加。

cmd.CommandText = "INSERT INTO Cars VALUES(1,'Audi',52642)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO Cars VALUES(2,'Mercedes',57127)";
cmd.ExecuteNonQuery();

我们在表中插入两行。

sqlite> .mode column  
sqlite> .headers on

sqlite3命令行工具中,我们修改了数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600     

我们验证数据。 Cars表已成功创建。

预备语句

现在,我们将以预备语句来关注自己。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {

        string cs = "URI=file:test.db";

        using(SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            using (SqliteCommand cmd = new SqliteCommand(con)) 
            {
                cmd.CommandText = "INSERT INTO Cars(Name, Price) VALUES(@Name, @Price)";
                cmd.Prepare();

                cmd.Parameters.AddWithValue("@Name", "BMW");
                cmd.Parameters.AddWithValue("@Price", 36600);
                cmd.ExecuteNonQuery();
            }

            con.Close();
        }
    }
}

我们向Cars表添加一行。 我们使用参数化命令。

cmd.CommandText = "INSERT INTO Cars(Name, Price) VALUES(@Name, @Price)";
cmd.Prepare();

在这里,我们创建一个预备语句。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预备语句更快,并且可以防止 SQL 注入攻击。 @Name@Price是占位符,稍后将填充。

cmd.Parameters.AddWithValue("@Name", "BMW");
cmd.Parameters.AddWithValue("@Price", 36600);

值绑定到占位符。

cmd.ExecuteNonQuery();

执行预备语句。 当我们不希望返回任何数据时,我们使用SqliteCommand对象的ExecuteNonQuery()方法。

$ mono prepared.exe 

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600     
9           BMW         36600  

我们有一辆新车插入表。

数据来源

已咨询 MSDN(Microsoft 开发者网络)来创建本教程。 该网站有几种定义。

这是 SQLite C# 教程的介绍性章节。

使用SqliteDataReader检索数据

原文: http://zetcode.com/db/sqlitecsharp/read/

SqliteDataReader是用于从数据库检索数据的类。 它与SqliteCommand类一起使用以执行 SQL SELECT语句,然后访问返回的行。 它提供对查询结果的快速,仅转发和只读访问。 这是从表中检索数据的最有效方法。

我们不使用构造器,而是通过调用ExecuteReader() method of the SqliteCommand 对象创建SqliteDataReader的实例。 在使用SqlDataReader时,关联的SqlConnectionSqlDataReader服务。 除了关闭SqlConnection以外,无法对其进行其他操作。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using(SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            string stm = "SELECT * FROM Cars LIMIT 5";

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {
                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read()) 
                    {
                        Console.WriteLine(rdr.GetInt32(0) + " " 
                            + rdr.GetString(1) + " " + rdr.GetInt32(2));
                    }         
                }
            }

            con.Close();   
         }
    }
}

我们从Cars表中获得 5 辆汽车,并将它们打印到控制台。

using (SqliteDataReader rdr = cmd.ExecuteReader())

要创建SQLiteDataReader对象,我们必须调用SqliteCommand对象的ExecuteReader()方法。

while (rdr.Read()) 
{
    Console.WriteLine(rdr.GetInt32(0) + " " 
        + rdr.GetString(1) + " " + rdr.GetInt32(2));
}   

Read()方法将数据读取器移至下一条记录。 如果有更多行,则返回true;否则,返回false。 否则为假。 我们可以使用数组索引符号来检索值,或者使用特定的方法来访问其本机数据类型中的列值。 后者效率更高。

$ mono retrieve.exe 
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

Cars表的前五行。

我们可以通过字段的列名来检索字段。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using(SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            string stm = "SELECT * FROM Cars LIMIT 5";

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {
                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read()) 
                    {
                        Console.Write("{0} ", rdr["Id"]);
                        Console.Write("{0} ", rdr["Name"]);
                        Console.Write("{0} \n", rdr["Price"]);
                    }
                }         
            }

            con.Close();
        }
    }
}

该示例从Cars表中打印 5 行。 这次我们使用列名来获取表字段。

while (rdr.Read()) 
{
    Console.Write("{0} ", rdr["Id"]);
    Console.Write("{0} ", rdr["Name"]);
    Console.Write("{0} \n", rdr["Price"]);
}

数据库表字段由其列名引用。

多个语句

ADO.NET 规范允许在单个字符串中执行多个语句。 如有查询,SqliteDataReader返回多个结果集。 它具有NextResult()方法来浏览结果集。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "Data Source=:memory:";

        using (SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            string stm = "SELECT 25; SELECT 44; SELECT 33";

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {
                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    do
                    {
                        rdr.Read();
                        Console.WriteLine("{0}", rdr.GetInt32(0));

                    } while (rdr.NextResult());

                }                
            }

            con.Close();
        }
    }
}

我们在一个 SQL 字符串中有三个查询。 将有三个结果集。

string stm = "SELECT 25; SELECT 44; SELECT 33";

有三个SELECT语句。 它们之间用分号分隔。 它们每个都将返回一个值。

do
{
    rdr.Read();
    Console.WriteLine("{0}", rdr.GetInt32(0));

} while (rdr.NextResult());

Read()方法将SqliteDataReader移至下一条记录。 GetInt32()方法将值检索为 32 位带符号整数。 NextResult()将数据读取器移至下一个结果。

$ dmcs multiple.cs -r:Mono.Data.Sqlite.dll 
$ mono multiple.exe 
25
44
33

运行示例。

我们已经完成了SqliteDataReader的数据读取。

ADO.NET 数据集

原文: http://zetcode.com/db/sqlitecsharp/dataset/

ADO.NET 架构由两个主要部分组成。 .NET 数据供应器和DataSet。 数据提供者是已明确设计用于数据处理和快速访问数据的组件。 创建DataSet的目的是为了独立于任何数据源进行数据访问。 它可以与多个不同的数据源,XML 数据一起使用,或者用于管理应用本地的数据。

DataSet是数据库表中数据的副本以及数据之间的关系。 它在内存中创建,并在需要对数据进行大量处理或将数据表绑定到 Winforms 控件时使用。 处理完成后,更改将被写入数据源。 DataSet是断开的关系结构。 这意味着在DataSet对象的整个生命周期中不必打开基础连接。 这使我们能够有效地使用可用的数据库连接。

可以通过多种方式填充数据集。 我们可以使用SqliteDataAdapter类的Fill()方法。 我们可以以编程方式创建DataTableDataColumnDataRow对象。 可以从 XML 文档或流中读取数据。

SqliteDataAdapter是数据集和数据源之间的中介。 它填充DataSet并解析数据源的更新。 DataTable是内存中数据库表的表示。 可以将一个或多个数据表添加到数据集。 对DataSet所做的更改由SqliteCommandBuilder类保存到数据源。

DataGridView控件提供了用于显示数据的可自定义表。 它允许通过使用属性来定制单元格,行,列和边框。 我们可以使用此控件来显示带有或不带有基础数据源的数据。

创建一个数据表

在第一个示例中,我们将使用DataTable类。

sqlite> CREATE TABLE Friends2(Id INTEGER PRIMARY KEY, Name TEXT);

在这种情况下,必须先创建表,然后才能将任何数据保存到表中。

using System;
using System.Data;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using( SqliteConnection con = new SqliteConnection(cs))
        {

            con.Open();

            DataTable table = new DataTable("Friends2");

            DataColumn column;
            DataRow row;

            column = new DataColumn();
            column.DataType = System.Type.GetType("System.Int32");
            column.ColumnName = "Id";
            table.Columns.Add(column);

            column = new DataColumn();
            column.DataType = Type.GetType("System.String");
            column.ColumnName = "Name";
            table.Columns.Add(column);

            row = table.NewRow();
            row["Id"] = 1;
            row["Name"] = "Jane";
            table.Rows.Add(row);

            row = table.NewRow();
            row["Id"] = 2;
            row["Name"] = "Lucy";
            table.Rows.Add(row);

            row = table.NewRow();
            row["Id"] = 3;
            row["Name"] = "Thomas";
            table.Rows.Add(row);

            string sql = "SELECT * FROM Friends2";

            using (SqliteDataAdapter da = new SqliteDataAdapter(sql, con))
            {
                using (new SqliteCommandBuilder(da))
                {
                    da.Fill(table);
                    da.Update(table);
                }
            }

            con.Close();
        }
    }
}

在示例中,我们创建一个新的DataTable对象。 我们在表中添加两列和三行。 然后,我们将数据保存在新的Friends2数据库表中。

DataTable table = new DataTable("Friends2");

创建一个新的DataTable对象。

column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.ColumnName = "Id";
table.Columns.Add(column);

新列将添加到表中。 我们为列提供数据类型和名称。 可通过Columns属性访问DataTable的列。

row = table.NewRow();
row["Id"] = 1;
row["Name"] = "Jane";
table.Rows.Add(row);

在表中添加一行。 可以通过Rows属性访问DataTable的行。

string sql = "SELECT * FROM Friends2";

using (SqliteDataAdapter da = new SqliteDataAdapter(sql, con))

SqliteDataAdapter是数据库表及其在内存中的表示之间的中介。

using (new SqliteCommandBuilder(da))

SqliteCommandBuilder包装数据适配器。 它只需要实例化。 我们稍后不会直接使用它。

da.Fill(table);
da.Update(table);

数据适配器中填充了表中的数据。 Update方法将数据插入数据库。

保存 XML 数据

来自DataTable的数据可以轻松保存在 XML 文件中。 此任务有WriteXml()方法。

using System;
using System.Data;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";      

        using (SqliteConnection con = new SqliteConnection(cs))
        {        
            con.Open();

            string stm = "SELECT * FROM Cars LIMIT 5";

            using (SqliteDataAdapter da = new SqliteDataAdapter(stm, con))
            {
                DataSet ds = new DataSet();

                da.Fill(ds, "Cars");
                DataTable dt = ds.Tables["Cars"];

                dt.WriteXml("cars.xml");

                foreach (DataRow row in dt.Rows) 
                {            
                    foreach (DataColumn col in dt.Columns) 
                    {
                        Console.Write(row[col] + " ");
                    }

                    Console.WriteLine();
                }
            }

            con.Close();
        }
    }
}

我们从Cars表中打印 5 辆汽车。 我们还将它们保存在 XML 文件中。

using (SqliteDataAdapter da = new SqliteDataAdapter(stm, con))

创建一个SqliteDataAdapter对象。 它以 SQL 语句和连接为参数。 SQL 语句将由SqliteDataAdapter用于检索和传递数据。

DataSet ds = new DataSet();

da.Fill(ds, "Cars");

我们创建DataSet对象。 Fill()方法使用数据适配器从数据源检索数据。 它创建一个名为Cars的新DataTable,并用检索到的数据填充它。

DataTable dt = ds.Tables["Cars"];

Tables属性为我们提供了DataSet中包含的数据表的集合。 从该集合中检索Cars DataTable

dt.WriteXml("cars.xml");

我们将数据表中的数据写入 XML 文件。

foreach (DataRow row in dt.Rows) 
{            
    foreach (DataColumn col in dt.Columns) 
    {
        Console.Write(row[col] + " ");
    }

    Console.WriteLine();
}

我们将Cars表的内容显示到终端。 为了遍历数据,我们利用了DataTable对象的行和列。

$ dmcs savexml.cs -r:Mono.Data.Sqlite.dll -r:System.Data.dll

为了编译该示例,我们添加了一个附加的 DLL 文件System.Data.dll

加载 XML 数据

我们已经展示了如何将数据保存在 XML 文件中。 现在我们将展示如何从 XML 文件加载数据。

using System;
using System.Data;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";      

        using (SqliteConnection con = new SqliteConnection(cs))
        {        
            con.Open();

            DataSet ds = new DataSet();

            ds.ReadXml("cars.xml");
            DataTable dt = ds.Tables["Cars"];

            foreach (DataRow row in dt.Rows) 
            {            
                foreach (DataColumn col in dt.Columns) 
                {
                  Console.Write(row[col] + " ");
                }                

                Console.WriteLine();
            }                         

            con.Close();
        }
    }
}

我们将cars.xml文件的内容读入DataSet。 我们将所有行打印到终端。

DataSet ds = new DataSet();

创建一个DataSet对象。

ds.ReadXml("cars.xml");

来自cars.xml的数据被读入数据集。

DataTable dt = ds.Tables["Cars"];

将数据读入数据集后,将创建一个新的DataTable。 我们得到这张表。

foreach (DataRow row in dt.Rows) 
{            
    foreach (DataColumn col in dt.Columns) 
    {
        Console.Write(row[col] + " ");
    }                

    Console.WriteLine();
}    

我们打印数据表的所有行。

$ mono loadxml.exe 
1 Audi 52642 
2 Mercedes 57127 
3 Skoda 9000 
4 Volvo 29000 
5 Bentley 350000 

运行示例。

DataGridView

在下一个示例中,我们将表绑定到 Winforms DataGridView控件。

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Data;
using Mono.Data.Sqlite;

class MForm : Form
{

    private DataGridView dgv = null;      
    private DataSet ds = null;

    public MForm()
    {

       this.Text = "DataGridView";
       this.Size = new Size(450, 350);

       this.InitUI();
       this.InitData();

       this.CenterToScreen();
    }

    void InitUI()
    {    
        dgv = new DataGridView();

        dgv.Location = new Point(8, 0);
        dgv.Size = new Size(350, 300);
        dgv.TabIndex = 0;
        dgv.Parent = this;        
    }

    void InitData()
    {    
        string cs = "URI=file:test.db";

        string stm = "SELECT * FROM Cars";

        using (SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            ds = new DataSet();

            using (SqliteDataAdapter da = new SqliteDataAdapter(stm, con))
            {
                da.Fill(ds, "Cars");                  
                dgv.DataSource = ds.Tables["Cars"];
            }

            con.Close();
        }     
    }
}

class MApplication 
{
    public static void Main() 
    {
        Application.Run(new MForm());
    }
}

在本示例中,我们将Cars表绑定到 Winforms DataGridView控件。

using System.Windows.Forms;
using System.Drawing;

这两个名称空间用于 GUI。

this.InitUI();
this.InitData();

InitUI()方法内部,我们构建了用户界面。 在InitData()方法中,我们连接到数据库,将数据检索到DataSet中并将其绑定到DataGrid控件。

dgv = new DataGridView();

DataGridView控件已创建。

string stm = "SELECT * FROM Cars";

我们将在DataGridView控件中显示Cars表中的数据。

dgv.DataSource = ds.Tables["Cars"];

我们将DataGridView控件的DataSource属性绑定到所选表。

$ dmcs datagridview.cs -r:System.Data.dll -r:System.Drawing.dll 
    -r:Mono.Data.Sqlite.dll -r:System.Windows.Forms.dll

要编译该示例,我们必须包含其他 DLL。 用于 SQLite 数据供应器,Winforms,绘图和数据的 DLL。

DataGridView

图:DataGridView

在 SQLite C# 教程的这一部分中,我们使用了DataSetDataTableSqliteDataAdapterSqliteCommandBuilderDataGridView类。

MySQL 存储引擎

原文: http://zetcode.com/databases/mysqltutorial/storageengines/

在本章中,我们将讨论 MySQL 存储引擎。

存储引擎是软件模块,数据库管理系统使用该软件模块来创建,读取和更新数据库中的数据。 MySQL 中有两种类型的存储引擎:事务性和非事务性。

对于 MySQL 5.5 和更高版本,默认存储引擎为 InnoDB。 5.5 版之前的 MySQL 默认存储引擎为 MyISAM。 选择正确的存储引擎是一项重要的战略决策,它将影响未来的发展。 在本教程中,我们将使用 MyISAM,InnoDB,内存和 CSV 存储引擎。 如果您不熟悉 MySQL,并且正在研究 MySQL 数据库管理系统,则不必担心。 如果您要计划生产数据库,那么事情将变得更加复杂。

存储引擎列表

MySQL 支持的存储引擎:

  • InnoDB
  • MyISAM
  • 内存
  • CSV
  • 合并
  • 归档
  • 联合
  • 黑洞

InnoDB 是使用最广泛的具有事务支持的存储引擎。 它是符合 ACID 的存储引擎。 它支持行级锁定,崩溃恢复和多版本并发控制。 它是唯一提供外键引用完整性约束的引擎。 Oracle 建议将 InnoDB 用于表,特殊用例除外。

MyISAM 是原始存储引擎。 这是一个快速的存储引擎。 它不支持事务。 MyISAM 提供表级锁定。 它主要用于 Web 和数据仓库。

内存存储引擎在内存中创建表。 这是最快的引擎。 它提供表级锁定。 它不支持事务。 内存存储引擎非常适合创建临时表或快速查找。 重新启动数据库后,数据将丢失。

CSV 将数据存储在 CSV 文件中。 它提供了极大的灵活性,因为这种格式的数据很容易集成到其他应用中。

合并对基础 MyISAM 表进行操作。 合并表有助于更轻松地管理大量数据。 它在逻辑上对一系列相同的 MyISAM 表进行分组,并将它们引用为一个对象。 适用于数据仓库环境。

归档存储引擎针对高速插入进行了优化。 插入时压缩数据。 它不支持事务。 它是存储和检索很少参考的历史,存档数据的理想选择。

黑洞存储引擎接受但不存储数据。 检索总是返回一个空集。 该功能可用于分布式数据库设计中,在该数据库中,数据将自动复制,但不会存储在本地。 该存储引擎可用于执行性能测试或其他测试。

联合存储引擎提供了将 MySQL 服务器分离以从许多物理服务器创建一个逻辑数据库的能力。 本地服务器上的查询将在远程(联合)表上自动执行。 没有数据存储在本地表上。 这对分布式环境很有用。

mysql> SHOW ENGINES\G
*************************** 1\. row ***************************
      Engine: InnoDB
     Support: DEFAULT
     Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
          XA: YES
  Savepoints: YES
*************************** 2\. row ***************************
      Engine: CSV
     Support: YES
     Comment: CSV storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
...

SHOW ENGINES命令显示服务器支持的所有可用引擎。

选择合适的引擎

没有存储引擎在所有情况下都是理想的。 有些在某些条件下表现最佳,而在其他情况下则表现较差。 存在一些必须考虑的折衷。 一个更安全的解决方案会占用更多资源; 它可能会更慢,需要更多的 CPU 时间和磁盘空间。 MySQL 提供了多种不同的存储引擎,因此非常灵活。 其中一些文件(例如存档引擎)是为在特定情况下使用而创建的。

在某些情况下,答案很明确。 每当我们处理某些支付系统时,我们都有义务使用最安全的解决方案。 我们无法承受丢失此类敏感数据的风险。 InnoDB 是必经之路。 如果要全文搜索,则可以选择 MyISAM 或 InnoDB。只有 InnoDB 支持外键引用完整性约束,如果我们计划使用此约束,则选择很明确。

指定和更改存储引擎

在创建表时指定存储引擎。

mysql> CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name VARCHAR(50), 
    -> Cost INTEGER) ENGINE='MyISAM';

ENGINE关键字指定用于此特定表的存储引擎。

如果我们未明确指定存储引擎,则使用默认存储引擎。 在 MySQL 5.5 之前,默认存储引擎是 MyISAM。 对于 MySQL 5.5 及更高版本,默认存储引擎为 InnoDB。

可以迁移到其他存储引擎。 请注意,迁移大表可能需要很长时间。 另外,在迁移表时,我们可能会遇到一些问题。 这两个表可能不支持某些功能。

mysql> SELECT ENGINE FROM information_schema.TABLES
    -> WHERE TABLE_SCHEMA='mydb'
    -> AND TABLE_NAME='Cars';
+--------+
| ENGINE |
+--------+
| InnoDB |
+--------+
1 row in set (0,05 sec)

该 SQL 语句在mydb数据库中找出用于Cars表的存储引擎。 我们也可以使用SELECT CREATE TABLE Cars SQL 语句。 information_schema是存储有关我们表的技术信息的表。

mysql> ALTER TABLE Cars ENGINE='MyISAM';

该 SQL 语句将Cars表的存储引擎更改为 MyISAM。

mysql> SELECT ENGINE FROM information_schema.TABLES
    -> WHERE TABLE_SCHEMA='mydb'
    -> AND TABLE_NAME='Cars';
+--------+
| ENGINE |
+--------+
| MyISAM |
+--------+
1 row in set (0,00 sec)

现在,表的存储引擎是 MyISAM。

在 MySQL 教程的这一部分中,我们介绍了存储引擎。

使用 C# 在 SQLite 中处理图像

原文: http://zetcode.com/db/sqlitecshaimg/

在 SQLite C# 教程的这一章中,我们将使用图像文件。 请注意,有些人反对将图像放入数据库。 在这里,我们仅展示如何执行此操作,并且避免了是否将图像保存在数据库中的技术问题。

sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,代表二进制大对象。

插入图像

在第一个示例中,我们将图像插入 SQLite 数据库。

using System;
using System.IO;
using System.Data;
using Mono.Data.Sqlite;

public class Example
{
    static void Main() 
    {

        string cs = "URI=file:test.db";        

        using(SqliteConnection con = new SqliteConnection(cs))
        {

            con.Open();

            byte[] data = null;

            try
            {
                data = File.ReadAllBytes("woman.jpg");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            SqliteCommand cmd = new SqliteCommand(con);

            cmd.CommandText = "INSERT INTO Images(Data) VALUES (@img)";
            cmd.Prepare();

            cmd.Parameters.Add("@img", DbType.Binary, data.Length);
            cmd.Parameters["@img"].Value = data;
            cmd.ExecuteNonQuery();

            con.Close();
        }
    }
}

我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的Images表中。

byte[] data = null;

图像数据将存储在字节数组中。

data = File.ReadAllBytes("woman.jpg");

ReadAllBytes()方法打开一个二进制文件,将文件的内容读取到字节数组中,然后关闭该文件。

cmd.CommandText = "INSERT INTO Images(Data) VALUES (@img)";
cmd.Prepare();

我们准备一个 SQL 语句,用于将字节数组插入Images表的Data列中。

cmd.Parameters.Add("@img", DbType.Binary, data.Length);
cmd.Parameters["@img"].Value = data;
cmd.ExecuteNonQuery();

我们将二进制数据绑定到预备语句。 然后执行该语句。 该图像被写入数据库表。

读取图像

在本节中,我们将执行相反的操作。 我们将从数据库表中读取图像。

using System;
using System.IO;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";        

        using(SqliteConnection con = new SqliteConnection(cs))
        {            
            con.Open();

            SqliteCommand cmd = new SqliteCommand(con);   
            cmd.CommandText = "SELECT Data FROM Images WHERE Id=1";
            byte[] data = (byte[]) cmd.ExecuteScalar();

            try
            {               
                if (data != null)
                { 
                    File.WriteAllBytes("woman2.jpg", data);
                } else 
                {
                    Console.WriteLine("Binary data not read");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }            

            con.Close();
        }
    }
}

我们从Images表中读取图像数据,并将其写入另一个文件woman2.jpg中。

cmd.CommandText = "SELECT Data FROM Images WHERE Id=1";

该行从表中选择图像数据。

byte[] data = (byte[]) cmd.ExecuteScalar();

我们从数据库表中检索二进制数据。 数据存储在字节数组中。

if (data != null)
{ 
    File.WriteAllBytes("woman2.jpg", data);
} else 
{
    Console.WriteLine("Binary data not read");
}

WriteAllBytes()方法创建一个新文件,将指定的字节数组写入该文件,然后关闭该文件。 如果目标文件已经存在,则将其覆盖。 当数据库表为空并运行此示例时,我们得到一个空值。 因此,我们检查空值。

SQLite C# 教程的这一部分专门用于读取和写入图像。

使用 C# 获取 SQLite 元数据

原文: http://zetcode.com/db/sqlitecsharp/meta/

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        string nrows = null;

        try {
            Console.Write("Enter rows to fetch: ");
            nrows = Console.ReadLine();
        } catch (FormatException e)
        {
            Console.WriteLine(e.ToString());
        }

        using (SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            using (SqliteCommand cmd = con.CreateCommand())
            {

                cmd.CommandText = "SELECT * FROM Cars LIMIT @Id";
                cmd.Prepare();

                cmd.Parameters.AddWithValue("@Id", Int32.Parse(nrows));

                int cols = 0;
                int rows = 0;

                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {

                    cols = rdr.FieldCount;
                    rows = 0;

                    while (rdr.Read()) 
                    {
                        rows++;
                    }

                    Console.WriteLine("The query fetched {0} rows", rows);
                    Console.WriteLine("Each row has {0} cols", cols);
                }    
            }

            con.Close();
        }
    }
}

在上面的示例中,我们获取查询返回的行数和列数。

try {
    Console.Write("Enter rows to fetch: ");
    nrows = Console.ReadLine();
} catch (FormatException e)
{
    Console.WriteLine(e.ToString());
}

该示例要求在命令行上的行数。

cmd.CommandText = "SELECT * FROM Cars LIMIT @Id";
cmd.Prepare();

cmd.Parameters.AddWithValue("@Id", Int32.Parse(nrows));

我们将选定的行限制为提供给程序的数目。

cols = rdr.FieldCount;

返回的列数可以很容易地从SqliteDataReader对象的FieldCount属性获得。

while (rdr.Read()) 
{
    rows++;
}

我们计算结果集中的行数。

$ mono fields_rows.exe 
Enter rows to fetch: 4
The query fetched 4 rows
Each row has 3 cols

输出。

列标题

接下来,我们将展示如何使用数据库表中的数据打印列标题。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {

        string cs = "URI=file:test.db";

        using (SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            string stm = "SELECT * FROM Cars LIMIT 5";

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {

                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", 
                        rdr.GetName(0), rdr.GetName(1), rdr.GetName(2)));

                    while (rdr.Read()) 
                    {
                        Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", 
                            rdr.GetInt32(0), rdr.GetString(1), rdr.GetInt32(2)));
                    }
                }
            }

            con.Close();
        }
    }
}

在此程序中,我们从Cars表中选择 5 行及其列名。

using (SqliteDataReader rdr = cmd.ExecuteReader())

我们创建一个SqliteDataReader对象。

Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", 
    rdr.GetName(0), rdr.GetName(1), rdr.GetName(2)));

我们使用阅读器的GetName()方法获得列的名称。 String.Format()方法用于格式化数据。

while (rdr.Read()) 
{
    Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", 
        rdr.GetInt32(0), rdr.GetString(1), rdr.GetInt32(2)));
}       

我们将 SQL 语句返回的数据打印到终端。

$ dmcs columns.cs -r:Mono.Data.Sqlite.dll
$ mono columns.exe 
Id  Name        Price
1   Audi        52642
2   Mercedes    57127
3   Skoda        9000
4   Volvo       29000
5   Bentley    350000

该程序的输出。

受影响的行

在下面的示例中,我们将发现特定的 SQL 命令已进行了多少更改。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "Data Source=:memory:";

        using (SqliteConnection con = new SqliteConnection(cs))
        {

            con.Open();

            using (SqliteCommand cmd = new SqliteCommand(con))
            {
                cmd.CommandText = "CREATE TABLE Friends(Id INT, Name TEXT)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends VALUES(1, 'Tom')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends VALUES(2, 'Jane')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends VALUES(3, 'Rebekka')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends VALUES(4, 'Lucy')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends VALUES(5, 'Robert')";
                cmd.ExecuteNonQuery();

                cmd.CommandText = "DELETE FROM Friends WHERE Id IN (3, 4, 5)";
                int n = cmd.ExecuteNonQuery();

                Console.WriteLine("The statement has affected {0} rows", n);

           }

           con.Close();
        }
    }
}

我们在内存中创建一个Friends表。 在最后一个 SQL 命令中,我们删除三行。 ExecuteNonQuery()方法返回受最后一个 SQL 命令影响的行数。

cmd.CommandText = "DELETE FROM Friends WHERE Id IN (3, 4, 5)";

在此 SQL 语句中,我们删除三行。

int n = cmd.ExecuteNonQuery();

我们找出上一条 SQL 语句完成的更改数量。

$ mono affected.exe 
The statement has affected 3 rows

示例输出。

表架构

GetSchemaTable()方法,它返回有关每一列的元数据。 它返回许多值,其中包括列名,列大小,基表名或列是否唯一。

using System;
using System.Data;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using(SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            string stm = "SELECT * FROM Cars LIMIT 3";   

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {

                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    DataTable schemaTable = rdr.GetSchemaTable();

                    foreach (DataRow row in schemaTable.Rows)
                    {
                        foreach (DataColumn col in schemaTable.Columns)
                            Console.WriteLine(col.ColumnName + " = " + row[col]);
                        Console.WriteLine();
                    }
                }
            }

            con.Close();
         }
    }
}

该示例打印有关表列的大量元数据。

DataTable schemaTable = rdr.GetSchemaTable();

我们得到架构表。

foreach (DataRow row in schemaTable.Rows)
{
    foreach (DataColumn col in schemaTable.Columns)
        Console.WriteLine(col.ColumnName + " = " + row[col]);
    Console.WriteLine();
}

我们浏览保存元数据的模式表行,并将其打印到控制台。

$ dmcs schema.cs -r:Mono.Data.Sqlite.dll -r:System.Data.dll
$ mono schema.exe 
ColumnName = Id
ColumnOrdinal = 0
ColumnSize = 8
NumericPrecision = 19
NumericScale = 0
IsUnique = True
IsKey = True
...

摘自示例输出。

表名

在与元数据有关的最后一个示例中,我们将列出test.db数据库中的所有表。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";

        using (SqliteConnection con = new SqliteConnection(cs))
        {
            con.Open();

            string stm = @"SELECT name FROM sqlite_master
                WHERE type='table' ORDER BY name";   

            using (SqliteCommand cmd = new SqliteCommand(stm, con))
            {
                using (SqliteDataReader rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read()) 
                    {
                        Console.WriteLine(rdr.GetString(0));
                    }
                }
            }    

            con.Close();    
        }
    }
}

该代码示例将所选数据库中的所有可用表打印到终端。

string stm = @"SELECT name FROM sqlite_master
    WHERE type='table' ORDER BY name"; 

表名称是从sqlite_master表中检索的。

$ mono tables.exe 
Cars
Friends
Images

这些是我们系统上的表。

在 SQLite C# 教程的这一部分中,我们使用了数据库元数据。

使用 C# 的 SQLite 事务

原文: http://zetcode.com/db/sqlitecsharp/trans/

在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们将有一些程序显示如何处理事务。

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABL ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。

手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准的事务级别:DEFERREDIMMEDIATEEXCLUSIVE。 除非我们启动自己的事务,否则 SQLite 会自动将每个命令放入其自己的事务中。 请注意,这也可能会受到驱动程序的影响。 SQLite Python 驱动程序默认情况下已关闭自动提交模式,并且第一个 SQL 命令启动新事务。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";        

        using (SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            using (SqliteCommand cmd = con.CreateCommand())
            {
                cmd.CommandText = "DROP TABLE IF EXISTS Friends";
                cmd.ExecuteNonQuery();
                cmd.CommandText = @"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, 
                                    Name TEXT)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')";
                cmd.ExecuteNonQuery();
            }
            con.Close();
        }
    }
}

我们创建一个Friends表,并用数据填充它。 我们没有明确地启动事务,也没有调用commitrollback方法。 但是数据已写入表中。 这是因为默认情况下,我们在自动提交模式下工作。 在这种模式下,每个 SQL 语句立即生效。

cmd.CommandText = "DROP TABLE IF EXISTS Friends";
cmd.ExecuteNonQuery();
cmd.CommandText = @"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, 
                    Name TEXT)";
cmd.ExecuteNonQuery();

如果Friends表已经存在,我们将其删除。 然后,我们使用CREATE TABLE语句创建表。

cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')";
cmd.ExecuteNonQuery();
...

我们插入两行。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian

Friends表已成功创建。

在第二个示例中,我们将使用BeginTransaction()方法启动自定义事务。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";        

        using (SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            using(SqliteTransaction tr = con.BeginTransaction())
            {
                using (SqliteCommand cmd = con.CreateCommand())
                {

                    cmd.Transaction = tr;
                    cmd.CommandText = "DROP TABLE IF EXISTS Friends";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = @"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, 
                                        Name TEXT)";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')";
                    cmd.ExecuteNonQuery();
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')";
                    cmd.ExecuteNonQuery();
                }

                tr.Commit();
            }

            con.Close();
        }
    }
}

所有 SQL 命令组成一个单元。 要么全部保存,要么什么都不保存。 这是事务背后的基本思想。

using(SqliteTransaction tr = con.BeginTransaction())

BeginTransaction()方法启动事务。

cmd.Transaction = tr;

我们设置SqliteCommand在其中执行的事务。

tr.Commit();

如果一切正常,我们将整个事务提交到数据库。 如果发生异常,则事务将在后台回滚。

显式回滚调用

现在,我们将显示一个示例,在发生异常的情况下,我们手动回滚事务。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";   

        SqliteConnection con = null;     
        SqliteTransaction tr = null;
        SqliteCommand cmd = null;

        try 
        {
            con = new SqliteConnection(cs);

            con.Open();

            tr = con.BeginTransaction();
            cmd = con.CreateCommand();

            cmd.Transaction = tr;
            cmd.CommandText = "DROP TABLE IF EXISTS Friends";
            cmd.ExecuteNonQuery();
            cmd.CommandText = @"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, 
                                Name TEXT)";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')";
            cmd.ExecuteNonQuery();
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')";
            cmd.ExecuteNonQuery();

            tr.Commit();            

        } catch (SqliteException ex) 
        {
            Console.WriteLine("Error: {0}",  ex.ToString());

            if (tr != null)
            {
                try 
                {
                    tr.Rollback();

                } catch (SqliteException ex2)
                {

                    Console.WriteLine("Transaction rollback failed.");
                    Console.WriteLine("Error: {0}",  ex2.ToString());

                } finally
                {
                    tr.Dispose();
                }
            }
        } finally 
        {
            if (cmd != null)
            {
                cmd.Dispose();
            }

            if (tr != null) 
            {
                tr.Dispose();
            }

            if (con != null)
            {
                try 
                {
                    con.Close();                    

                } catch (SqliteException ex)
                {

                    Console.WriteLine("Closing connection failed.");
                    Console.WriteLine("Error: {0}",  ex.ToString());

                } finally
                {
                    con.Dispose();
                }
            }
        }    
    }
}

我们创建自己的trycatchfinally块,以解决可能的问题。

} catch (SqliteException ex) 
{
    Console.WriteLine("Error: {0}",  ex.ToString());

    if (tr != null)
    {
        try 
        {
            tr.Rollback();

        } catch (SqliteException ex2)
        {

            Console.WriteLine("Transaction rollback failed.");
            Console.WriteLine("Error: {0}",  ex2.ToString());

        } finally
        {
            tr.Dispose();
        }
    }
}

在创建Friends表的过程中引发异常时,我们将调用Rollback()方法。 即使进行回滚,也可能会发生异常。 因此,我们也检查这种情况。

if (cmd != null)
{
    cmd.Dispose();
}

if (tr != null) 
{
    tr.Dispose();
}

当一切顺利时,我们将配置资源。

if (con != null)
{
    try 
    {
        con.Close();                    

    } catch (SqliteException ex)
    {

        Console.WriteLine("Closing connection failed.");
        Console.WriteLine("Error: {0}",  ex.ToString());

    } finally
    {
        con.Dispose();
    }
}

关闭连接时,我们可能会收到另一个异常。 我们在这里处理这种情况。

错误

当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";        

        using (SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            using(SqliteTransaction tr = con.BeginTransaction())
            {
                using (SqliteCommand cmd = con.CreateCommand())
                {

                    cmd.Transaction = tr;
                    cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1";
                    cmd.ExecuteNonQuery();

                    cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4";
                    cmd.ExecuteNonQuery();
                }

                tr.Commit();
            }

            con.Close();
        }
    }
}

在代码示例中,我们要更改两个名称。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。

cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4";

表名称不正确。 数据库中没有Friend表。

$ mono trans_error.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend
...

运行示例将显示此错误消息。 事务回滚。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Jane

Friends表中没有发生任何变化。 即使第一个UPDATE语句正确。

我们将再次尝试更改两行; 这次不使用SqliteTransaction

using System;
using Mono.Data.Sqlite;

public class Example
{

    static void Main() 
    {
        string cs = "URI=file:test.db";        

        using (SqliteConnection con = new SqliteConnection(cs)) 
        {
            con.Open();

            using (SqliteCommand cmd = con.CreateCommand())
            {

                cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1";
                cmd.ExecuteNonQuery();

                cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4";
                cmd.ExecuteNonQuery();
            }

            con.Close();
        }
    }
}

我们尝试更新Friends表中的两个名称,汤姆(Tom)到托马斯(Thomas)和罗伯特(Robert)到鲍勃(Bob)。

cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4";
cmd.ExecuteNonQuery();

UPDATE语句不正确。

$ mono notrans_error.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend

我们收到与上一个示例相同的错误消息。

sqlite> SELECT * FROM Friends;
1|Thomas
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Jane

但是这一次,第一个UPDATE语句被保存。 第二个不是。

在 SQLite C# 教程的这一部分中,我们处理了事务。

SQLite Visual Basic 教程

原文: http://zetcode.com/db/sqlitevb/

这是 SQLite 数据库的 Visual Basic 教程。 它涵盖了使用 Visual Basic 进行 SQLite 编程的基础。 这些示例是在 Linux 上创建并测试的。

目录

SQLite & Visual Basic

SQLite 是嵌入式关系数据库引擎。 它是一个自包含,无服务器,零配置和事务型 SQL 数据库引擎。 SQLite 实现了 SQL 的大多数 SQL-92 标准。 SQLite 引擎不是独立的进程。 而是将其静态或动态链接到应用。 SQLite 数据库是单个普通磁盘文件,可以位于目录层次结构中的任何位置。

Visual Basic 是一种现代的,高层通用的,基于对象的编程语言。 它是 .NET 框架中第二重要的语言。 该语言的主要设计目标是创建一种易于使用和学习的编程语言。 它源自经典的 BASIC 语言。

Tweet

相关教程

SQLite 教程涵盖了 SQLite 嵌入式数据库引擎。 如果您需要刷新有关 Visual Basic 语言的知识,请参阅 ZetCode 上的完整 Visual Basic 教程SQLite C# 教程SQLite Perl 教程SQLite PHP 教程SQLite Python 教程提供了有关 C# ,Perl,PHP 和 Python 语言的编程教程。

SQLite Visual Basic 简介

原文: http://zetcode.com/db/sqlitevb/intro/

在 SQLite Visual Basic 教程的第一章中,我们将提供必要的定义。 我们将展示如何安装 Mono & Visual Basic。 本教程中的所有示例都将在 Mono 上运行。 稍后,我们创建第一个工作示例。

关于 SQLite 数据库

SQLite是嵌入式关系数据库引擎。 它的开发者称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 它非常受欢迎,当今全球有数亿本使用。 SQLite 用于 Solaris 10 和 Mac OS 操作系统,iPhone 或 Skype。 Qt4 库对 SQLite 以及 Python 或 PHP 语言提供了内置支持。 许多流行的应用内部都使用 SQLite,例如 Firefox 或 Amarok。

$ sudo apt-get install sqlite3

如果尚未安装sqlite3库,则需要安装它。

SQLite 附带sqlite3命令行工具。 它可用于对数据库发出 SQL 命令。 现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 test.db
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

我们为sqlite3工具提供了一个参数。 test.db是数据库名称。 它是我们磁盘上的单个文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
test.db

.tables命令提供了test.db数据库中的表列表。 当前没有表。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到test.db文件。 所有数据将存储在该单个文件中。

Mono

Mono 是基于 C# 和公共语言运行时的 ECMA 标准的 Microsoft .NET Framework 的开源实现。 在本教程中,我们需要安装 Mono 才能编译和运行示例。

可以从 Linux 发行版的包中安装 Mono,也可以从源代码中安装 Mono,以获得更多最新版本。

$ bunzip2 mono-2.10.8.tar.bz2
$ tar -xf mono-2.10.8.tar
$ cd mono-2.10.8/
$ ./configure
$ make
$ sudo make install

我们从 Mono 网站下载mono-2.10.8.tar.bz2 tarball。 我们将其解压缩,构建并安装库。 我们安装了 Mono 运行时,C# 语言和 SQLite .NET 数据适配器。

$ bunzip2 libgdiplus-2.10.9.tar.bz2 
$ tar -xf libgdiplus-2.10.9.tar 
$ cd libgdiplus-2.10.9/
$ ./configure
$ make
$ sudo make install

对于带有 Winforms 控件的示例,我们还需要libgdiplus库。 它位于单独的文件中。 我们构建并安装它。

$ sudo ldconfig
$ ldconfig -p | grep libgdiplus
        libgdiplus.so.0 (libc6) => /usr/local/lib/libgdiplus.so.0
        libgdiplus.so (libc6) => /usr/local/lib/libgdiplus.so

我们还运行ldconfig工具来更新动态库的数据库。 ldconfig扫描正在运行的系统,并设置用于加载共享库的符号链接。

Mono.Data.Sqlite程序集包含 SQLite 数据库的 ADO.NET 数据供应器。 它用 C# 编写,并且可用于所有 CLI 语言,包括 C# ,Visual Basic 和 Boo。

$ ls /usr/local/lib/mono/4.0/Mono.Data.Sqlite.dll 
/usr/local/lib/mono/4.0/Mono.Data.Sqlite.dll

从技术角度来看,我们需要一个 DLL。 在我们的系统上,它位于上述路径下。 (实际上,上面的链接是指向 DLL 的软链接,该 DLL 位于gac子目录中。)

Visual Basic

Visual Basic是一种现代的,高层通用的,基于对象的编程语言。 它是.NET 框架中第二重要的语言。 该语言的主要设计目标是创建一种易于使用和学习的编程语言。 它源自经典的 BASIC 语言。 Mono 将 Visual Basic 引入 Unix 平台。 它在单独的包中具有 Visual Basic 编译器。

$ bunzip2 mono-basic-2.10.tar.bz2
$ tar xvf mono-basic-2.10.tar
$ cd mono-basic-2.10/

我们从 Mono 项目网站下载资源。 我们对文件进行压缩,然后进入新创建的子目录。

$ ./configure
$ make
$ sudo make install

我们建立&安装编译器。

$ ls /usr/local/bin/vbnc*
/usr/local/bin/vbnc  /usr/local/bin/vbnc2

编译器称为 vbnc,默认情况下位于/usr/local/lib/bin目录中。

ADO.NET

ADO.NET是.NET 框架的重要组成部分。 该规范统一了对关系数据库,XML 文件和其他应用数据的访问。 从程序员的角度来看,它是一组与数据库和其他数据源一起使用的库和类。 Mono.Data.SQLite是 SQLite 数据库的 ADO.NET 规范的实现。 它是用 C# 语言编写的驱动程序,可用于所有 .NET 语言。

SqliteConnectionSqliteCommandSqliteDataReaderSqliteDataAdapter是.NET 数据供应器模型的核心元素。 SqliteConnection创建到特定数据源的连接。 SqliteCommand对象针对数据源执行一条 SQL 语句。 SqliteDataReader从数据源读取数据流。 SqliteDataAdapterDataSet和数据源之间的中介。 它填充DataSet并解析数据源的更新。

DataSet对象用于大量数据的离线工作。 它是一种断开连接的数据表示形式,可以保存来自各种不同来源的数据。 SqliteDataReaderDataSet都用于处理数据。 它们在不同的情况下使用。 如果只需要读取查询结果,则SqliteDataReader是更好的选择。 如果我们需要更广泛的数据处理,或者要将 Winforms 控件绑定到数据库表,则首选DataSet

SQLite 版本

如果是第一个程序,我们将检查 SQLite 数据库的版本。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim con As SqliteConnection
        Dim cmd As SqliteCommand

        Try            
            Dim cs As String = "Data Source=:memory:"
            con = New SqliteConnection(cs)
            con.Open()

            Dim stm As String = "SELECT SQLITE_VERSION()"
            cmd = New SqliteCommand(stm, con)

            Dim version As String = Convert.ToString(cmd.ExecuteScalar())

            Console.WriteLine("SQLite version : {0}", version)

        Catch ex As SqliteException

            Console.WriteLine("Error: " & ex.ToString())

        Finally

            If cmd IsNot Nothing
                cmd.Dispose()
            End If

            If con IsNot Nothing

                Try
                    con.Close()
                Catch ex As SqliteException
                    Console.WriteLine("Failed closing connection")
                    Console.WriteLine("Error: " & ex.ToString())
                Finally
                    con.Close()
                    con.Dispose()
                End Try

            End If

        End Try

    End Sub

End Module

我们连接到内存数据库,然后选择 SQLite 版本。

Imports Mono.Data.Sqlite

Mono.Data.SqliteClient程序集包含 SQLite 数据库引擎的 ADO.NET 数据供应器。 我们导入 SQLite 数据供应器的元素。

Dim con As SqliteConnection
Dim cmd As SqliteCommand

我们声明两个变量。 它们被放置在Try关键字之前,因为我们稍后将在它们上调用Dispose()Close()方法。

Dim cs As String = "Data Source=:memory:"

这是连接字符串。 数据提供者使用它来建立与数据库的连接。 我们创建一个内存数据库。

con = New SqliteConnection(cs)

创建一个SqliteConnection对象。 该对象用于打开与数据库的连接。

con.Open()

这行打开数据库连接。

Dim stm As String = "SELECT SQLITE_VERSION()"

这是 SQL SELECT语句。 它返回数据库的版本。 SQLITE_VERSION()是内置的 SQLite 函数。

Dim cmd As New SqliteCommand(stm, con)

SqliteCommand是一个对象,用于在数据库上执行查询。 参数是 SQL 语句和连接对象。

Dim version As String = Convert.ToString(cmd.ExecuteScalar())

有些查询仅返回标量值。 在我们的例子中,我们需要一个简单的字符串来指定数据库的版本。 在这种情况下使用ExecuteScalar()。 我们避免了使用更复杂的对象的开销。

Console.WriteLine("SQLite version : {0}", version)

数据库的版本将打印到控制台。

Catch ex As SqliteException

    Console.WriteLine("Error: " & ex.ToString())

如果发生异常,我们将错误消息打印到控制台。

Finally

    If cmd IsNot Nothing
        cmd.Dispose()
    End If

SqliteCommand类实现IDisposable接口。 因此,必须将其显式放置在Finally块中。

If con IsNot Nothing

    Try
        con.Close()
    Catch ex As SqliteException
        Console.WriteLine("Failed closing connection")
        Console.WriteLine("Error: " & ex.ToString())
    Finally
        con.Close()
        con.Dispose()
    End Try

End If

关闭连接可能会引发另一个异常。 我们处理这种情况。 (尽管这种情况更有可能发生在基于服务器的数据库中,例如 MySQL 或 PostgreSQL。)

$ vbnc version.vb -r:Mono.Data.Sqlite.dll

我们汇编示例。 提供了 SQLite 数据供应器 DLL 的路径。

$ mono version.exe 
SQLite version : 3.7.7

这是我们系统上程序的输出。

using语句

Visual Basic 语言实现垃圾回收。 这是一个自动释放不再需要的对象的过程。 该过程是不确定的。 我们不能确定 CLR(公共语言运行时)何时决定释放资源。 对于有限的资源,例如文件句柄或网络连接,最好尽快释放它们。 使用Using语句,程序员可以控制何时释放资源。 当程序不在Using块中时,到达其末尾或引发异常,资源将被释放。

在内部,通过Finally块中的Dispose()调用将Using语句转换为TryFinally块。 请注意,您可能更喜欢使用TryCatchFinally块,而不是Using语句。 特别是,如果您想显式地利用Catch块。 在本教程中,我们选择了Using语句。

通常,当我们使用IDisposable对象时,应在Using语句中声明并实例化它。 (或在Finally块中调用Dispose()。)对于 SQLite ADO.NET 驱动程序,我们对SqliteConnectionSqliteCommandSqliteDataReaderSqliteCommandBuilder使用Using语句。 和SqliteDataAdapter类。 我们不必将其用于DataSet。 或DataTable类。 他们可以留给垃圾收集器。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT SQLITE_VERSION()"

                Dim version As String = Convert.ToString(cmd.ExecuteScalar())
                Console.WriteLine("SQLite version : {0}", version)

            End Using

            con.Close()

        End Using

    End Sub

End Module

我们有同样的例子。 这次我们实现了Using关键字。

Using con As New SqliteConnection(cs)

    con.Open()

    Using cmd As New SqliteCommand(con)

SqliteConnectionSqliteCommand都实现IDisposable接口。 因此,它们用Using关键字包装。

创建并填充表

接下来,我们将创建一个数据库表并用数据填充它。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "DROP TABLE IF EXISTS Cars"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "CREATE TABLE Cars(Id INTEGER PRIMARY KEY," _
                   & "Name TEXT, Price INT)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(1,'Audi',52642)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(2,'Mercedes',57127)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(3,'Skoda',9000)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(4,'Volvo',29000)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(5,'Bentley',350000)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(6,'Citroen',21000)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(7,'Hummer',41400)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Cars VALUES(8,'Volkswagen',21600)"
                cmd.ExecuteNonQuery()

            End Using

            con.Close()

        End Using

    End Sub

End Module

在上面的代码示例中,我们创建具有 8 行的Cars表。

cmd.CommandText = "DROP TABLE IF EXISTS Cars"
cmd.ExecuteNonQuery()

首先,如果该表已经存在,则将其删除。 如果我们不想要结果集,则可以使用ExecuteNonQuery()方法。 例如,对于DROPINSERTDELETE语句。

cmd.CommandText = "CREATE TABLE Cars(Id INTEGER PRIMARY KEY," _
    & "Name TEXT, Price INT)"
cmd.ExecuteNonQuery()

Cars表已创建。 INTEGER PRIMARY KEY列在 SQLite 中自动增加。

cmd.CommandText = "INSERT INTO Cars VALUES(1,'Audi',52642)"
cmd.ExecuteNonQuery()
cmd.CommandText = "INSERT INTO Cars VALUES(2,'Mercedes',57127)"
cmd.ExecuteNonQuery()

我们在表中插入两行。

sqlite> .mode column  
sqlite> .headers on

sqlite3命令行工具中,我们修改了数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600    

我们验证数据。 Cars表已成功创建。

预备语句

现在,我们将以预备语句来关注自己。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "INSERT INTO Cars(Name, Price) VALUES(@Name, @Price)"
                cmd.Prepare()

                cmd.Parameters.AddWithValue("@Name", "BMW")
                cmd.Parameters.AddWithValue("@Price", 36600)
                cmd.ExecuteNonQuery()

            End Using

            con.Close()

        End Using

    End Sub

End Module

我们向Cars表添加一行。 我们使用参数化命令。

cmd.CommandText = "INSERT INTO Cars(Name, Price) VALUES(@Name, @Price)"
cmd.Prepare()

在这里,我们创建一个预备语句。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预备语句更快,并且可以防止 SQL 注入攻击。 @Name@Price是占位符,稍后将填充。

cmd.Parameters.AddWithValue("@Name", "BMW")
cmd.Parameters.AddWithValue("@Price", 36600)

值绑定到占位符。

cmd.ExecuteNonQuery()

执行预备语句。 当我们不希望返回任何数据时,我们使用SqliteCommand对象的ExecuteNonQuery()方法。

$ mono prepared.exe 

sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600     
9           BMW         36600  

我们有一辆新车插入表。

数据来源

已咨询 MSDN(Microsoft 开发者网络)来创建本教程。 该网站有几种定义。

这是 SQLite Visual Basic 教程的介绍性章节。

使用SqliteDataReader检索数据

原文: http://zetcode.com/db/sqlitevb/read/

SqliteDataReader是用于从数据库检索数据的类。 它与SqliteCommand类一起使用以执行 SQL SELECT语句,然后访问返回的行。 它提供对查询结果的快速,仅转发和只读访问。 这是从表中检索数据的最有效方法。

我们不使用构造器,而是通过调用SqliteCommand对象的ExecuteReader()方法来创建SqliteDataReader的实例。 在使用SqlDataReader时,关联的SqlConnectionSqlDataReader服务。 除了关闭SqlConnection之外,无法执行其他任何操作。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)        
            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr                
                    While (rdr.Read())
                        Console.WriteLine(rdr.GetInt32(0) & " " _ 
                            & rdr.GetString(1) & " " & rdr.GetInt32(2))
                    End While         
                End Using        
            End Using

            con.Close()
        End Using

    End Sub

End Module

我们从Cars表中获得 5 辆汽车,并将它们打印到控制台。

Dim rdr As SqliteDataReader = cmd.ExecuteReader()

要创建SQLiteDataReader对象,我们必须调用SqliteCommand对象的ExecuteReader()方法。

While (rdr.Read())
    Console.WriteLine(rdr.GetInt32(0) & " " _ 
        & rdr.GetString(1) & " " & rdr.GetInt32(2))
End While

Read()方法将数据读取器移至下一条记录。 如果有更多行,则返回true;否则,返回true。 否则为假。 我们可以使用数组索引符号来检索值,或者使用特定的方法来访问其本机数据类型中的列值。 后者效率更高。

$ mono retrieve.exe 
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

Cars表的前五行。

我们可以通过字段的列名来检索字段。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)        
            con.Open()

            Using cmd As New SqliteCommand(con)
                cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr
                    While (rdr.Read())
                        Console.Write("{0} ", rdr("Id"))
                        Console.Write("{0} ", rdr("Name"))
                        Console.WriteLine("{0} ", rdr("Price"))
                    End While         
                End Using

            End Using
            con.Close()
        End Using

    End Sub

End Module

该示例从Cars表中打印 5 行。 这次我们使用列名来获取表字段。

While (rdr.Read())
    Console.Write("{0} ", rdr("Id"))
    Console.Write("{0} ", rdr("Name"))
    Console.WriteLine("{0} ", rdr("Price"))
End While

数据库表字段由其列名引用。

多个语句

ADO.NET 规范允许在单个字符串中执行多个语句。 如有查询,SqliteDataReader返回多个结果集。 它具有NextResult()方法来浏览结果集。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)       
            con.Open()

            Using cmd As New SqliteCommand(con)
                cmd.CommandText = "SELECT 25; SELECT 44; SELECT 33"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr                
                    Do                    
                        rdr.Read()
                        Console.WriteLine("{0}", rdr.GetInt32(0))
                    Loop While rdr.NextResult()         
                End Using      
            End Using

            con.Close()
        End Using

    End Sub

End Module

我们在一个 SQL 字符串中有三个查询。 将有三个结果集。

cmd.CommandText = "SELECT 25; SELECT 44; SELECT 33"

有三个SELECT语句。 它们之间用分号分隔。 它们每个都将返回一个值。

Do                    
    rdr.Read()
    Console.WriteLine("{0}", rdr.GetInt32(0))

Loop While rdr.NextResult()

Read()方法将SqliteDataReader移至下一条记录。 GetInt32()方法将值检索为 32 位带符号整数。 NextResult()将数据读取器移至下一个结果。

$ mono multiple.exe 
25
44
33

运行示例。

我们已经完成了SqliteDataReader的数据读取。

ADO.NET 的数据集

原文: http://zetcode.com/db/sqlitevb/dataset/

ADO.NET 架构由两个主要部分组成。 .NET 数据供应器和DataSet。 数据提供者是已明确设计用于数据处理和快速访问数据的组件。 创建DataSet的目的是为了独立于任何数据源进行数据访问。 它可以与多个不同的数据源,XML 数据一起使用,或者用于管理应用本地的数据。

DataSet是数据库表中数据的副本以及数据之间的关系。 它在内存中创建,并在需要对数据进行大量处理或将数据表绑定到 Winforms 控件时使用。 处理完成后,更改将被写入数据源。 DataSet是断开的关系结构。 这意味着在DataSet对象的整个生命周期中不必打开基础连接。 这使我们能够有效地使用可用的数据库连接。

DataSet可以通过多种方式填充。 我们可以使用SqliteDataAdapter类的Fill()方法。 我们可以以编程方式创建DataTableDataColumnDataRow对象。 可以从 XML 文档或流中读取数据。

SqliteDataAdapterDataSet和数据源之间的中介。 它填充DataSet并解析数据源的更新。 DataTable是内存中数据库表的表示。 可以将一个或多个数据表添加到数据集。 对DataSet所做的更改由SqliteCommandBuilder类保存到数据源。

DataGridView控件提供了用于显示数据的可自定义表。 它允许通过使用属性来定制单元格,行,列和边框。 我们可以使用此控件来显示带有或不带有基础数据源的数据。

创建一个数据表

在第一个示例中,我们将使用DataTable类。

sqlite> CREATE TABLE Friends2(Id INTEGER PRIMARY KEY, Name TEXT);

在这种情况下,必须先创建表,然后才能将任何数据保存到表中。

Option Strict On

Imports Mono.Data.Sqlite
Imports System.Data

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim table As New DataTable("Friends2")

            Dim column As New DataColumn
            Dim row As DataRow

            column = New DataColumn()
            column.DataType = System.Type.GetType("System.Int32")
            column.ColumnName = "Id"
            table.Columns.Add(column)

            column = new DataColumn()
            column.DataType = Type.GetType("System.String")
            column.ColumnName = "Name"
            table.Columns.Add(column)

            row = table.NewRow()
            row("Id") = 1
            row("Name") = "Jane"
            table.Rows.Add(row)

            row = table.NewRow()
            row("Id") = 2
            row("Name") = "Lucy"
            table.Rows.Add(row)

            row = table.NewRow()
            row("Id") = 3
            row("Name") = "Thomas"
            table.Rows.Add(row)

            Dim sql As String = "SELECT * FROM Friends2"

            Using da As New SqliteDataAdapter(sql, con)             
                Using cb As New SqliteCommandBuilder(da)                
                    da.Fill(table)
                    da.Update(table)
                End Using
            End Using

            con.Close()

        End Using

    End Sub

End Module

在示例中,我们创建一个新的DataTable对象。 我们在表中添加两列和三行。 然后,我们将数据保存在新的Friends2数据库表中。

Dim table As New DataTable("Friends2")

创建一个新的DataTable对象。

column = New DataColumn()
column.DataType = System.Type.GetType("System.Int32")
column.ColumnName = "Id"
table.Columns.Add(column)

新列将添加到表中。 我们为列提供数据类型和名称。 可通过Columns属性访问DataTable的列。

row = table.NewRow()
row("Id") = 1
row("Name") = "Jane"
table.Rows.Add(row)

在表中添加一行。 可以通过Rows属性访问DataTable的行。

Dim sql As String = "SELECT * FROM Friends2"

Using da As New SqliteDataAdapter(sql, con)  

SqliteDataAdapter是数据库表及其在内存中的表示之间的中介。

Using cb As New SqliteCommandBuilder(da)

SqliteCommandBuilder包装数据适配器。 它只需要实例化。 我们稍后不会直接使用它。

da.Fill(table)
da.Update(table)

数据适配器中填充了表中的数据。 Update方法将数据插入数据库。

保存 XML 数据

来自DataTable的数据可以轻松保存在 XML 文件中。 此任务有一种WriteXml()方法。

Option Strict On

Imports Mono.Data.Sqlite
Imports System.Data

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)        
            con.Open()

            Dim stm As String = "SELECT * FROM Cars LIMIT 5"

            Using da As New SqliteDataAdapter(stm, con)
                Dim ds As New DataSet()

                da.Fill(ds, "Cars")
                Dim dt As DataTable = ds.Tables("Cars")

                dt.WriteXml("cars.xml")

                For Each row As DataRow In dt.Rows                         
                    For Each col As DataColumn In dt.Columns                    
                        Console.Write(row(col) & " ")
                    Next
                    Console.WriteLine()
                Next      
            End Using
            con.Close()
        End Using

    End Sub

End Module

我们从Cars表中打印 5 辆汽车。 我们还将它们保存在 XML 文件中。

Using da As New SqliteDataAdapter(stm, con)

创建一个SqliteDataAdapter对象。 它以 SQL 语句和连接为参数。 SQL 语句将由SqliteDataAdapter用于检索和传递数据。

Dim ds As New DataSet()

da.Fill(ds, "Cars")

我们创建DataSet对象。 Fill()方法使用数据适配器从数据源检索数据。 它创建一个名为Cars的新DataTable,并用检索到的数据填充它。

Dim dt As DataTable = ds.Tables("Cars")

Tables属性为我们提供了DataSet中包含的数据表的集合。 从该集合中检索Cars DataTable

dt.WriteXml("cars.xml")

我们将数据表中的数据写入 XML 文件。

For Each row As DataRow In dt.Rows                         
    For Each col As DataColumn In dt.Columns                    
        Console.Write(row(col) & " ")
    Next
    Console.WriteLine()
Next    

我们将Cars表的内容显示到终端。 为了遍历数据,我们利用了DataTable对象的行和列。

加载 XML 数据

我们已经展示了如何将数据保存在 XML 文件中。 现在我们将展示如何从 XML 文件加载数据。

Option Strict On

Imports Mono.Data.Sqlite
Imports System.Data

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)   

            con.Open()

            Dim ds As New DataSet()

            ds.ReadXml("cars.xml")
            Dim dt As DataTable = ds.Tables("Cars")

            For Each row As DataRow In dt.Rows                        
                For Each col As DataColumn In dt.Columns                
                    Console.Write(row(col) + " ")
                Next
                Console.WriteLine()
            Next

            con.Close()
        End Using

    End Sub

End Module

我们将cars.xml文件的内容读入DataSet。 我们将所有行打印到终端。

Dim ds As New DataSet()

创建一个DataSet对象。

ds.ReadXml("cars.xml")

来自cars.xml的数据被读入数据集。

Dim dt As DataTable = ds.Tables("Cars")

将数据读入数据集后,将创建一个新的DataTable。 我们得到这张表。

For Each row As DataRow In dt.Rows                        
    For Each col As DataColumn In dt.Columns                
        Console.Write(row(col) + " ")
    Next
    Console.WriteLine()
Next 

我们打印数据表的所有行。

$ mono loadxml.exe 
1 Audi 52642 
2 Mercedes 57127 
3 Skoda 9000 
4 Volvo 29000 
5 Bentley 350000

运行示例。

DataGridView

在下一个示例中,我们将表绑定到 Winforms DataGridView控件。

Option Strict On

Imports System.Windows.Forms
Imports System.Drawing
Imports System.Data
Imports Mono.Data.Sqlite

Public Class WinVBApp
    Inherits Form

    Private dgv As DataGridView
    Private da As SqliteDataAdapter
    Private ds As DataSet

    Public Sub New()

       Me.Text = "DataGridView"
       Me.Size = New Size(450, 350)

       Me.InitUI()
       Me.InitData()

       Me.CenterToScreen()

    End Sub

    Private Sub InitUI()

        dgv = New DataGridView()

        dgv.Location = New Point(8, 0)
        dgv.Size = New Size(350, 300)
        dgv.TabIndex = 0
        dgv.Parent = Me 

    End Sub

    Private Sub InitData()

        Dim cs As String = "URI=file:test.db"

        Dim con As New SqliteConnection(cs)

        Dim stm As String = "SELECT * FROM Cars"

        Using con As New SqliteConnection(cs)
            con.Open()

            ds = new DataSet()

            Using da As New SqliteDataAdapter(stm, con)            
                da.Fill(ds, "Cars")
                dgv.DataSource = ds.Tables("Cars")
            End Using

            con.Close()
        End Using   

    End Sub

    Public Shared Sub Main()
        Application.Run(New WinVBApp)
    End Sub

End Class

在本示例中,我们将Cars表绑定到 Winforms DataGridView控件。

Imports System.Windows.Forms
Imports System.Drawing

这两个名称空间用于 GUI。

Me.InitUI()
Me.InitData()

InitUI()方法内部,我们构建了用户界面。 在InitData()方法中,我们连接到数据库,将数据检索到DataSe中并将其绑定到DataGrid控件。

dgv = New DataGridView()

DataGridView控件已创建。

Dim stm As String = "SELECT * FROM Cars"

我们将在DataGridView控件中显示Cars表中的数据。

dgv.DataSource = ds.Tables("Cars")

我们将DataGridView控件的DataSource属性绑定到所选表。

DataGridView

图:DataGridView

在 SQLite Visual Basic 教程的这一部分中,我们使用了DataSetDataTableSqliteDataAdapterSqliteCommandBuilderDataGridView类。

使用 Visual Basic 在 SQLite 中处理图像

原文: http://zetcode.com/db/sqliteimg/

在 SQLite Visual Basic 教程的这一章中,我们将使用图像文件。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论天气技术问题是否将图像保存在数据库中。

sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,代表二进制大对象。

插入图像

在第一个示例中,我们将图像插入 SQLite 数据库。

Option Strict On

Imports System.IO
Imports System.Data
Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim data As Byte()

            Try
                data = File.ReadAllBytes("woman.jpg")
            Catch ex As Exception
                Console.WriteLine(ex.ToString())
            End Try

            Dim cmd As New SqliteCommand(con)

            cmd.CommandText = "INSERT INTO Images(Data) VALUES (@img)"
            cmd.Prepare()

            cmd.Parameters.Add("@img", DbType.Binary, data.Length)
            cmd.Parameters("@img").Value = data
            cmd.ExecuteNonQuery()

            con.Close()

        End Using

    End Sub

End Module

我们从当前工作目录中读取图像,并将其写入 SQLite test.db数据库的Images表中。

Dim data As Byte()

图像数据将存储在字节数组中。

data = File.ReadAllBytes("woman.jpg")

ReadAllBytes()方法打开一个二进制文件,将文件的内容读取到字节数组中,然后关闭该文件。

cmd.CommandText = "INSERT INTO Images(Data) VALUES (@img)"
cmd.Prepare()

我们准备一条 SQL 语句,用于将字节数组插入Images表的Data列中。

cmd.Parameters.Add("@img", DbType.Binary, data.Length)
cmd.Parameters("@img").Value = data
cmd.ExecuteNonQuery()

我们将二进制数据绑定到预备语句。 然后执行该语句。 该图像被写入数据库表。

读取图像

在本节中,我们将执行相反的操作。 我们将从数据库表中读取图像。

Option Strict On

Imports System.IO
Imports System.Data
Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim cmd As New SqliteCommand(con)
            cmd.CommandText = "SELECT Data FROM Images WHERE Id=1"

            Dim data As Byte() = cmd.ExecuteScalar()

            Try                           
                If data IsNot Nothing
                    File.WriteAllBytes("woman2.jpg", data)
                Else 
                    Console.WriteLine("Binary data not read")
                End If

            Catch ex As Exception            
                Console.WriteLine(ex.ToString())
            End Try    

            con.Close()

        End Using

    End Sub

End Module

我们从Images表中读取图像数据,并将其写入另一个文件woman2.jpg中。

cmd.CommandText = "SELECT Data FROM Images WHERE Id=1"

该行从表中选择图像数据。

Dim data As Byte() = cmd.ExecuteScalar()

我们从数据库表中检索二进制数据。 数据存储在字节数组中。

If data IsNot Nothing
    File.WriteAllBytes("woman2.jpg", data)
Else 
    Console.WriteLine("Binary data not read")
End If

WriteAllBytes()方法创建一个新文件,将指定的字节数组写入该文件,然后关闭该文件。 如果目标文件已经存在,则将其覆盖。 当数据库表为空并运行此示例时,我们得到Nothing。 因此,我们检查Nothing值。

SQLite Visual Basic 教程的这一部分专门用于读取和写入图像。

使用 Visual Basic 获取 SQLite 元数据

原文: http://zetcode.com/db/sqlitevb/meta/

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"
        Dim nrows As String

        Try 
            Console.Write("Enter rows to fetch: ")
            nrows = Console.ReadLine()
        Catch e As FormatException
            Console.WriteLine(e.ToString())
        End Try

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT @Id"
                cmd.Prepare()

                cmd.Parameters.AddWithValue("@Id", Int32.Parse(nrows))

                Dim cols As Integer = 0
                Dim rows As Integer = 0

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr

                    cols = rdr.FieldCount
                    rows = 0

                    While rdr.Read()                    
                        rows += 1
                    End While

                    Console.WriteLine("The query fetched {0} rows", rows)
                    Console.WriteLine("Each row has {0} cols", cols)
                End Using    
            End Using

            con.Close()
        End Using

    End Sub

End Module

在上面的示例中,我们获取查询返回的行数和列数。

Try 
    Console.Write("Enter rows to fetch: ")
    nrows = Console.ReadLine()
Catch e As FormatException
    Console.WriteLine(e.ToString())
End Try

该示例要求在命令行上的行数。

cmd.CommandText = "SELECT * FROM Cars LIMIT @Id"
cmd.Prepare()

cmd.Parameters.AddWithValue("@Id", Int32.Parse(nrows))

我们选择的行数与在命令行中提供的一样多。

cols = rdr.FieldCount

可以从SqliteDataReader对象的FieldCount属性检索返回的列数。

While rdr.Read()                    
    rows += 1
End While

我们计算结果集中的行数。

$ mono fields_rows.exe 
Enter rows to fetch: 5
The query fetched 5 rows
Each row has 3 cols

输出。

列标题

接下来,我们将展示如何使用数据库表中的数据打印列标题。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr

                    Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", _
                        rdr.GetName(0), rdr.GetName(1), rdr.GetName(2)))

                    While rdr.Read()                    
                        Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", _
                            rdr.GetInt32(0), rdr.GetString(1), rdr.GetInt32(2)))
                    End While

                End Using        
            End Using

            con.Close()
        End Using

    End Sub

End Module

在此程序中,我们从Cars表中选择 5 行及其列名。

Dim rdr As SqliteDataReader = cmd.ExecuteReader()

我们创建一个SqliteDataReader对象。

Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", _
    rdr.GetName(0), rdr.GetName(1), rdr.GetName(2)))

我们使用阅读器的GetName()方法获得列的名称。 String.Format()方法用于格式化数据。

While rdr.Read()                    
    Console.WriteLine(String.Format("{0, -3} {1, -8} {2, 8}", _
        rdr.GetInt32(0), rdr.GetString(1), rdr.GetInt32(2)))
End While

我们将 SQL 语句返回的数据打印到终端。

$ mono headers.exe 
Id  Name        Price
1   Audi        52642
2   Mercedes    57127
3   Skoda        9000
4   Volvo       29000
5   Bentley    350000

该程序的输出。

受影响的行

在下面的示例中,我们将发现特定的 SQL 命令已进行了多少更改。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "Data Source=:memory:"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "CREATE TABLE Friends(Id INT, Name TEXT)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends VALUES(1, 'Tom')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends VALUES(2, 'Jane')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends VALUES(3, 'Rebekka')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends VALUES(4, 'Lucy')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends VALUES(5, 'Robert')"
                cmd.ExecuteNonQuery()

                cmd.CommandText = "DELETE FROM Friends WHERE Id IN (3, 4, 5)"
                Dim n As Integer = cmd.ExecuteNonQuery()

                Console.WriteLine("The statement has affected {0} rows", n)

            End Using

            con.Close()

        End Using

    End Sub

End Module

我们在内存中创建一个Friends表。 在最后一个 SQL 命令中,我们删除三行。 ExecuteNonQuery()方法返回上一条 SQL 命令影响的行数。

cmd.CommandText = "DELETE FROM Friends WHERE Id IN (3, 4, 5)"

在此 SQL 语句中,我们删除三行。

Dim n As Integer = cmd.ExecuteNonQuery()

我们找出上一条 SQL 语句完成的更改数量。

$ mono affected_rows.exe 
The statement has affected 3 rows

示例输出。

表架构

GetSchemaTable()方法,它返回有关每一列的元数据。 它返回许多值,其中包括列名,列大小,基表名或列是否唯一。

Option Strict On

Imports System.Data
Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT 4"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr

                    Dim schemaTable As DataTable = rdr.GetSchemaTable()

                    For Each row As DataRow In schemaTable.Rows                    
                        For Each col As DataColumn In schemaTable.Columns
                            Console.WriteLine(col.ColumnName & " = " & row(col))
                        Next
                        Console.WriteLine()
                    Next

                End Using
            End Using

            con.Close()
        End Using

    End Sub

End Module

该示例打印有关表列的大量元数据。

Dim schemaTable As DataTable = rdr.GetSchemaTable()

我们得到数据库架构表。

For Each row As DataRow In schemaTable.Rows                    
    For Each col As DataColumn In schemaTable.Columns
        Console.WriteLine(col.ColumnName & " = " & row(col))
    Next
    Console.WriteLine()
Next

我们浏览保存元数据的模式表行,并将其打印到控制台。

$ mono table_schema.exe 
ColumnName = Id
ColumnOrdinal = 0
ColumnSize = 8
NumericPrecision = 19
NumericScale = 0
IsUnique = True
IsKey = True
...

摘自示例输出。

表名

在与元数据有关的最后一个示例中,我们将列出test.db数据库中的所有表。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT name FROM sqlite_master " _
                    & "WHERE type='table' ORDER BY name"

                Dim rdr As SqliteDataReader = cmd.ExecuteReader()

                Using rdr                
                    While (rdr.Read())
                        Console.WriteLine(rdr.GetString(0))
                    End While         
                End Using        
            End Using

            con.Close()
        End Using

    End Sub

End Module

该代码示例将所选数据库中的所有可用表打印到终端。

cmd.CommandText = "SELECT name FROM sqlite_master " _
    & "WHERE type='table' ORDER BY name"

表名称是从sqlite_master表中检索的。

$ mono table_names.exe
Cars
Friends2
Images

这些是我们系统上的表。

在 SQLite Visual Basic 教程的这一部分中,我们使用了数据库元数据。

使用 Visual Basic 的 SQLite 事务

原文: http://zetcode.com/db/sqlitevb/trans/

在本章中,我们将处理事务。 首先,我们提供一些基本定义。 然后,我们将有一些程序显示如何处理事务。

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

在 SQLite 中,除SELECT以外的任何命令都将启动隐式事务。 同样,在事务中,诸如CREATE TABLE ...,VACUUMPRAGMA之类的命令将在执行之前提交先前的更改。

手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE。 除非我们启动自己的事务,否则 SQLite 会自动将每个命令放入其自己的事务中。 请注意,这也可能会受到驱动程序的影响。 SQLite Python 驱动程序默认情况下已关闭自动提交模式,并且第一个 SQL 命令启动新事务。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "DROP TABLE IF EXISTS Friends"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                                    & "Name TEXT)"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
                cmd.ExecuteNonQuery()
                cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
                cmd.ExecuteNonQuery()

            End Using

            con.Close()

        End Using

    End Sub

End Module

我们创建一个Friends表并用数据填充它。 我们没有明确地启动事务,也没有调用commitrollback方法。 但是数据已写入表中。 这是因为默认情况下,我们在自动提交模式下工作。 在这种模式下,每个 SQL 语句立即生效。

cmd.CommandText = "DROP TABLE IF EXISTS Friends"
cmd.ExecuteNonQuery()
cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                    & "Name TEXT)"
cmd.ExecuteNonQuery()

如果Friends表已经存在,我们将其删除。 然后,我们使用CREATE TABLE语句创建表。

cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
cmd.ExecuteNonQuery()
cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
cmd.ExecuteNonQuery()
...

我们插入两行。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian

Friends表已成功创建。

在第二个示例中,我们将使用BeginTransaction()方法启动自定义事务。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim tr As SqliteTransaction = con.BeginTransaction()

            Using tr 

                Using cmd As New SqliteCommand(con)

                    cmd.Transaction = tr
                    cmd.CommandText = "DROP TABLE IF EXISTS Friends"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _ 
                                        & "Name TEXT)"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
                    cmd.ExecuteNonQuery()
                    cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')"
                    cmd.ExecuteNonQuery()
                End Using

                tr.Commit()
            End Using

            con.Close()
        End Using

    End Sub

End Module

所有 SQL 命令组成一个单元。 要么全部保存,要么什么都不保存。 这是事务背后的基本思想。

Dim tr As SqliteTransaction = con.BeginTransaction()

BeginTransaction()方法启动事务。

cmd.Transaction = tr

我们设置SqliteCommand在其中执行的事务。

tr.Commit()

如果一切正常,我们将整个事务提交到数据库。 如果发生异常,则事务将在后台回滚。

显式回滚调用

现在,我们将显示一个示例,在发生异常的情况下,我们手动回滚事务。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Dim con As SqliteConnection     
        Dim tr As SqliteTransaction
        Dim cmd As SqliteCommand

        Try
            con = New SqliteConnection(cs)

            con.Open()

            tr = con.BeginTransaction()
            cmd = con.CreateCommand()

            cmd.Transaction = tr
            cmd.CommandText = "DROP TABLE IF EXISTS Friends"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY," _
                              &  "Name TEXT)"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Tom')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Rebecca')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jim')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Robert')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Julian')"
            cmd.ExecuteNonQuery()
            cmd.CommandText = "INSERT INTO Friends(Name) VALUES ('Jane')"
            cmd.ExecuteNonQuery()

            tr.Commit()

        Catch ex As SqliteException

            Console.WriteLine("Error: {0}",  ex.ToString())

            If tr IsNot Nothing
                Try                 
                    tr.Rollback()                    
                Catch ex2 As SqliteException
                    Console.WriteLine("Transaction rollback failed.")
                    Console.WriteLine("Error: {0}",  ex2.ToString())
                Finally                
                    tr.Dispose()
                End Try
            End If
        Finally 
            If cmd IsNot Nothing
                cmd.Dispose()
            End If

            If tr IsNot Nothing
                tr.Dispose()
            End If

            If con IsNot Nothing
                Try                 
                    con.Close()
                Catch ex As SqliteException                
                    Console.WriteLine("Closing connection failed.")
                    Console.WriteLine("Error: {0}",  ex.ToString())
                Finally                
                    con.Dispose()
                End Try
            End If
        End Try    

    End Sub

End Module

我们创建自己的TryCatchFinally块,在其中处理可能的问题。

Catch ex As SqliteException

    Console.WriteLine("Error: {0}",  ex.ToString())

    If tr IsNot Nothing
        Try                 
            tr.Rollback()                    
        Catch ex2 As SqliteException
            Console.WriteLine("Transaction rollback failed.")
            Console.WriteLine("Error: {0}",  ex2.ToString())
        Finally                
            tr.Dispose()
        End Try
    End If

在创建Friends表的过程中引发异常时,我们将调用Rollback()方法。 回滚事务也可能失败; 我们检查这种情况。

If cmd IsNot Nothing
    cmd.Dispose()
End If

If tr IsNot Nothing
    tr.Dispose()
End If

当一切顺利时,我们将配置资源。

If con IsNot Nothing
    Try                 
        con.Close()
    Catch ex As SqliteException                
        Console.WriteLine("Closing connection failed.")
        Console.WriteLine("Error: {0}",  ex.ToString())
    Finally                
        con.Dispose()
    End Try
End If

关闭连接时,我们可能会收到另一个异常。 我们在这里处理这种情况。

错误

当事务中存在错误时,将回滚事务,并且不会将任何更改提交到数据库。

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Dim tr As SqliteTransaction = con.BeginTransaction()

            Using tr 

                Using cmd As New SqliteCommand(con)

                    cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                    cmd.Transaction = tr
                    cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1"
                    cmd.ExecuteNonQuery()

                    cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
                    cmd.ExecuteNonQuery()

                    tr.Commit()
                End Using
            End Using
            con.Close()
        End Using

    End Sub

End Module

在代码示例中,我们要更改两个名称。 有两个语句构成一个事务。 第二个 SQL 语句中有错误。 因此,该事务将回滚。

cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
cmd.ExecuteNonQuery()

表名称不正确。 数据库中没有Friend表。

$ mono error.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend
...

运行示例将显示此错误消息。 事务回滚。

sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Jane

Friends表中没有发生任何变化。 即使第一个UPDATE语句正确。

我们将再次尝试更改两行; 这次不使用SqliteTransaction

Option Strict On

Imports Mono.Data.Sqlite

Module Example

    Sub Main()

        Dim cs As String = "URI=file:test.db"

        Using con As New SqliteConnection(cs)

            con.Open()

            Using cmd As New SqliteCommand(con)

                cmd.CommandText = "SELECT * FROM Cars LIMIT 5"

                cmd.CommandText = "UPDATE Friends SET Name='Thomas' WHERE Id=1"
                cmd.ExecuteNonQuery()

                cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
                cmd.ExecuteNonQuery()

            End Using

            con.Close()
        End Using

    End Sub

End Module

我们尝试更新Friends表中的两个名称,汤姆(Tom)到托马斯(Thomas)和罗伯特(Robert)到鲍勃(Bob)。

cmd.CommandText = "UPDATE Friend SET Name='Bob' WHERE Id=4"
cmd.ExecuteNonQuery()

UPDATE语句不正确。

$ mono error2.exe 

Unhandled Exception: Mono.Data.Sqlite.SqliteException: SQLite error
no such table: Friend
...

我们收到与上一个示例相同的错误消息。

sqlite> SELECT * FROM Friends;
1|Thomas
2|Rebecca
3|Jim
4|Robert
5|Julian
6|Jane

但是这一次,第一个UPDATE语句被保存。 第二个不是。

在 SQLite Visual Basic 教程的这一部分中,我们处理了事务。

MySQL 数据类型

原文: http://zetcode.com/databases/mysqltutorial/datatypes/

在 MySQL 教程的这一部分中,我们介绍了 MySQL 数据类型。

数据类型是一组可表示的值。 每个值都属于一种数据类型。 可以通过名称引用的项目,例如 SQL 参数,列,字段,属性和变量,也具有声明的类型。

MySQL 支持以下数据类型组:

为列选择正确的数据类型是数据库初始设计的一部分。 数据类型可确保所提供数据的正确性。 他们确保以有意义的方式使用数据。 当我们进行比较,数据排序时,这一点很重要。 例如,日期比较与数字比较不同。 使用我们的表的其他开发者将知道期望从数据库架构中获取哪些数据。 数据类型使 MySQL 可以对插入的数据进行验证。 最后,使用正确的表列数据类型,我们允许 MySQL 优化查询并使用更少的磁盘空间。

数字

数值类型可以是整数,也可以是浮点数。

  1. 整数
    1. 天音
    2. SMALLINT
    3. 中号
    4. 整数
    5. 比金特
  2. 浮点数
    1. 浮动
    2. 十进制

整数

整数是实数的子集。 它们写时没有小数或小数部分。 整数落入集合Z = {..., -2, -1, 0, 1, 2, ......}中,整数是无限的。 实际上,计算机只能使用整数值的子集,因为计算机的容量有限。 整数用于计算离散实体。 我们可以有 3、4、6 辆车,但不能有 3.33 辆车。 我们可以有 3.33 公斤。

以下是 MySQL 中整数类型的表:TINYINTMEDIUMINTBIGINT是 SQL 标准的 MySQL 扩展。

Table 1: Signed integer types

数据类型 字节数 最小值 最大值
TINYINT 1 -128 127
SMALLINT 2 -32768 32767
MEDIUM 3 -8388608 8388607
INT 4 -2147483648 2147483647
BIGINT 8 -9223372036854775808 9223372036854775807

整数类型的存储方式不同。 我们可以选择符合我们要求的值。

mysql> CREATE TABLE Ages(Id SMALLINT, Age TINYINT) ENGINE=Memory;

我们已经创建了一个临时的Ages表。 这只是一个临时测试表,因此只有几行。 SMALLINT就足够了。 我们不认识任何年龄超过 130 岁的人,因此Age列可以使用TINYINT

mysql> INSERT INTO Ages VALUES(1, 43);
Query OK, 1 row affected (0.00 sec)

我们在表中插入一行。

mysql> INSERT INTO Ages VALUES (2, 128);
ERROR 1264 (22003): Out of range value for column 'Age' at row 1

尝试插入列类型范围之外的值会导致错误。

当我们处理年龄时,我们不需要负整数值。 MySQL 支持无符号整数。 这样,我们可以进一步优化表定义。

Table 2: Unsigned integer types

数据类型 字节数 最小值 最大值
TINYINT 1 个 0 255
SMALLINT 2 0 65535
MEDIUM 3 0 16777215
INT 4 0 4294967295
BIGINT 8 0 18446744073709551615

我们使用 SQL 语句将Age列更改为具有TINYINT UNSIGNED数据类型。

mysql> ALTER TABLE Ages MODIFY Age TINYINT UNSIGNED;

现在我们可以插入 0 到 255 之间的值。

mysql> INSERT INTO Ages VALUES(2, 128);
Query OK, 1 row affected (0,00 sec)

mysql> SELECT * FROM Ages;
+------+------+
| ID   | Age  |
+------+------+
|    1 |   43 |
|    2 |  128 |
+------+------+
2 rows in set (0,00 sec)

我们插入了一个假设的年龄 128。现在该列接受它。

浮点值

浮点数表示计算中的实数。 实数测量连续的量,例如重量,高度或速度。

MySQL 具有用于近似值的浮点类型:FLOATDOUBLE和用于精确值的定点类型:DECIMALNUMERIC

FLOAT是单精度浮点数。 MySQL 使用四个字节来存储FLOAT值。 DOUBLE是双精度浮点数。 MySQL 使用八个字节来存储DOUBLE值。

MySQL 将DOUBLE视为DOUBLE PRECISION(非标准扩展名)的同义词。 另外,除非启用REAL_AS_FLOAT SQL 模式,否则 MySQL 还将REAL视为DOUBLE PRECISION的同义词。

DECIMALNUMERIC类型存储精确的数值数据值。 当保留精确度非常重要时,例如使用货币数据,则使用这些类型。 在 MySQL 中,NUMERICDECIMAL的同义词。

浮点数,双精度数和小数可能已指定其精度和小数位数。 在DECIMAL[M, D]中,M是最大位数,即精度。 D是小数点右边的位数。 它是规模。 如果您有一列带有DECIMAL(3, 1)的列,则可以插入最多三位数的数字:小数点之前和之后的wwo

mysql> SELECT 1/3;
+--------+
| 1/3    |
+--------+
| 0.3333 |
+--------+
1 row in set (0,00 sec)

mysql> SELECT 0.3333 = 1/3;
+--------------+
| 0.3333 = 1/3 |
+--------------+
|            0 |
+--------------+
1 row in set (0,00 sec)

您可能希望第二条 SQL 语句中的比较返回true,但事实并非如此。 原因是如何存储浮点值。

使用浮点值时必须格外小心。 浮点数和双精度词处理起来更快,但它们到最后一位数字不准确。 舍入误差很小,在许多情况下都可以。 在许多真实的单词情况下,我们只需要有一个近似值。 例如,您在一家商店里有 7.5321 公斤苹果和 4.372 公斤橘子。 可以将这两个值分别存储为 7.5 kg 和 4.4 kg。 没什么大不了。 另一方面,在精确的科学或货币计算中,需要高精度。 在这种情况下,我们使用DECIMAL数据类型。

mysql> CREATE TABLE Numbers (Id TINYINT, Floats FLOAT, Decimals DECIMAL(3, 2));

我们创建一个表,其中将存储一些浮点数和小数。

mysql> INSERT INTO Numbers VALUES (1, 1.1, 1.1), (2, 1.1, 1.1), (3, 1.1, 1.1);

我们将三行插入到新创建的表中。

mysql> SELECT * FROM Numbers;
+------+--------+----------+
| Id   | Floats | Decimals |
+------+--------+----------+
|    1 |    1.1 |     1.10 |
|    2 |    1.1 |     1.10 |
|    3 |    1.1 |     1.10 |
+------+--------+----------+
3 rows in set (0,00 sec)

这是表的外观。

mysql> SELECT SUM(Floats), SUM(Decimals) FROM Numbers;
+--------------------+---------------+
| SUM(Floats)        | SUM(Decimals) |
+--------------------+---------------+
| 3.3000000715255737 |          3.30 |
+--------------------+---------------+
1 row in set (0,08 sec)

两种结果不同。 十进制计算更精确。 由于一些内部舍入,浮点数的总和不准确。

日期&时间值

MySQL 具有用于存储日期和时间的数据类型:DATETIMEDATETIMEYEARTIMESTAMP。 MySQL 试图以几种格式解释日期和时间值,但是日期部分必须始终以年/月/日的顺序给出。 如果在数字上下文中使用日期或时间值,则 MySQL 自动将日期或时间值转换为数字,反之亦然。

日期

DATE用于存储日期。 MySQL 检索并以YYYY-MM-DD格式显示日期值。 支持的范围是1000-01-019999-12-31

mysql> SELECT CURDATE();
+------------+
| CURDATE()  |
+------------+
| 2017-01-31 |
+------------+
1 row in set (0,00 sec)

CURDATE()函数返回当前日期。

mysql> SELECT DATE('2017-01-31 12:01:00');
+-----------------------------+
| DATE('2017-01-31 12:01:00') |
+-----------------------------+
| 2017-01-31                  |
+-----------------------------+
1 row in set (0,00 sec)

DATE()函数返回日期和时间值的日期部分。

mysql> SELECT ADDDATE('2017-01-20', 8);
+--------------------------+
| ADDDATE('2017-01-20', 8) |
+--------------------------+
| 2017-01-28               |
+--------------------------+
1 row in set (0,00 sec)

ADDDATE()函数将日期添加到日期。 它返回计算的日期。

mysql> CREATE TABLE Dates(Id TINYINT, Dates DATE);
mysql> INSERT INTO Dates VALUES(1, '2017-01-24');
mysql> INSERT INTO Dates VALUES(2, '2017/01/25');
mysql> INSERT INTO Dates VALUES(3, '20170126');
mysql> INSERT INTO Dates VALUES(4, '170127');
mysql> INSERT INTO Dates VALUES(5, '2017+01+28');

日期在 MySQL 中以一种格式显示,但是我们可以在 SQL 语句中使用各种日期格式。 YYYY-MM-DD是标准格式。 日期部分之间可以使用任何标点符号。

mysql> SELECT * FROM Dates;
+------+------------+
| Id   | Dates      |
+------+------------+
|    1 | 2017-01-24 |
|    2 | 2017-01-25 |
|    3 | 2017-01-26 |
|    4 | 2017-01-27 |
|    5 | 2017-01-28 |
+------+------------+
5 rows in set (0,00 sec)

我们使用多种格式将日期插入表中。 MySQL 使用一种格式来显示日期。

mysql> INSERT INTO Dates VALUES (6, '10000-01-01');
ERROR 1292 (22007): Incorrect date value: '10000-01-01' for column 'Dates' at row 1

如果我们超出了支持的日期值范围,则会发生错误。

时间

TIME数据类型用于在 MySQL 中显示时间。 它以HH:MM:SS格式显示值。 MySQL 检索并以'HH:MM:SS'格式或'HHH:MM:SS'格式显示TIME值以获得较大的小时值。 范围是从-838:59:59838:59:59。 时间格式的小时部分可能大于 24。这是因为TIME数据类型可用于表示时间间隔。 这也是为什么我们可以使用负时间值的原因。

mysql> SELECT CURTIME();
+-----------+
| CURTIME() |
+-----------+
| 11:47:36  |
+-----------+
1 row in set (0,00 sec)

CURTIME()函数返回当前时间。

mysql> SELECT TIMEDIFF('23:34:32', '22:00:00');
+----------------------------------+
| TIMEDIFF('23:34:32', '22:00:00') |
+----------------------------------+
| 01:34:32                         |
+----------------------------------+
1 row in set (0,02 sec)

TIMEDIFF()函数用于减去两个时间值。

mysql> SELECT TIME('2017-01-31 11:06:43');
+-----------------------------+
| TIME('2017-01-31 11:06:43') |
+-----------------------------+
| 11:06:43                    |
+-----------------------------+
1 row in set (0,00 sec)

我们可以使用TIME()函数提取日期和时间值的时间部分。

mysql> SELECT TIMEDIFF('211344', 201123);
+----------------------------+
| TIMEDIFF('211344', 201123) |
+----------------------------+
| 01:02:21                   |
+----------------------------+
1 row in set (0,00 sec)

我们也可以用不同的格式编写时间值。 第一个参数是没有分隔符的字符串格式的时间值。 第二个是指定为数字的时间值。

时间日期

DATETIME值包含日期和时间。 MySQL 检索并以YYYY-MM-DD HH:MM:SS格式显示值。 支持的范围是1000-01-01 00:00:009999-12-31 23:59:59

mysql> SELECT NOW();
+---------------------+
| NOW()               |
+---------------------+
| 2017-01-31 11:57:53 |
+---------------------+
1 row in set (0,00 sec)

NOW()函数返回当前日期时间。

mysql> SELECT DAYNAME('2017@01@31 11@12@12');
+--------------------------------+
| DAYNAME('2017@01@31 11@12@12') |
+--------------------------------+
| Tuesday                        |
+--------------------------------+
1 row in set (0,02 sec)

MySQL 仅以一种格式显示日期和时间。 但是在我们的 SQL 语句中,我们可以使用不同的格式。 任何标点符号都可以用作日期部分或时间部分之间的分隔符。 在本例中,我们使用了@字符。

YEAR是用于表示年份的数据类型。 MySQL 以YYYY格式显示YEAR值。 它允许我们使用字符串或数字将值分配给YEAR列。 允许的范围是 1901 至 2155,即 0000。非法年份值转换为 0000。

mysql> SELECT YEAR(CURDATE()) AS 'Current year';
+--------------+
| Current year |
+--------------+
|         2017 |
+--------------+
1 row in set (0,02 sec)

在上面的 SQL 语句中,我们检索了当年。

时间戳

时间戳是一个字符序列,表示某个事件发生的日期和/或时间。 时间戳通常用于记录事件。 在 MySQL 中,我们有TIMESTAMP数据类型用于创建时间戳。 TIMESTAMP列可用于记录INSERTUPDATE操作的日期和时间。 如果您自己没有给它一个值,它将自动设置为最近一次操作的日期和时间。 TIMESTAMP数据类型的范围为1970-01-01 00:00:01 UTC 到2038-01-19 03:14:07 UTC。

下表总结了支持的TIMESTAMP格式。

数据类型 格式
TIMESTAMP(14) YYYYMMDDHHMMSS
TIMESTAMP(12) YYMMDDHHMMSS
TIMESTAMP(10) YYMMDDHHMM
TIMESTAMP(8) YYYYMMDD
TIMESTAMP(6) YYMMDD
TIMESTAMP(4) YYMM
TIMESTAMP(2) YY

TIMESTAMP数据类型提供自动初始化和更新。 我们可以限制此数据类型为仅具有自动初始化或仅自动更新。

mysql> CREATE TABLE Prices(Id TINYINT PRIMARY KEY, Price DECIMAL(8, 2), Stamp TIMESTAMP);
mysql> INSERT INTO Prices(Id, Price) VALUES(1, 234.34);
mysql> INSERT INTO Prices(Id, Price) VALUES(2, 344.12);

我们创建一个带有TIMESTAMP列的表。 我们在表中插入两行。 Stamp列不包含在 SQL 语句中。 MySQL 自动填充该列。

mysql> SELECT * FROM Prices;
+----+--------+---------------------+
| Id | Price  | Stamp               |
+----+--------+---------------------+
|  1 | 234.34 | 2017-01-31 12:12:25 |
|  2 | 344.12 | 2017-01-31 12:15:10 |
+----+--------+---------------------+
2 rows in set (0,00 sec)

两行的时间戳已创建。 这是TIMESTAMP数据类型的自动初始化。 创建表时,可以通过Stamp TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP SQL 代码将其关闭。

mysql> UPDATE Prices SET Price=250.50 WHERE Id=1;

我们执行 SQL 语句以更新第一行中的Price列。

mysql> SELECT * FROM Prices;
+----+--------+---------------------+
| Id | Price  | Stamp               |
+----+--------+---------------------+
|  1 | 250.50 | 2017-01-31 12:17:21 |
|  2 | 344.12 | 2017-01-31 12:15:10 |
+----+--------+---------------------+
2 rows in set (0,00 sec)

第一列的时间戳已更新。 如果要关闭TIMESTAMP的自动更新,则可以在创建表时使用Stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP SQL 代码。

字符串

MySQL 具有以下字符串数据类型:

  • CHAR
  • VARCHAR
  • BINARY
  • VARBINARY
  • BLOB
  • TEXT
  • ENUM
  • SET

CHAR

CHAR是固定长度的字符数据类型。 声明的长度为CHAR(x),其中x可以在 0 到 255 之间。CHAR始终为每个条目使用相同数量的存储空间。 如果我们指定的项目短于声明的长度,则该值将用空格右填充到指定的长度。 检索值时,尾部空格将被删除。

mysql> CREATE TABLE Chars(Id TINYINT PRIMARY KEY, Chars CHAR(3));
mysql> INSERT INTO Chars VALUES (1, 'a'), (2, 'ab'), (3, 'abc'), (4, 'abb');

在上面的 SQL 代码中,我们创建了Chars表,该表具有CHAR数据类型的一列。 最大长度设置为三个字符。

mysql> INSERT INTO Chars VALUES (5, 'abcd');
ERROR 1406 (22001): Data too long for column 'Chars' at row 1

尝试插入比指定长度更大的字符串会导致错误。

mysql> SELECT * FROM Chars;
+------+-------+
| Id   | Chars |
+------+-------+
|    1 | a     |
|    2 | ab    |
|    3 | abc   |
|    4 | abb   |
+------+-------+
4 rows in set (0,00 sec)

这就是我们表中的内容。

mysql> SELECT Id, LENGTH(Chars) AS Length FROM Chars;
+------+--------+
| Id   | Length |
+------+--------+
|    1 |      1 |
|    2 |      2 |
|    3 |      3 |
|    4 |      3 |
+------+--------+
4 rows in set (0,00 sec)

我们已经检索了 ID 和插入字符的长度。 之前我们已经说过,字符以固定大小存储。 为什么现在行的大小值不同? 我们希望每一行都包含 3 个字符。 原因是 MySQL 在数据检索时会修剪char的空间。 通过将sql_mode设置为PAD_CHAR_TO_FULL_LENGTH,空格也会被修剪。

mysql> SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
Query OK, 0 rows affected, 1 warning (0,00 sec)

mysql> SELECT Id, LENGTH(Chars) AS Length FROM Chars;
+------+--------+
| Id   | Length |
+------+--------+
|    1 |      3 |
|    2 |      3 |
|    3 |      3 |
|    4 |      3 |
+------+--------+
4 rows in set (0,00 sec)

通过更改sql_mode,我们可以获得预期的结果。

VARCHAR

VARCHAR数据类型存储可变长度的字符串。 字符串的长度可以在 0 到 65535 之间。VARCHAR值在存储时不会被填充。 在存储和检索值时保留尾随空格。 大多数较短的字符串数据类型都存储在此数据类型中。 例如电子邮件,人员名称,商品或地址。

mysql> CREATE TABLE FirstNames(Id TINYINT, Firstname VARCHAR(20));
mysql> INSERT INTO FirstNames VALUES (1, 'Tom'), (2, 'Lucy'), (3, 'Alice'),
    -> (4, 'Robert'), (5, 'Timothy'), (6, 'Alexander');

我们创建一个FirstNames表,其中存储六个名字。

mysql> SELECT Id, LENGTH(FirstName) AS Length FROM FirstNames;
+------+--------+
| Id   | Length |
+------+--------+
|    1 |      3 |
|    2 |      4 |
|    3 |      5 |
|    4 |      6 |
|    5 |      7 |
|    6 |      9 |
+------+--------+
6 rows in set (0,00 sec)

我们可以看到VARCHAR列类型的名称以可变长度存储。 这样可以节省磁盘空间。

BINARYVARBINARY

BINARYVARBINARY是二进制字节数据类型。 它们包含字节字符串而不是字符串。 他们没有字符集。 排序和比较基于值中字节的数字值。 BINARY数据类型的范围是 0 到 255。它以固定长度存储值。 VARBINARY的范围是 0 到 65535。

BLOB

BLOB是二进制大对象数据类型。 它可以保存可变数量的二进制数据。 它可以用于存储图像或文档等二进制数据。 BLOB有四种类型:

BLOB类型 范围(以字节为单位)
TINYBLOB 0-255
BLOB 0-65535
MEDIUMBLOB 0-16777215
LONGBLOB 0-4294967295

接下来,我们将读取和写入图像。

mysql> CREATE TABLE Images(Id INT PRIMARY KEY, Img LONGBLOB);

创建带有LONGBLOB列的表。

mysql> SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-----------------------+
| Variable_name    | Value                 |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
1 row in set (0,02 sec)

MySQL 在加载和转储数据方面有安全限制。 secure_file_priv显示允许进行此类操作的目录路径。

mysql> INSERT INTO Images VALUES (1, LOAD_FILE('/var/lib/mysql-files/image1.jpg'));

借助LOAD_FILE()函数,我们将图像插入Images表。

mysql> SELECT Img FROM Images WHERE Id=1 INTO DUMPFILE '/var/lib/mysql-files/image_bck.jpg';

我们从表中选择图像并将其写入/var/lib/mysql-files目录中的文件。

$ sudo ls /var/lib/mysql-files/ -l
total 608
-rw-r--r-- 1 root  root  309262 jan 31 13:08 image1.jpg
-rw-rw-rw- 1 mysql mysql 309262 jan 31 13:12 image_bck.jpg

现在,我们应该在目录中拥有这两个文件。

TEXT

TEXT数据类型用于存储大型文本数据。 例如文章,博客或页面。 当VARCHAR和其他基于字符串的数据对象不足以处理所需信息量的存储时,最好使用TEXT值。

文本类型 范围(以字节为单位)
TINYTEXT 0-255
TEXT 0-65535
MEDIUMTEXT 0-16777215
LONGTEXT 0-4294967295

插入时没有填充,选择时也不会删除字节。

ENUM

ENUM是一个字符串对象,其值是从允许的值列表中选择的。 在列规范中明确枚举了它们。 我们只能从列表中插入一个值。

mysql> CREATE TABLE Sizes(Size ENUM('S', 'M', 'L', 'XL', 'XXL'));

我们创建一个表,该表具有ENUM类型的一列。 明确列出了允许值的列表。

mysql> INSERT INTO SizeTable VALUES ('S'), ('L');

我们在表中插入两行。

mysql> INSERT INTO Sizes VALUES ('Large');
ERROR 1265 (01000): Data truncated for column 'Size' at row 1

由于列表中未提及'Large',因此我们会收到一条错误消息。

mysql> SELECT * FROM Sizes;
+------+
| Size |
+------+
| S    |
| L    |
+------+
2 rows in set (0,00 sec)

该表中有两个常规值。

SET

SET是一个字符串对象,可以具有零个或多个值,每个值都必须从允许值列表中选择。 它类似于ENUM数据类型。 不同之处在于,它可以包含允许值列表中的零个或多个值。

mysql> CREATE TABLE Letters(Let SET('a', 'b', 'c', 'd', 'e'));

我们创建一个允许在列上使用一组字母的表。

mysql> INSERT INTO Letters VALUES ('a');
mysql> INSERT INTO Letters VALUES ('b');
mysql> INSERT INTO Letters VALUES ('b,a');
mysql> INSERT INTO Letters VALUES ('');
mysql> INSERT INTO Letters VALUES ('a,b,c');

mysql> SELECT * FROM Letters;
+-------+
| Let   |
+-------+
| a     |
| b     |
| a,b   |
|       |
| a,b,c |
+-------+
5 rows in set (0,00 sec)

我们添加了SET允许的各种字母组合。

JSON 格式

从 MySQL 5.7.8 开始,MySQL 支持本机 JSON 数据类型。

JSON(JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类很容易读写,机器也很容易解析和生成。

MySQL 自动验证存储在 JSON 列中的 JSON 文档。 无效的文档会产生错误。 存储在 JSON 列中的 JSON 文档已经过优化,可实现高效访问。 JSON 列不能具有默认值。

mysql> CREATE TABLE t1 (Doc JSON);

创建带有JSON列的表。

mysql> INSERT INTO t1 VALUES('{"chair": "5", "table": "4", "lamp": "6"}');

将文档添加到表中。

mysql> SELECT * FROM t1;
+-------------------------------------------+
| Doc                                       |
+-------------------------------------------+
| {"lamp": "6", "chair": "5", "table": "4"} |
+-------------------------------------------+
1 row in set (0,00 sec)

我们显示表的内容。

mysql> SELECT JSON_ARRAY('pen', 4, 'pencil', 2, 'rubber', 1);
+------------------------------------------------+
| JSON_ARRAY('pen', 4, 'pencil', 2, 'rubber', 1) |
+------------------------------------------------+
| ["pen", 4, "pencil", 2, "rubber", 1]           |
+------------------------------------------------+
1 row in set (0,02 sec)

JSON_ARRAY()函数获取值列表,并将其转换为 JSON 数组。

mysql> SELECT JSON_OBJECT('pen', 4, 'pencil', 2, 'rubber', 1);
+-------------------------------------------------+
| JSON_OBJECT('pen', 4, 'pencil', 2, 'rubber', 1) |
+-------------------------------------------------+
| {"pen": 4, "pencil": 2, "rubber": 1}            |
+-------------------------------------------------+
1 row in set (0,00 sec)

JSON_OBJECT()函数获取键/值对的列表,然后返回包含这些对的 JSON 对象。

MySQL 教程的这一部分专门针对 MySQL 数据类型。

PostgreSQL C 教程

原文: http://zetcode.com/db/postgresqlc/

这是 PostgreSQL 数据库的 C 编程教程。 它涵盖了使用 C API 进行 PostgreSQL 编程的基础。

关于 PostgreSQL 数据库

PostgreSQL 是一个功能强大的开源对象关系数据库系统。 它是一个多用户数据库管理系统。 它可以在多个平台上运行,包括 Linux,FreeBSD,Solaris,Microsoft Windows 和 Mac OSX。PostgreSQL 由 PostgreSQL 全球开发小组开发。

PostgreSQL 具有复杂的功能,例如多版本并发控制(MVCC),时间点恢复,表空间,异步复制,嵌套事务(保存点),联机/热备份,复杂的查询计划程序/优化程序以及用于容错的预写日志记录 。 它支持国际字符集,多字节字符编码,Unicode,并且对于排序,区分大小写和格式具有区域设置意识。

安装

我们将安装 PostgreSQL 数据库和 C 开发库。

$ sudo apt-get install postgresql

在基于 Debian 的系统上,我们可以使用上述命令从包中安装 PostgreSQL 数据库。

$ sudo update-rc.d -f postgresql remove
 Removing any system startup links for /etc/init.d/postgresql ...
   /etc/rc0.d/K21postgresql
   /etc/rc1.d/K21postgresql
   /etc/rc2.d/S19postgresql
   /etc/rc3.d/S19postgresql
   /etc/rc4.d/S19postgresql
   /etc/rc5.d/S19postgresql
   /etc/rc6.d/K21postgresql

如果我们从包中安装 PostgreSQL 数据库,它将自动添加到操作系统的启动脚本中。 如果我们仅学习使用数据库,则不必在每次引导系统时都启动数据库。 上面的命令删除 PostgreSQL 数据库的所有系统启动链接。

$ sudo apt-get install libpq-dev

为了能够编译 C 示例,我们需要安装 PostgreSQL C 开发库。 上面的行显示了我们如何在基于 Debian 的 Linux 上做到这一点。

$ sudo -u postgres psql postgres
psql (9.3.9)
Type "help" for help.

postgres=# \password postgres

我们为postgres用户设置了密码。

启动和停止 PostgreSQL

在下一节中,我们将展示如何启动 PostgreSQL 数据库,停止它以及查询它的状态。

$ sudo service postgresql start
 * Starting PostgreSQL 9.3 database server     [ OK ]

在基于 Debian 的 Linux 上,我们可以使用service postgresql start命令启动服务器。

$ sudo service postgresql status
9.3/main (port 5432): online

我们使用service postgresql status命令检查 PostgreSQL 是否正在运行。

$ sudo service postgresql stop
 * Stopping PostgreSQL 9.3 database server     [ OK ]

我们使用service postgresql stop命令停止 PostgreSQL。

$ service postgresql status
9.3/main (port 5432): down

此时,service postgresql status命令报告 PostgreSQL 数据库已关闭。

创建用户和数据库

在以下步骤中,我们将创建一个新的数据库用户和数据库。

$ sudo -u postgres createuser janbodnar

我们在 PostgreSQL 系统中创建了一个新角色。 我们允许它具有创建新数据库的能力。 角色是数据库世界中的用户。 角色与操作系统用户是分开的。

$ sudo -u postgres psql postgres
psql (9.3.9)
Type "help" for help.

postgres=# ALTER USER janbodnar WITH password 'pswd37';
ALTER ROLE
postgres=# \q

使用psql命令,为新用户添加密码。

PostgreSQL 通常在本地连接上使用信任或对等认证策略。 在信任认证策略的情况下,PostgreSQL 假定可以连接到服务器的任何人都可以使用他们指定的任何数据库用户名(甚至超级用户名)访问数据库。 与数据库建立连接时,不需要密码。 (在数据库和用户列中所做的限制仍然适用。)信任认证对于单用户工作站上的本地连接是适当的,并且非常方便。 它通常不适用于多用户计算机。 如果使用对等认证策略,则数据库用户名必须与操作系统用户名匹配。

$ sudo -u postgres createdb testdb --owner janbodnar

使用createdb命令,我们创建了一个名为testdb的新数据库。 它的所有者是新的数据库用户。

C99

本教程使用 C99。 对于 GNU C 编译器,我们需要添加-std=c99选项。 对于 Windows 用户,强烈建议使用 Pelles C IDE。 (MSVC 不支持 C99。)

PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

在 C99 中,我们可以将声明与代码混合使用。 在较早的 C 程序中,我们需要将这一行分成两行。

for(int i=0; i<rows; i++) {

我们还可以将初始化用于循环声明。

libpq 库

libpq库是 PostgreSQL 的 C 接口。 它是一组库函数,允许客户端程序与 PostgreSQL 交互。 它也是其他 PostgreSQL 应用接口的基础引擎,包括为 C++ ,Perl,PHP,Ruby,Python 和 Tcl 编写的接口。

lib_version.c

#include <stdio.h>
#include <libpq-fe.h>

int main() {

    int lib_ver = PQlibVersion();

    printf("Version of libpq: %d\n", lib_ver);

    return 0;
}

该程序将打印libpq库的版本。

#include <libpq-fe.h>

libpq-fe.h文件包含 C 编程接口的枚举,结构,常量和函数的定义。

int lib_ver = PQlibVersion();

PQlibVersion()函数返回正在使用的libpq的版本。

$ pg_config --includedir
/usr/include/postgresql
$ pg_config --libdir
/usr/lib

pg_config工具用于查找 C 头文件和目标代码库的位置。

$ gcc -o lib_version lib_version.c -I/usr/include/postgresql -lpq -std=c99

使用上面的命令,我们编译程序。

$ ./lib_version 
Version of libpq: 90309

该库的版本是 9.3.9。

服务器版本

在下面的示例中,我们找到了 PostgreSQL 数据库的版本。

server_version.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    int ver = PQserverVersion(conn);

    printf("Server version: %d\n", ver);

    PQfinish(conn);

    return 0;
}

该示例连接到 PostgreSQL 数据库,执行PQserverVersion()函数,打印版本,关闭连接,然后清除。

...
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
...

pg_hba.conf中,我们具有peer默认认证方法。 在这种方法中,数据库用户名必须与操作系统用户名匹配。 无需密码即可建立连接。

PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

使用PQconnectdb()函数,我们可以连接数据库。 在连接字符串中,我们提供用户名和数据库名。

if (PQstatus(conn) == CONNECTION_BAD) {

    fprintf(stderr, "Connection to database failed: %s",
        PQerrorMessage(conn));
    do_exit(conn);
}

PQstatus()函数返回连接状态。 如果连接成功,则返回CONNECTION_OK; 对于不成功的连接,返回CONNECTION_BADPQerrorMessage()返回最近生成的错误消息。

int ver = PQserverVersion(conn);

PQserverVersion()函数返回一个表示 PostgreSQL 数据库版本的整数。 它以连接对象为参数。

PQfinish(conn);

PQfinish()函数关闭与服务器的连接,并释放PGconn对象使用的内存。

$ ./server_version 
Server version: 90309

运行程序,我们获得数据库服务器版本。

密码认证

接下来,我们将使用密码对数据库服务器进行认证。 在本教程的所有其他示例中,我们假设使用peertrust认证模式。 我们将pg_hba.conf文件中本地连接的认证类型更改为md5

$ sudo service postgresql restart

要应用更改,必须重新启动数据库服务器。

password_authentication.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar password=pswd37 dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    char *user = PQuser(conn);
    char *db_name = PQdb(conn);
    char *pswd = PQpass(conn);

    printf("User: %s\n", user);
    printf("Database name: %s\n", db_name);
    printf("Password: %s\n", pswd);

    PQfinish(conn);

    return 0;
}

在示例中,我们使用密码连接到数据库。 我们打印用户名,数据库名称和当前数据库连接的密码。

PGconn *conn = PQconnectdb("user=janbodnar password=pswd37 dbname=testdb");

在连接字符串中,我们添加了密码选项。

char *user = PQuser(conn);

PQuser()函数返回连接的用户名。

char *db_name = PQdb(conn);

PQdb()函数返回连接的数据库名称。

char *pswd = PQpass(conn);

PQpass()函数返回连接的密码。

$ ./password_authentication 
User: janbodnar
Database name: testdb
Password: pswd37

该程序将打印数据库用户,数据库名称和使用的密码。

创建数据库表

在本节中,我们将创建一个数据库表并用数据填充它。

create_table.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn, PGresult *res) {

    fprintf(stderr, "%s\n", PQerrorMessage(conn));    

    PQclear(res);
    PQfinish(conn);    

    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));

        PQfinish(conn);
        exit(1);
    }

    PGresult *res = PQexec(conn, "DROP TABLE IF EXISTS Cars");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);
    }

    PQclear(res);

    res = PQexec(conn, "CREATE TABLE Cars(Id INTEGER PRIMARY KEY," \
        "Name VARCHAR(20), Price INT)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res); 
    }

    PQclear(res);

    res = PQexec(conn, "INSERT INTO Cars VALUES(1,'Audi',52642)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) 
        do_exit(conn, res);     

    PQclear(res);    

    res = PQexec(conn, "INSERT INTO Cars VALUES(2,'Mercedes',57127)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);    

    res = PQexec(conn, "INSERT INTO Cars VALUES(3,'Skoda',9000)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);  

    res = PQexec(conn, "INSERT INTO Cars VALUES(4,'Volvo',29000)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);      

    res = PQexec(conn, "INSERT INTO Cars VALUES(5,'Bentley',350000)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);  

    res = PQexec(conn, "INSERT INTO Cars VALUES(6,'Citroen',21000)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);  

    res = PQexec(conn, "INSERT INTO Cars VALUES(7,'Hummer',41400)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);  

    res = PQexec(conn, "INSERT INTO Cars VALUES(8,'Volkswagen',21600)");

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
        do_exit(conn, res);   
    }

    PQclear(res);  
    PQfinish(conn);

    return 0;
}

创建的表称为Cars,它具有三列:ID,汽车名称及其价格。

PGresult *res = PQexec(conn, "DROP TABLE IF EXISTS Cars");

PQexec()函数将 SQL 命令提交到服务器并等待结果。 PGresult封装了查询结果。 如果表已经存在,我们的 SQL 命令将删除该表。

if (PQresultStatus(res) != PGRES_COMMAND_OK) {
    do_exit(conn, res);
}

应该调用PQresultStatus()函数以检查返回值是否有任何错误。 如果命令已正确执行且未返回数据,则返回PGRES_COMMAND_OK

PQclear(res);

当不再需要每个命令结果时,应使用PQclear()函数将其释放。

$ ./create_table 
$ psql testdb
psql (9.3.9)
Type "help" for help.

testdb=> SELECT * FROM Cars;
 id |    name    | price  
----+------------+--------
  1 | Audi       |  52642
  2 | Mercedes   |  57127
  3 | Skoda      |   9000
  4 | Volvo      |  29000
  5 | Bentley    | 350000
  6 | Citroen    |  21000
  7 | Hummer     |  41400
  8 | Volkswagen |  21600
(8 rows)

我们执行程序,并使用psql工具验证创建的表。

简单查询

在本节中,我们执行一个简单的查询命令。

query_version.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    PGresult *res = PQexec(conn, "SELECT VERSION()");    

    if (PQresultStatus(res) != PGRES_TUPLES_OK) {

        printf("No data retrieved\n");        
        PQclear(res);
        do_exit(conn);
    }    

    printf("%s\n", PQgetvalue(res, 0, 0));

    PQclear(res);
    PQfinish(conn);

    return 0;
}

该示例获取数据库服务器的版本。

PGresult *res = PQexec(conn, "SELECT VERSION()");

SELECT VERSION() SQL 语句检索数据库的版本。

if (PQresultStatus(res) != PGRES_TUPLES_OK) {

    printf("No data retrieved\n");        
    PQclear(res);
    do_exit(conn);
}    

对于通过PQresultStatus()函数返回数据的查询,返回PGRES_TUPLES_OK

printf("%s\n", PQgetvalue(res, 0, 0));

PQgetvalue()函数返回PGresult的一行的单个字段值。

$ ./query_version 
PostgreSQL 9.3.9 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4, 64-bit

程序将打印此输出。

检索多行数据

以下示例执行一个查询,该查询返回多行数据。

multiple_rows.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    PGresult *res = PQexec(conn, "SELECT * FROM Cars LIMIT 5");    

    if (PQresultStatus(res) != PGRES_TUPLES_OK) {

        printf("No data retrieved\n");        
        PQclear(res);
        do_exit(conn);
    }    

    int rows = PQntuples(res);

    for(int i=0; i<rows; i++) {

        printf("%s %s %s\n", PQgetvalue(res, i, 0), 
            PQgetvalue(res, i, 1), PQgetvalue(res, i, 2));
    }    

    PQclear(res);
    PQfinish(conn);

    return 0;
}

该程序将打印Cars表的前五行的数据。

PGresult *res = PQexec(conn, "SELECT * FROM Cars LIMIT 5");

此 SQL 查询返回五行数据。

int rows = PQntuples(res);

PQntuples()返回查询结果中的行数。

for(int i=0; i<rows; i++) {

    printf("%s %s %s\n", PQgetvalue(res, i, 0), 
        PQgetvalue(res, i, 1), PQgetvalue(res, i, 2));
}    

for循环中,使用PQgetvalue()函数获得一行的所有三个字段。

$ ./multiple_rows 
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

这是multiple_rows程序的输出。

预备语句

预备语句可防止 SQL 注入并提高性能。 在使用预备语句时,我们使用占位符,而不是直接将值写入语句中。

prepared_statement.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main(int argc, char *argv[]) {

    const int LEN = 10;
    const char *paramValues[1];

    if (argc != 2) {

        fprintf(stderr, "Usage: prepared_statement rowId\n");
        exit(1);
    }

    int rowId;
    int ret = sscanf(argv[1], "%d", &rowId);

    if (ret != 1) {
        fprintf(stderr, "The argument must be an integer\n");
        exit(1);
    }

    if (rowId < 0) {
        fprintf(stderr, "Error passing a negative rowId\n");
        exit(1);        
    }

    char str[LEN];
    snprintf(str, LEN, "%d", rowId);  
    paramValues[0] = str;  

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    char *stm = "SELECT * FROM Cars WHERE Id=$1";
    PGresult *res = PQexecParams(conn, stm, 1, NULL, paramValues, 
        NULL, NULL, 0);    

    if (PQresultStatus(res) != PGRES_TUPLES_OK) {

        printf("No data retrieved\n");        
        PQclear(res);
        do_exit(conn);
    }    

    printf("%s %s %s\n", PQgetvalue(res, 0, 0), 
        PQgetvalue(res, 0, 1), PQgetvalue(res, 0, 2));    

    PQclear(res);
    PQfinish(conn);

    return 0;
}

该程序将一个行 ID 作为其参数。 它获取指定行的数据并进行打印。 由于该程序从用户那里获取了一个不可信任的值,因此我们必须清除输入数据。 使用PQexecParams()函数创建一个预备语句。

int rowId;
int ret = sscanf(argv[1], "%d", &rowId);

命令行参数存储在rowId变量中。 我们期望一个整数值。

char str[LEN];
snprintf(str, LEN, "%d", rowId);  
paramValues[0] = str;  

该值将转换为字符串,然后传递给字符数组。 paramValuesPQexecParams()函数的参数。

char *stm = "SELECT * FROM Cars WHERE Id=$1";

这是我们的 SQL 语句,它返回Cars表的一行。 $1是一个占位符,稍后会用实际值填充。

PGresult *res = PQexecParams(conn, stm, 1, NULL, paramValues, 
    NULL, NULL, 0);   

PQexecParams()函数创建一个预备语句并执行它。 第二个参数是 SQL 语句。 第三个参数是传递的参数数。 将NULL传递给第四个参数意味着服务器应确定参数类型。 第五个参数是指向包含参数的字符串数组的指针。 接下来的两个参数仅与二进制参数相关。 将 0 传递给最终参数,我们以文本格式获得结果,以二进制格式获得 1。

printf("%s %s %s\n", PQgetvalue(res, 0, 0), 
    PQgetvalue(res, 0, 1), PQgetvalue(res, 0, 2));

我们打印指定行的三个字段。

$ ./prepared_statement 4
4 Volvo 29000

这是示例的输出。

元数据

元数据是有关数据库中数据的信息。 以下内容属于元数据:有关存储数据的表和列的信息,受 SQL 语句影响的行数或结果集中返回的行和列数。

列标题

在第一个示例中,我们打印列标题。

column_headers.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    PGresult *res = PQexec(conn, "SELECT * FROM Cars WHERE Id=0");    

    if (PQresultStatus(res) != PGRES_TUPLES_OK) {

        printf("No data retrieved\n");        
        PQclear(res);
        do_exit(conn);
    }       

    int ncols = PQnfields(res);

    printf("There are %d columns\n", ncols);

    printf("The column names are:\n");

    for (int i=0; i<ncols; i++) {

        char *name = PQfname(res, i);
        printf("%s\n", name);
    }

    PQclear(res);
    PQfinish(conn);

    return 0;
}

该示例将可用列的数量及其名称打印到控制台。

PGresult *res = PQexec(conn, "SELECT * FROM Cars WHERE Id=0");

在 SQL 语句中,我们选择一行的所有列。

int ncols = PQnfields(res);

PQnfields函数返回查询结果行中的列数。

char *name = PQfname(res, i);

PQfname()函数返回与给定列号关联的列名。

$ ./column_headers 
There are 3 columns
The column names are:
id
name
price

这是示例的输出。

表的清单

PostgreSQL 的信息模式由一组视图组成,这些视图包含有关当前数据库中定义的对象的信息。 tables视图包含当前数据库中定义的所有表和视图。

list_tables.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    PGresult *res = PQexec(conn, "SELECT table_name FROM information_schema.tables "
                    "WHERE table_schema = 'public'");    

    if (PQresultStatus(res) != PGRES_TUPLES_OK) {

        printf("No data retrieved\n");        
        PQclear(res);
        do_exit(conn);
    }       

    int rows = PQntuples(res);

    for(int i=0; i<rows; i++) {

        printf("%s\n", PQgetvalue(res, i, 0));
    }        

    PQclear(res);
    PQfinish(conn);

    return 0;
}

该示例打印testdb数据库中的所有表。

PGresult *res = PQexec(conn, "SELECT table_name FROM information_schema.tables "
                "WHERE table_schema = 'public'"); 

该 SQL 语句从当前数据库中选择所有表。

int rows = PQntuples(res);

for(int i=0; i<rows; i++) {

    printf("%s\n", PQgetvalue(res, i, 0));
}    

表将打印到控制台。

$ ./list_tables 
cars
authors
books

list_tables程序在testdb数据库中打印可用表。

事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中的 SQL 语句可以全部提交给数据库,也可以全部回滚。 为了数据安全和完整性,将 SQL 语句放入事务中。

PostgreSQL 在自动提交模式下运行。 每个 SQL 语句都在一个事务中执行:每个单独的语句周围都包含一个隐式BEGIN和(如果成功)COMMIT

显式事务以BEGIN命令开始,以COMMIT命令结束。

transaction.c

#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

void do_exit(PGconn *conn) {

    PQfinish(conn);
    exit(1);
}

int main() {

    PGconn *conn = PQconnectdb("user=janbodnar dbname=testdb");

    if (PQstatus(conn) == CONNECTION_BAD) {

        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
        do_exit(conn);
    }

    PGresult *res = PQexec(conn, "BEGIN");    

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {

        printf("BEGIN command failed\n");        
        PQclear(res);
        do_exit(conn);
    }    

    PQclear(res);   

    res = PQexec(conn, "UPDATE Cars SET Price=23700 WHERE Id=8");    

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {

        printf("UPDATE command failed\n");        
        PQclear(res);
        do_exit(conn);
    }    

    res = PQexec(conn, "INSERT INTO Cars VALUES(9,'Mazda',27770)");    

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {

        printf("INSERT command failed\n");        
        PQclear(res);
        do_exit(conn);
    }       

    res = PQexec(conn, "COMMIT"); 

    if (PQresultStatus(res) != PGRES_COMMAND_OK) {

        printf("COMMIT command failed\n");        
        PQclear(res);
        do_exit(conn);
    }       

    PQclear(res);      
    PQfinish(conn);

    return 0;
}

在示例中,我们更新了汽车的价格并插入了新汽车。 这两个操作包含在单个事务中。 这意味着要么执行两个操作,要么不执行任何操作。

PGresult *res = PQexec(conn, "BEGIN");

使用BEGIN命令开始事务。

res = PQexec(conn, "UPDATE Cars SET Price=23700 WHERE Id=8");

我们使用 ID 8 更新汽车的价格。

res = PQexec(conn, "INSERT INTO Cars VALUES(9,'Mazda',27770)");

将新车插入Cars表。

res = PQexec(conn, "COMMIT");

使用COMMIT命令提交事务。

Tweet

这是 PostgreSQL C API 教程。 您可能也对 ZetCode 上的 PostgreSQL Python 教程PostgreSQL PHP 教程SQLite C 教程MySQL C 教程感兴趣。

PostgreSQL Ruby 教程

原文: http://zetcode.com/db/postgresqlruby/

这是针对 PostgreSQL 数据库的 Ruby 编程教程。 它涵盖了使用 Ruby 语言进行 PostgreSQL 编程的基础。

PostgreSQL

PostgreSQL 是一个功能强大的开源对象关系数据库系统。 它是一个多用户数据库管理系统。 它可以在多个平台上运行,包括 Linux,FreeBSD,Solaris,Microsoft Windows 和 Mac OSX。PostgreSQL 由 PostgreSQL 全球开发小组开发。

PostgreSQL 具有复杂的功能,例如多版本并发控制(MVCC),时间点恢复,表空间,异步复制,嵌套事务(保存点),联机/热备份,复杂的查询计划程序/优化程序以及用于容错的预写日志记录 。 它支持国际字符集,多字节字符编码,Unicode,并且对于排序,区分大小写和格式具有区域设置意识。

Ruby

Ruby 是一种动态的,反射性的,通用的面向对象的编程语言。 原始作者是日本程序员松本行弘。 Ruby 于 1995 年首次出现。Ruby 支持各种编程范例。 这包括面向对象,反射,命令式和反射式编程。

Ruby pg

Ruby pg是一个模块,允许 Ruby 程序与 PostgreSQL 数据库引擎进行交互。 它支持libpq C 库中定义的函数。

安装

我们将安装 PostgreSQL 数据库和其他必要的库。

$ sudo apt-get install postgresql

在基于 Debian 的系统上,我们可以使用上述命令从包中安装 PostgreSQL 数据库。

$ sudo update-rc.d -f postgresql remove
 Removing any system startup links for /etc/init.d/postgresql ...
   /etc/rc0.d/K21postgresql
   /etc/rc1.d/K21postgresql
   /etc/rc2.d/S19postgresql
   /etc/rc3.d/S19postgresql
   /etc/rc4.d/S19postgresql
   /etc/rc5.d/S19postgresql
   /etc/rc6.d/K21postgresql

如果我们从包中安装 PostgreSQL 数据库,它将自动添加到操作系统的启动脚本中。 如果我们仅学习使用数据库,则不必在每次引导系统时都启动数据库。 上面的命令删除 PostgreSQL 数据库的所有系统启动链接。

$ sudo apt-get install libpq-dev

要编译 Ruby pg模块,我们还需要 C libpq库的开发文件。

$ sudo -u postgres psql postgres
psql (9.3.9)
Type "help" for help.

postgres=# \password postgres

我们为postgres用户设置了密码。

$ sudo apt-get install ruby-dev

我们安装了 Ruby 开发库,这是编译 Ruby 扩展模块所必需的。

$ sudo gem install pg 

我们安装了 Ruby pg模块,它是 PostgreSQL 数据库的 Ruby 接口。

启动和停止 PostgreSQL

在下一节中,我们将展示如何启动 PostgreSQL 数据库,停止它以及查询它的状态。

$ sudo service postgresql start
 * Starting PostgreSQL 9.3 database server     [ OK ]

在基于 Debian 的 Linux 上,我们可以使用service postgresql start命令启动服务器。

$ sudo service postgresql status
9.3/main (port 5432): online

我们使用service postgresql status命令检查 PostgreSQL 是否正在运行。

$ sudo service postgresql stop
 * Stopping PostgreSQL 9.3 database server     [ OK ]

我们使用service postgresql stop命令停止 PostgreSQL。

$ service postgresql status
9.3/main (port 5432): down

此时,service postgresql status命令报告 PostgreSQL 数据库已关闭。

创建用户和数据库

在以下步骤中,我们将创建一个新的数据库用户和数据库。

$ sudo -u postgres createuser janbodnar

我们在 PostgreSQL 系统中创建了一个新角色。 我们允许它具有创建新数据库的能力。 角色是数据库世界中的用户。 角色与操作系统用户是分开的。

$ sudo -u postgres psql postgres
psql (9.3.9)
Type "help" for help.

postgres=# ALTER USER janbodnar WITH password 'pswd37';
ALTER ROLE
postgres=# \q

使用psql命令,为新用户添加密码。

PostgreSQL 通常在本地连接上使用信任或对等认证策略。 在信任​​认证策略的情况下,PostgreSQL 假定可以连接到服务器的任何人都可以使用他们指定的任何数据库用户名(甚至超级用户名)访问数据库。 与数据库建立连接时,不需要密码。 (在数据库和用户列中所做的限制仍然适用。)信任认证对于单用户工作站上的本地连接是适当的,并且非常方便。 它通常不适用于多用户计算机。 如果使用对等认证策略,则数据库用户名必须与操作系统用户名匹配。

$ sudo -u postgres createdb testdb --owner janbodnar

使用createdb命令,我们创建了一个名为testdb的新数据库。 它的所有者是新的数据库用户。

libpq 库

libpq库是 PostgreSQL 的 C 接口。 它是一组库函数,允许客户端程序与 PostgreSQL 交互。 它也是其他 PostgreSQL 应用接口的基础引擎,包括为 C++ ,Perl,PHP,Ruby,Python 和 Tcl 编写的接口。

Ruby pg模块是libpg库的包装。

lib_version.rb

#!/usr/bin/ruby

require 'pg'

puts 'Version of libpg: ' + PG.library_version.to_s

该程序将打印libpq库的版本。

require 'pg'

我们包括pg模块。

puts 'Version of libpg: ' + PG.library_version.to_s

library_version方法返回正在使用的libpq的版本。

$ ./lib_version.rb 
Version of libpg: 90309

该库的版本是 9.3.9。

服务器版本

在下面的示例中,我们找到了 PostgreSQL 数据库的版本。

server_version.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'
    puts con.server_version

rescue PG::Error => e

    puts e.message 

ensure

    con.close if con

end

该示例连接到 PostgreSQL 数据库,执行server_version方法,打印版本,关闭连接,然后清除。

...
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
...

pg_hba.conf中,我们具有peer默认认证方法。 在这种方法中,数据库用户名必须与操作系统用户名匹配。 无需密码即可建立连接。

con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

使用connect方法,我们连接到数据库。 在连接字符串中,我们提供用户名和数据库名。

rescue PG::Error => e

    puts e.message 

我们检查错误。 这很重要,因为使用数据库容易出错。

ensure

    con.close if con

end

最后,我们释放资源。

$ ./server_version.rb 
90309

运行程序,我们获得数据库服务器版本。

密码认证

接下来,我们将使用密码对数据库服务器进行认证。 在本教程的所有其他示例中,我们假设使用peertrust认证模式。 我们将pg_hba.conf文件中本地连接的认证类型更改为md5

$ sudo service postgresql restart

要应用更改,必须重新启动数据库服务器。

password_authentication.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar', 
        :password => 'pswd37'

    user = con.user
    db_name = con.db
    pswd = con.pass

    puts "User: #{user}"
    puts "Database name: #{db_name}"
    puts "Password: #{pswd}" 

rescue PG::Error => e

    puts e.message 

ensure

    con.close if con

end

在示例中,我们使用密码连接到数据库。 我们打印用户名,数据库名称和当前数据库连接的密码。

con = PG.connect :dbname => 'testdb', :user => 'janbodnar', 
    :password => 'pswd37'

在连接字符串中,我们添加了密码选项。

user = con.user

user方法返回连接的用户名。

db_name = con.db

db方法返回连接的数据库名称。

pswd = con.pass

pass方法返回连接的密码。

$ ./password_authentication.rb 
User: janbodnar
Database name: testdb
Password: pswd37

该程序将打印数据库用户,数据库名称和使用的密码。

创建数据库表

在本节中,我们将创建一个数据库表并用数据填充它。

create_table.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    con.exec "DROP TABLE IF EXISTS Cars"
    con.exec "CREATE TABLE Cars(Id INTEGER PRIMARY KEY, 
        Name VARCHAR(20), Price INT)"
    con.exec "INSERT INTO Cars VALUES(1,'Audi',52642)"
    con.exec "INSERT INTO Cars VALUES(2,'Mercedes',57127)"
    con.exec "INSERT INTO Cars VALUES(3,'Skoda',9000)"
    con.exec "INSERT INTO Cars VALUES(4,'Volvo',29000)"
    con.exec "INSERT INTO Cars VALUES(5,'Bentley',350000)"
    con.exec "INSERT INTO Cars VALUES(6,'Citroen',21000)"
    con.exec "INSERT INTO Cars VALUES(7,'Hummer',41400)"
    con.exec "INSERT INTO Cars VALUES(8,'Volkswagen',21600)"

rescue PG::Error => e

    puts e.message 

ensure

    con.close if con

end

创建的表称为Cars,它具有三列:ID,汽车名称及其价格。

con.exec "DROP TABLE IF EXISTS Cars"

exec方法将 SQL 命令提交到服务器并等待结果。 如果表已经存在,我们的 SQL 命令将删除该表。

$ ./create_table.rb
$ psql testdb
psql (9.3.9)
Type "help" for help.

testdb=> SELECT * FROM Cars;
 id |    name    | price  
----+------------+--------
  1 | Audi       |  52642
  2 | Mercedes   |  57127
  3 | Skoda      |   9000
  4 | Volvo      |  29000
  5 | Bentley    | 350000
  6 | Citroen    |  21000
  7 | Hummer     |  41400
  8 | Volkswagen |  21600
(8 rows)

我们执行程序,并使用psql工具验证创建的表。

简单查询

在本节中,我们执行一个简单的查询命令。

query_version.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    rs = con.exec 'SELECT VERSION()'
    puts rs.getvalue 0, 0

rescue PG::Error => e

    puts e.message 

ensure

    con.close if con

end

该示例获取数据库服务器的版本。

rs = con.exec 'SELECT VERSION()'

SELECT VERSION() SQL 语句检索数据库的版本。

puts rs.getvalue 0, 0

getvalue方法返回返回结果集的一行的单个字段值。

$ ./query_version.rb 
PostgreSQL 9.3.9 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4, 64-bit

程序将打印此输出。

检索多行数据

以下示例执行一个查询,该查询返回多行数据。

multiple_rows.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    rs = con.exec "SELECT * FROM Cars LIMIT 5"

    rs.each do |row|
      puts "%s %s %s" % [ row['id'], row['name'], row['price'] ]
    end

rescue PG::Error => e

    puts e.message 

ensure

    rs.clear if rs
    con.close if con

end

该程序将打印Cars表的前五行的数据。

rs = con.exec "SELECT * FROM Cars LIMIT 5"

此 SQL 查询返回五行数据。

rs.each do |row|
    puts "%s %s %s" % [ row['id'], row['name'], row['price'] ]
end

使用each方法,我们遍历结果集并打印一行的杂项。

$ ./multiple_rows.rb 
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000

这是multiple_rows.rb程序的输出。

预备语句

预备语句可防止 SQL 注入并提高性能。 在使用预备语句时,我们使用占位符,而不是直接将值写入语句中。

prepared_statement.rb

#!/usr/bin/ruby

require 'pg'

if ARGV.length != 1 then
    puts "Usage: prepared_statement.rb rowId"
    exit
end

rowId = ARGV[0]

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    con.prepare 'stm1', "SELECT * FROM Cars WHERE Id=$1"
    rs = con.exec_prepared 'stm1', [rowId]

    puts rs.values 

rescue PG::Error => e

    puts e.message 

ensure

    rs.clear if rs
    con.close if con

end

该程序将一个行 ID 作为其参数。 它获取指定行的数据并进行打印。 由于程序从用户那里获取了一个不可信任的值,因此有必要使用预备语句。

rowId = ARGV[0]

命令行参数存储在rowId变量中。

con.prepare 'stm1', "SELECT * FROM Cars WHERE Id=$1"

prepare方法准备一个具有给定名称的 SQL 语句,以便稍后执行。 我们的 SQL 语句返回Cars表的一行。 $1是一个占位符,稍后会填充一个实际值。

rs = con.exec_prepared 'stm1', [rowId]

exec_prepared方法执行由语句名称指定的预备命名语句。 第二个参数是 SQL 查询的绑定参数数组。

puts rs.values 

values方法打印行的字段值。

$ ./prepared_statement.rb 4
4
Volvo
29000

这是示例的输出。

以下示例显示了另一种创建预备语句的方法。

prepared_statement2.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    stm = "SELECT $1::int AS a, $2::int AS b, $3::int AS c"
    rs = con.exec_params(stm, [1, 2, 3])

    puts rs.values 

rescue PG::Error => e

    puts e.message 

ensure

    rs.clear if rs
    con.close if con

end

该示例使用exec_params创建并执行预备语句。

stm = "SELECT $1::int AS a, $2::int AS b, $3::int AS c"

在语句中,我们将预期参数的数据类型附加到占位符。

rs = con.exec_params(stm, [1, 2, 3])

exec_params方法使用占位符作为参数将 SQL 查询请求发送到数据库。

$ ./prepared_statement2.rb 
1
2
3

这是示例的输出。

元数据

元数据是有关数据库中数据的信息。 以下内容属于元数据:有关存储数据的表和列的信息,受 SQL 语句影响的行数或结果集中返回的行和列数。

列标题

在第一个示例中,我们打印列标题。

column_headers.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    rs = con.exec 'SELECT * FROM Cars WHERE Id=0'
    puts 'There are %d columns ' % rs.nfields
    puts 'The column names are:'
    puts rs.fields

rescue PG::Error => e

    puts e.message 

ensure

    rs.clear if rs
    con.close if con

end

该示例将可用列的数量及其名称打印到控制台。

rs = con.exec 'SELECT * FROM Cars WHERE Id=0'

在 SQL 语句中,我们选择一行的所有列。

puts "There are %d columns " % rs.nfields

nfields方法返回查询结果行中的列数。

puts rs.fields

fields方法返回一个字符串数组,该字符串表示结果中字段的名称。

$ ./column_headers.rb 
There are 3 columns 
The column names are:
id
name
price

这是示例的输出。

表的清单

PostgreSQL 的信息模式由一组视图组成,这些视图包含有关当前数据库中定义的对象的信息。 tables视图包含当前数据库中定义的所有表和视图。

list_tables.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    rs = con.exec "SELECT table_name FROM information_schema.tables 
        WHERE table_schema = 'public'"

    rs.each do |row|
        puts row['table_name']
    end

rescue PG::Error => e

    puts e.message 

ensure

    rs.clear if rs
    con.close if con

end

该示例打印testdb数据库中的所有表。

rs = con.exec "SELECT table_name FROM information_schema.tables 
    WHERE table_schema = 'public'"

该 SQL 语句从当前数据库中选择所有表。

rs.each do |row|
    puts row['table_name']
end

表将打印到控制台。

$ ./list_tables.rb 
authors
books
cars

list_tables.rb程序在testdb数据库中打印可用表。

事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中的 SQL 语句可以全部提交给数据库,也可以全部回滚。 为了数据安全和完整性,将 SQL 语句放入事务中。

PostgreSQL 在自动提交模式下运行。 每个 SQL 语句都在一个事务中执行:每个单独的语句周围都包含一个隐式BEGIN和(如果成功)COMMIT

显式事务以BEGIN命令开始,以COMMITROLLBACK命令结束。

transaction.rb

#!/usr/bin/ruby

require 'pg'

begin

    con = PG.connect :dbname => 'testdb', :user => 'janbodnar'

    con.transaction do |con|

        con.exec "UPDATE Cars SET Price=23700 WHERE Id=8"
        con.exec "INSERT INTO Car VALUES(9,'Mazda',27770)"

    end

rescue PG::Error => e

    puts e.message 

ensure

    con.close if con

end

在示例中,我们更新了汽车的价格并插入了新汽车。 这两个操作包含在单个事务中。 这意味着要么执行两个操作,要么不执行任何操作。

con.transaction do |con|

    con.exec "UPDATE Cars SET Price=23700 WHERE Id=8"
    con.exec "INSERT INTO Car VALUES(9,'Mazda',27770)"

end

transaction方法在单个事务中在块内运行代码。 它在块的开头执行BEGIN,在块的末尾执行COMMIT,如果发生任何异常,则执行ROLLBACK

Tweet

这是 PostgreSQL Ruby 教程。 您可能也对 SQLite Ruby 教程MySQL Ruby 教程PostgreSQL Python 教程MongoDB Ruby 教程感兴趣。 或 ZetCode 上的 PostgreSQL PHP 教程

posted @ 2024-10-24 18:19  绝不原创的飞龙  阅读(1)  评论(0编辑  收藏  举报