在 Swift 中构建 SQL 查询。可扩展的、基于协议的设计,支持 DQL、DML 和 DDL。
使用标准的 SwiftPM 语法将 SQLKit 作为依赖项包含在您的 Package.swift
文件中。
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0")
SQLKit 3.x 需要 SwiftNIO 2.x 或更高版本。之前的重大版本不再受支持。
SQLKit 支持以下平台
SQLKit 是一个用于在 Swift 中构建和序列化 SQL 查询的 API。SQLKit 尝试在可能的情况下抽象化 SQL 方言的不一致性,允许您编写可以在多种数据库风格上运行的查询。在无法进行抽象化的地方,SQLKit 提供了强大的 API 用于自定义或动态行为。
这些数据库包是 SQLKit 的驱动程序
SQLKit 本身不处理创建或管理数据库连接。此包完全专注于构建和序列化 SQL 查询。要连接到您的 SQL 数据库,请参考您特定数据库包的文档。一旦您连接到数据库并拥有 SQLDatabase
的实例,您就可以继续了。
SQLDatabase
的实例能够序列化和执行 SQLExpression
。
let db: any SQLDatabase = ...
db.execute(sql: any SQLExpression, onRow: (any SQLRow) -> ())
SQLExpression
是一个协议,表示 SQL 查询字符串和可选的绑定值。它可以表示完整的 SQL 查询或仅是片段。
SQLKit 为常见的查询提供 SQLExpression
,例如 SELECT
、UPDATE
、INSERT
、DELETE
、CREATE TABLE
以及更多。
var select = SQLSelect()
select.columns = [...]
select.tables = [...]
select.predicate = ...
SQLDatabase
可以用于为大多数这些查询类型创建流畅的查询构建器。
struct Planet: Codable { var id: Int, name: String }
let db: some SQLDatabase = ...
try await db.create(table: "planets")
.column("id", type: .int, .primaryKey(autoIncrement: true), .notNull)
.column("name", type: .string, .notNull)
.run()
try await db.insert(into: "planets")
.columns("id", "name")
.values(SQLLiteral.default, SQLBind("Earth"))
.values(SQLLiteral.default, SQLBind("Mars"))
.run()
let planets = try await db.select()
.columns("id", "name")
.from("planets")
.all(decoding: Planet.self)
print(planets) // [Planet(id: 1, name: "Earth"), Planet(id: 2, name: "Mars")]
您可以通过调用 run()
来执行查询构建器。
对于支持返回结果的查询构建器(例如,任何符合 SQLQueryFetcher
协议的构建器),还有其他方法用于处理数据库输出
all()
:返回行的数组。first()
:返回一个可选的行。run(_:)
:接受一个闭包,该闭包处理返回的行。这些方法中的每一个都返回 SQLRow
,它具有访问列值的方法。
let row: any SQLRow
let name = try row.decode(column: "name", as: String.self)
print(name) // String
SQLRow
还支持直接从行解码 Codable
模型。
struct Planet: Codable {
var name: String
}
let planet = try row.decode(model: Planet.self)
支持返回结果的查询构建器具有方便的方法,可以自动解码模型。
let planets: [Planet] = try await db.select()
...
.all(decoding: Planet.self)
SQLDatabase.select()
方法创建一个 SELECT
查询构建器
let planets: [any SQLRow] = try await db.select()
.columns("id", "name")
.from("planets")
.where("name", .equal, "Earth")
.all()
当与 PostgresKit 驱动程序一起使用时,此代码生成以下 SQL
SELECT "id", "name" FROM "planets" WHERE "name" = $1 -- bindings: ["Earth"]
请注意,Encodable
值会自动绑定为参数,而不是直接序列化到查询中。
select 构建器包括以下方法(通常有几种变体)
columns()
(指定要返回的列和/或表达式列表)from()
(指定要从中选择的表)join()
(指定其他表以及如何将它们与其他表关联)where()
和 orWhere()
(指定缩小可能结果范围的条件)limit()
和 offset()
(指定要返回的结果的有限和/或偏移范围)orderBy()
(指定如何在返回结果之前对其进行排序)groupBy()
(指定用于聚合结果的列和/或表达式)having()
和 orHaving()
(指定在聚合后应用于结果的辅助条件)distinct()
(指定合并重复结果)for()
和 lockingClause()
(指定出现在结果中的行的锁定行为)提供给 where()
或 having()
的条件表达式与 AND
连接。相应的 orWhere()
和 orHaving()
方法使用 OR
连接条件。
builder.where("name", .equal, "Earth").orWhere("name", .equal, "Mars")
当与 MySQL 驱动程序一起使用时,此代码生成以下 SQL
WHERE `name` = ? OR `name` = ? -- bindings: ["Earth", "Mars"]
where()
、orWhere()
、having()
和 orHaving()
也支持创建分组子句
builder.where("name", .notEqual, SQLLiteral.null).where {
$0.where("name", .equal, SQLBind("Milky Way"))
.orWhere("name", .equal, SQLBind("Andromeda"))
}
当与 SQLite 驱动程序一起使用时,此代码生成以下 SQL
WHERE "name" <> NULL AND ("name" = ?1 OR "name" = ?2) -- bindings: ["Milky Way", "Andromeda"]
insert(into:)
方法创建一个 INSERT
查询构建器
try await db.insert(into: "galaxies")
.columns("id", "name")
.values(SQLLiteral.default, SQLBind("Milky Way"))
.values(SQLLiteral.default, SQLBind("Andromeda"))
.run()
当与 PostgreSQL 驱动程序一起使用时,此代码生成以下 SQL
INSERT INTO "galaxies" ("id", "name") VALUES (DEFAULT, $1), (DEFAULT, $2) -- bindings: ["Milky Way", "Andromeda"]
insert 构建器还具有一个方法,用于将 Codable
类型编码为一组值
struct Galaxy: Codable {
var name: String
}
try builder.model(Galaxy(name: "Milky Way"))
此代码生成与 builder.columns("name").values("Milky Way")
相同的 SQL。
update(_:)
方法创建一个 UPDATE
查询构建器
try await db.update("planets")
.set("name", to: "Jupiter")
.where("name", .equal, "Jupiter")
.run()
当与 MySQL 驱动程序一起使用时,此代码生成以下 SQL
UPDATE `planets` SET `name` = ? WHERE `name` = ? -- bindings: ["Jupiter", "Jupiter"]
update 构建器通过 SQLPredicateBuilder
协议支持与 select 构建器相同的 where()
和 orWhere()
方法。
delete(from:)
方法创建一个 DELETE
查询构建器
try await db.delete(from: "planets")
.where("name", .equal, "Jupiter")
.run()
当与 SQLite 驱动程序一起使用时,此代码生成以下 SQL
DELETE FROM "planets" WHERE "name" = ?1 -- bindings: ["Jupiter"]
delete 构建器也是一个 SQLPredicateBuilder
。
raw(_:)
方法允许传递自定义 SQL 查询字符串,并支持参数化绑定和正确引用的标识符
let planets = try await db.raw("SELECT \(SQLLiteral.all) FROM \(ident: table) WHERE \(ident: name) = \(bind: "planet")")
.all()
当与 PostgreSQL 驱动程序一起使用时,此代码生成以下 SQL
SELECT * FROM "planets" WHERE "name" = $1 -- bindings: ["planet"]
\(bind:)
插值应用于任何用户输入,以避免 SQL 注入。\(ident:)
插值用于安全地指定标识符,例如表名和列名。
重要提示
始终优先选择结构化查询(即,存在构建器或表达式类型的查询)而不是原始查询。考虑编写您自己的 SQLExpression
,甚至您自己的 SQLQueryBuilder
,而不是使用原始查询,并且不要犹豫打开 issue 以请求额外的功能支持。