这是一个示例SQL查询,您无法使用GRDB中的便利构建器构建...
let q = "job.id, job.name, job.city,
ifnull(jobcategory.value, 'no category'),
ifnull(jobpriority.value, 'no priority'),
from job
left join jobcategory on job.category = jobcategory.id
left join jobpriority on job.priority = jobpriority.id
where job.user = 13"
(事实上,我做了一个例子,你不能在任何较旧的,较少支持的iOS Swift SQL库中构建)
然后像
for ..
let id = ..
let name = ..
let city = ..
let category = ..
let priority = ..
我听说过使用GRDB,你实际上可以使用原始SQL(即实际上甚至不使用GRDB的查询构建器),但是,在结果中仍然使用方便消费* ,照顾类型等
如果是这样,实际上你是如何在这个例子中做到的?
答案 0 :(得分:2)
GRDB提供了一个查询构建器:
let persons = try Person.filter(emailColumn != nil).fetchAll(db) // [Person]
它也理解SQL:
let persons = try Person.fetchAll(db, "SELECT * FROM persons WHERE email IS NOT NULL")
上面的两个代码段都将数据库行转换为Person实例。 RowConvertible
协议和GRDB提供的全功能Record
类支持此类转换。下面的代码使用协议:
struct Person {
let email: String
let name: String
}
extension Person : RowConvertible {
init(row: Row) {
email = row.value(named: "email")
name = row.value(named: "name")
}
}
init(row:)
构造函数用于“查询接口”请求Person.filter(...).fetchAll(db)
和SQL请求Person.fetchAll(db, "SELECT ...")
。
这就是 GRDB的意思,当你想使用原始SQL 时,它不会惩罚你。您的自定义记录类型支持查询接口请求和开箱即用的SQL请求。使用这两种技术获取记录同样容易:
// Two one-liners:
let persons = try Person.filter(emailColumn != nil).fetchAll(db)
let persons = try Person.fetchAll(db, "SELECT * FROM persons WHERE email IS NOT NULL")
现在,您的示例可以写成:
struct Job {
let id: Int64
let name: String
let city: String
let category: String
let priority: String
}
extension Job : RowConvertible {
init(row: Row) {
id = row.value(named: "id")
name = row.value(named: "name")
city = row.value(named: "city")
category = row.value(named: "category")
priority = row.value(named: "priority")
}
}
try dbQueue.inDatabase { db in
let q = "SELECT job.id, job.name, job.city, " +
" IFNULL(jobcategory.value, 'no category') AS category, " +
" IFNULL(jobpriority.value, 'no priority') AS priority " +
"FROM job " +
"LEFT JOIN jobcategory ON job.category = jobcategory.id " +
"LEFT JOIN jobpriority ON job.priority = jobpriority.id " +
"WHERE job.user = 13"
let jobs = try Job.fetchAll(db, q)
}
由于类别和优先级不是作业列,您可能更喜欢将上述结构分成两部分:
struct Job {
let id: Int64
let name: String
let city: String
}
struct ExtendedJob {
let job: Job
let category: String
let priority: String
}
extension Job : RowConvertible {
init(row: Row) {
id = row.value(named: "id")
name = row.value(named: "name")
city = row.value(named: "city")
}
}
extension ExtendedJob : RowConvertible {
init(row: Row) {
job = Job(row: row)
category = row.value(named: "category")
priority = row.value(named: "priority")
}
}
try dbQueue.inDatabase { db in
let q = "SELECT job.id, job.name, job.city, " +
" IFNULL(jobcategory.value, 'no category') AS category, " +
" IFNULL(jobpriority.value, 'no priority') AS priority " +
"FROM job " +
"LEFT JOIN jobcategory ON job.category = jobcategory.id " +
"LEFT JOIN jobpriority ON job.priority = jobpriority.id " +
"WHERE job.user = 13"
let jobs = try ExtendedJob.fetchAll(db, q)
}
您最终可以将自定义SQL查询封装在“自定义请求”中:
extension ExtendedJob {
static func filter(userId: Int64) -> AnyTypedRequest<ExtendedJob> {
let request = SQLRequest(
"SELECT job.id, job.name, job.city, " +
" IFNULL(jobcategory.value, 'no category') AS category, " +
" IFNULL(jobpriority.value, 'no priority') AS priority " +
"FROM job " +
"LEFT JOIN jobcategory ON job.category = jobcategory.id " +
"LEFT JOIN jobpriority ON job.priority = jobpriority.id " +
"WHERE job.user = ?",
arguments: [userId])
return request.asRequest(of: ExtendedJob.self)
}
}
// No SQL in sight:
let jobs = try dbQueue.inDatabase { db in
try ExtendedJob.filter(userId: 13).fetchAll(db)
}