SQLite 入门教程三 好多封锁 Constraints

发布时间:2018-11-15  栏目:sqlite  评论:0 Comments

一、约束 Constraints

原文:SQLite Tutorial: Getting Started

每当上同一首随笔的尾声,我关系了束缚,
但是在那里我将她译成了限定符,不太可靠,这里先还凑巧一下,应该翻译成束缚更合适一点。
那么什么是约为?

留神:在2016-04-06已更新至 Xcode 7.3 iOS 9.3Swift 2.2

我们于数据库中存储数据的时,有局部数来鲜明的约束原则。
比如一所学关于教师的数据表,其中的字段列可能发如下约束:

于软件开发的社会风气中,在您用针对你的 app
进行多少持久化前开其并无用花费很多时间。在很多情形下,这是以数据结构的款式出现的,但若怎么样更有效地蕴藏它们?

年龄 – 至少逾20年份。如果您想录入一个低于20年度的园丁,系统会报错
国籍 – 默认中国。所谓默认,就是如果您不填,系统自动填写上默认值
姓名 – 不能够吧空。每个人都起名字嘛
员工号 – 唯一。这个可不能乱,工资发错了便烦了
地方提到的超、默认、不能够啊空、唯一等等,就是多少的羁绊原则。 我们以就此
CREATE TABLE
创建表的时候,就应该以每个字段列的封锁原则先说明(如果有的话),
以后重为表里输入数据的当儿,系统会活动吗我们检查是否满足约束原则,如果无满足系统会报错。

侥幸的凡,一些壮烈之先行者已经支付了关于访问存储于数据库中之结构化数据和描写副语言的功能的化解方案。这个
SQLite 教程向而出示了哪快速使 Swift 在风行平台工作直达行使
SQLite 数据库,在
iOS 平台 SQLite 是默认可用的。如果你熟悉 Core Data
的说话,你便会了解 SQLite 一些和 Core Data
类似的最好广大持久化对象图的方。

SQLite 常用约束如下

每当本SQLite的教程被,您将学及以下几栽什么执行数据库的操作:

NOT NULL – 非空
UNIQUE – 唯一
PRIMARY KEY – 主键
FOREIGN KEY – 外键
CHECK – 条件检查
DEFAULT – 默认

  • 创造和连续一个数据库
  • 缔造一个表
  • 插入行
  • 更新行
  • 删除行
  • 查询数据库
  • 处理 SQLite 错误

二、主键 PRIMARY KEY

在模仿了这些骨干的怎么样履行操作后,你会盼什么用同一种植更加 Swift
的法门将它们包装起来。它见面支援在你的应用中描绘越空虚 APIs
,这样的话就可大大方方的免直接面对 SQLite 中的C语言的 APIs

咱要上 SQLite 命令行环境,建立一个 test.db 数据库用来开实验,如下

最终,我以简单介绍流行的开源资源 Swift 封装的
SQLite.swift
的最底层框架内是什么样行事之。

复制代码 代码如下:

Note:
数据库,甚至是SQLite自身,是巨大的主题,所以它的好多情节超出了仍课程的限。本文假定你于关系数据库的思来核心的问询,你根本是来学习怎样构成Swift来利用SQLite。

myqiao@ubuntu:~/My Documents/db$ sqlite3 test.db
— Loading resources from /home/myqiao/.sqliterc
SQLite version 3.7.4
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite> .tables
sqlite> 

开始

下载者 SQLite 教程的
启航项目
,打开 SQLiteTutorial.xcworkspace。根据项目的导航栏打开
Tutorial.playground。你会马上注意到 import SQLite
那行有一个错误信息,你足足要编译项目雷同潮,可以用 Command + B
。这是盖急需工作空间的安排信息来创造一个模块,这样SQLite才方可运作在
playground

Note:
SQLite是无科学的模块,你不能够以你的路面临调用导入SQLite模块,不要受
import SQLite 这行误导;相反,你得用一个桥接的脑瓜儿。

playground 打开的时节,设置它呢手动运行,而无是电动运行:

图片 1

sqliteswift_manually_run_payground.png

当下将推保险按照你想那么运行SQL命令。

于这页面的顶部而啊得看来一个 destroyPart1Database()
函数的调用;你可高枕无忧地忽视这个,由于每次运行 playground
时,数据库文件会吃毁掉。这将保证您于斯SQLite教程所有语句成功实施。

你的 playground
的文件系统中,在用的地方写SQLite数据库文件。可以于终端中运作下面的指令,为汝的
playground 创建数量目录:

mkdir -p ~/Documents/Shared\ Playground\ Data/SQLiteTutorial

            运行 .tables
命令没有回到,说明数据库是拖欠的。如果您的数据库里有内容连影响及脚的尝试,
你可以用我们上等同篇学的 DROP TABLE 来删除造成影响的表明, 或者用 ALTER
TABLE … RENAME TO … 来改名。

自己怎么要选SQLite?

对的,在iOS中 SQLite 不是绝无仅有保存数据的路。除了 Core
Data
,还有好多数目持久化的选取,包括
Realm , Couchbase
Lite
, Firebase 和
NSCoding 。

每一个还来自己的亮点和瑕疵,包括 SQLite
自身。在没数量持久层,作为开发者,基于你的应用要求来控制择哪位选项。

SQLite 确实有一定之优势:

  • 每当iOS的应用程序包没有长额外的开销
  • 品味和测试;第一个本子在2000年8月发表
  • 开源
  • 对于数据库开发人员和领队熟悉的询问语言
  • 跨平台

SQLite 的上下可以说坏直观和明显,所以这些我们见面留让您自己研究!


C的API

当下有的底 SQLite 教程通过极端常用和主导的 SQLite
的API来导而。你会迅速发现及用 Swift
的法门来包装C语言的API是只大好之想法,但是以下来,我们先行经C语言去落实;你将以本教程的第二部分举行有
SQLite 的包装。

下面说归正转,我们来说说主键 PRIMARY KEY 。

打开一个连连

以您开另外业务之前,你都要事先创造一个数据库的连年。

在开始有之 playground 上补偿加如下方法:

func openDatabase() -> COpaquePointer {
    var db: COpaquePointer = nil
    if sqlite3_open(part1DbPath, &db) == SQLITE_OK {
        print("Successfully opened connection to database at \  (part1DbPath)")
        return db
    } else {
        print("Unable to open database. Verify that you created the directory described " + "in the Getting Started section.")
        XCPlaygroundPage.currentPage.finishExecution()
    }
}

方的点子调用 sqlite3_open()
来打开或创造一个新的数据库文件,如果成功,将回来一个 COpaquePointer
;它是一个 Swift
类型,以为C指针不能够一直在Swift中意味着。当你调用这个主意,您将会见捕获到回的指针,用于和数据库交互。

无数之SQLite函数返回一个 Int32
结果,大多数这些代码被定义也于SQLite库常数,例如, SQLITE_OK
代表结果代码0。你可以SQLite的网站上找到一个有关不同结果代码的列表。

失掉开辟数据库,在你的 playground 上补偿加以下行:

let db = openDatabase()

按下 Play
按钮运行playground,观察控制高出口。如果控制台没有打开,按下左边的按钮来运作:

图片 2

如果 openDatabase() 返回成功,你晤面相以下输出内容:

Successfully opened connection to database at /Users/username/
Documents/Shared Playground Data/SQLiteTutorial/Part1.sqlite

username 是你的 Home 目录。

率先,数据表中各一样长长的记下还发一个主键,
这即比如咱各级的身份证号码、员工号、银行帐号;
反过来为堪说,每一个主键对承诺着同漫漫数记录。
所以,主键必须是绝无仅有的。
下,一般景象下主键同时也是一个目录,所以经过主键查找记录速度比较快。
其三,在关系项目数据库被,一个说明的主键可以看成另外一个表明底外键,
这样,这片单说明中就经过者键建立了干。
末段,主键一般是整数或者字符串,只要保证唯一就实施。 在 SQLite
中,主键如果是整数类型,该列的值好自行增长。

创建表

现你已连续上数据库文件了,你可创建同张表。您将祭一个非常简单的表来存储联系人。

脚这张表由少独列成, id 的之数据类型是 INT 是表的主键, Name
的数据类型是 CHAR(255) .

图片 3

sqliteswift_table_diagram.png

加上脚的字符串,是创造表必要之SQL语句:

let createTableString = "CREATE TABLE Contact(" + "Id INT PRIMARY KEY NOT NULL," + "Name CHAR(255));"

下同样步,添加此方法来执行创建表的SQL语句:

func createTable() {
    // 1
    var createTableStatement: COpaquePointer = nil
    // 2
    if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
        // 3
        if sqlite3_step(createTableStatement) == SQLITE_DONE {
            print("Contact table created.")
        } else {
            print("Contact table could not be created.")
        }
    } else {
        print("CREATE TABLE statement could not be prepared.")
    }
    // 4
    sqlite3_finalize(createTableStatement)
}

平等步一步来诠释:

  1. 首先,你创造一个指南针便于第二步之援。

  2. sqlite3_prepare_v2() 函数将 SQLite
    语句编译成字节码并返回一个状态码,在推行任意数据库语句前即是生重大之平等步。如果您针对是感兴趣,你可以于这里按照查看更多,通过检查返回的状态码来确保编译成功。如果是这样的话,这个历程将走到步骤3;否则,控制台会打印一长长的告句无法编译的消息。

  3. sqlite3_step()
    运行已编译的语句,在这种气象下,只有当有一个结出时,才实施同样不行。之后于这
    SQLite 的科目它是必不可少之手续,你见面频看到它的扬言。

  4. 君必须连续调用 sqlite3_finalize()
    编译语词删除其,避免资源泄漏。一旦一个讲话已经完成,你尽管不应更利用她了。

如今,你得加上下面的措施以 playground 中调用:

createTable()

运行 playground ,你见面在控制台上看下面的音讯出现:

Contact table created

兹你的数据库里发相同摆设表了,是岁月为里面加加些数据了。添加一行数,
id1NameRay


安插一些数据

长下的SQL语句到你的 playground 的底部:

let insertStatementString = "INSERT INFO Contact (Id, Name) VALUES (?, ?);"

如果你莫最好多开SQL的更或者看起有几许意外。为什么值是问号也?

尚记得上面用的 sqlite3_prepare_v2() 编译的句子?这个 ?
的语法是报编译器,当实际施行语句时,将提供真正的价。

眼看是产生性上的考虑,让您提前编译语句,这或者是一个特性增益,因为编译是一个高昂的操作。编译后的语句可以重新行使不同之价值。

属下去,创建下面的措施以公的 playground

func insert() {
    var insertStatement: COpaquePointer = nil

    // 1
    if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
        let id: Int32 = 1
        let name: NSString = "Ray"

        // 2
        sqlite3_bind_int(insertStatement, 1, id)
        // 3
        sqlite3_bind_text(insertStatement, 2, name.UTF8String, -1, nil)

        // 4
        if sqlite3_step(insertStatement) == SQLITE_DONE {
            print("Successfully inserted row.")
        } else {
            print("Could not insert row.")
        }
    } else {
        print("INSERT staremnet could not be prepared.")
    }
    // 5
    sqlite3_finalize(insertStatement)
}

下介绍者的不二法门是如何做事的:

  1. 首先,编写SQL语句并说明是否是;
  2. ? 占位符得地方定义值。函数的讳 sqlite3_bind_int()
    意味着你绑定一个 int
    值的宣示。函数的首先个参数是绑定到的讲话,而第二只参数是一个非零的目录用于绑定
    ?
    的职。第三只与最终一个参数是价值我。这个绑定调用返回一个态代码,但现行公要它成功了的;
  3. 履行同样的绑定过程,但是及时等同不成是一个文本值。在是调用里发生些许只附加的参数;在这SQLite的课里你可以省略而为
    -1nil
    。如果你发趣味,你可于这里读书更多关于绑定参数;
  4. 使用 sqlite3_step() 函数执行之口舌和认证就;
  5. 还,最后确定声明。如果你而插入多个挂钩人,您或许会见保留该语句,并以不同的价值更使用其。

下一步,在 playground 中调用下面是新增长的方:

insert()

运作而的 playground 和验证您看您于控制高出口:

Successfully inserted row.

下我们来开实验

挑战:多修插入

挑战时!你的任务是更新 insert() 函数要其可插入一组联系人。

当一个提拔,你得重编排 SQL 语句,用 sqlite3_reset()
返回到它的上马状态又实行同样遍。

复制代码 代码如下:

化解方案:插入多行
func insert() {
    var insertStatement: COpauePointer = nil
    // 1
    let names: [NSString] = ["Ray", "Chirs", "Martha", "Danielle"]

    if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
        // 2
        for (index, name) in names.enumerate() {
            // 3
            let id = Int32(index + 1)
            sqlite3_bind_int(insertStatement, 1, id)
            sqlite3_bind_text(insertStatement, 2, name.UTF8String, -1, nil)
            if sqlite3_step(insertStatement) == SQLITE_DONE {
                print("Successfully inserted row.")
            } else {
                print("Could not insert row.")
            }
            // 4
            sqlite3_reset(insertStatement)
        }
        sqlite3_finalize(insertStatement)
    } else {
        print("INSERT statement could not be prepared.")
    }
}

sqlite> 
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) Values(‘张三’);
sqlite> INSERT INTO Teachers(Name) Values(‘李四’);
sqlite> INSERT INTO Teachers(Name) Values(‘王二麻子’);
sqlite> SELECT * FROM Teachers;
Id          Name     
———-  ———-
1           张三   
2           李四   
3           王二麻
sqlite> INSERT INTO Teachers(Id,Name) Values(2,’孙悟空’);
Error: PRIMARY KEY must be unique
sqlite> 

      

咱先行新建了一个 Teachers 表,并设置了少于个字段列,其中 Id
字段列为主键列。 然后,我们通往中插入三久数并询问,反馈一切正常。

只顾:在插入前三长数的时光,命令中连没有强烈指明 Id
的价值,系统自动赋值,并且数值自动增长。

栽第四长长的数的时刻,我深受了一个明了的 Id 编号为 2,因为李四的编号都是
2 了, 所以系统提示我错:主键必须唯一。

三、默认值 DEFAULT

有局部专程之字段列,在每一样长长的记下受,他的价基本上都是均等的。只是在个别情况下才改吗别的值,这样的字段列我们好叫他若一个默认值。

下我们来举行试验

复制代码 代码如下:

sqlite> 
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name
text,Country text DEFAULT ‘中国’);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) Values(‘张三’);
sqlite> INSERT INTO Teachers(Name) Values(‘李四’);
sqlite> INSERT INTO Teachers(Name) Values(‘王二麻子’);
sqlite> INSERT INTO Teachers(Name,Country) Values(‘孙悟空’,’天庭’);
sqlite> SELECT * FROM Teachers;
Id    Name             Country       
—-  —————  —————
1     张三           中国        
2     李四           中国        
3     王二麻子     中国        
4     孙悟空        天庭
sqlite>

            先将之前的 Teachers 表删除,然后再次创设。这拨 Teachers
表多矣一个 Country 字段, 并且设置默认值为“中国”,然后我们插入四条数据及
Teachers 表。

前方三长达数据都未曾明白指明 Country
字段的价,只有第四长条数据指明了“孙悟空”的 Country 为“天庭”。

询问数据,发现前面三漫漫数还填上了默认值,实验成功。


数量展示小走样,命令 .width 4 15 15 设置的列宽,可以经过 .show 查看,
可能是以中文的来头,所以并未指向共同。

**四、非空 NOT NULL

**有部分字段我们也许一时不知到拖欠填些什么,同时其也远非设定默认值,
当添加数量经常,我们将这样的字段空着无填,系统认为他是 NULL 值。

但是还有另外一近乎字段,必须为填上数据,如果未填,系统便见面报错。
这样的字段被称 NOT NULL 非空字段,需要以定义表的早晚先声明。

下我们来举行试验

复制代码 代码如下:

sqlite> 
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text,Age
integer NOT NULL,City text);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name,Age) Values(‘Alice’,23);
sqlite> INSERT INTO Teachers(Name,Age) Values(‘Bob’,29);
sqlite> INSERT INTO Teachers(id,Name,Age) Values(6,’Jhon’,36);
sqlite> SELECT * FROM Teachers;
Id    Name             Age              City          
—-  —————  —————  —————
1     Alice            23               NULL          
2     Bob              29               NULL          
6     Jhon             36               NULL          
sqlite> INSERT INTO Teachers(Name) Values(‘Mary’);
Error: Teachers.Age may not be NULL
sqlite>

            还是事先去旧表,创建新表。

即时拨 Teachers 表声明了一个 NOT NULL 字段 Age,同时还发生一个好吗 NULL
的字段 City

插入前三久数还未曾点名 City 的价值,查询好见到 City 字段全部乎空

注意:这里的 NULL 只是针对“什么都并未”的均等栽显示形式, 可以透过 .nullvalue
命令改呢别的形式,具体见第一首

安插第四久数据时尚未点名 Age 的价值,系统就是报错了: Teachers.Age 不克为空

五、 唯一 UNIQUE 即时等同束缚非常好明,除了主列以为,还有一些列也非可知闹还值。不多说,直接看代码

复制代码 代码如下:

sqlite> 
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text
UNIQUE);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) VALUES(‘Alice’);
sqlite> INSERT INTO Teachers(Name) VALUES(‘Bob’);
sqlite> INSERT INTO Teachers(Name) VALUES(‘Jane’);
sqlite> INSERT INTO Teachers(Name) VALUES(‘Bob’);
Error: column Name is not unique
sqlite>

            这次的 Teachers 表只有 Name 这无异排,但是 Name
列不可知产生双重值。可以见到,到我们第二糟栽 Bob 时,系统即报错了。

六、 条件检查 CHECK

或多或少价值必须符合自然之口径才允存入,这是就是需要因此到这个 CHECK 约束。

复制代码 代码如下:

sqlite> 
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Age integer
CHECK(Age>22));
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Age) VALUES(45);
sqlite> INSERT INTO Teachers(Age) VALUES(33);
sqlite> INSERT INTO Teachers(Age) VALUES(23);
sqlite> INSERT INTO Teachers(Age) VALUES(21);
Error: constraint failed
sqlite>

            Age 字段要求必须过 22,当插入的多寡低于22时,系统报错。

七、外键 FOREIGN KEY

今昔,我们的数据库中已来 Teachers 表了,假如我们重起一个 Students
表, 要求 Students 表中之每一个学童还对应一个 Teachers 表中的师长。

挺简单,只需要以 Students 表中确立一个 TeacherId 字段,保存对应教师的 Id
号, 这样,学生以及师资间便建了关联。


问题是:我们来或于学生存入一个勿在 Teachers 表中之 TeacherId 值,
而且发现无了之似是而非。

这种景象下,可以把 Students 表中 TeacherId 字段声明也一个外键,
让她的价值对许到 Teachers 表中的 Id 字段上。

这般,一旦在 Students 表中存入一个休有的教员 Id ,系统就会见报错。

复制代码 代码如下:

sqlite>
sqlite> .tables
Teachers
sqlite> CREATE TABLE Students (Id integer PRIMARY KEY,  TeacherId
integer,  FOREIGN KEY(TeacherId) REFERENCES Teachers(id) );
sqlite> .tables
Students  Teachers
sqlite> SELECT * FROM Teachers;
Id    Age           
—-  —————
1     40            
2     33            
3     23
sqlite> INSERT INTO Students(TeacherId) VALUES(1);
sqlite> INSERT INTO Students(TeacherId) VALUES(3);
sqlite> INSERT INTO Students(TeacherId) VALUES(9);
sqlite> SELECT * FROM Students;
Id    TeacherId     
—-  —————
1     1             
2     3             
3     9
sqlite>

这里建立了 Students 表,并且将 TeacherId 作为外键与 Teachers 表的 Id
列相呼应。

题目来了:插入的前面片久数据尚未问题,因为 Id 编号 1、3 都当 Teachers 表中;
但是数字 9 并无以 Teachers
表中,不但没报错,系统还保留进去了,这是怎吧?

传言 SQLite
的外键约束默认情况下并无是打开之,如果您待是效应,你恐怕用下载源代码版本,设置每个编译参数,然后再次编译,这样你便获取支持外键的
SQLite 了。

你或许感兴趣的篇章:

  • 叫Sqlite脱离VC++
    Runtime独立运作的法门
  • C++操作SQLite简明教程
  • Sqlite
    常用函数封装提高Codeeer的频率
  • C#遭采用SQLite数据库的法介绍
  • ASP.NET(C#)中操作SQLite数据库实例
  • VC++基于Dx实现之截图程序示例代码
  • VC++实现出口GIF到窗体并显示GIF动画的艺术
  • VC++开发被圆满解决头文件相互包含问题的方法分析
  • 解析VC++中之条文件包含问题
  • VC++操作SQLite简单实例

留下评论

网站地图xml地图