sql - Sqlite 使用绑定参数查询没有结果,适用于具有硬编码值的相同查询
问题描述
我遇到了 Sqlite 3 的一个奇怪问题。我有一个相对简单的查询,它在其WHERE
谓词中采用绑定参数。使用绑定参数执行查询时,我得到 0 个结果,但是使用硬编码的值运行完全相同的查询,我得到预期的结果数(> 0)。
sqlite3_expanded_sql()
我通过调用和检查生成的 SQL 字符串仔细检查了我的查询是否已按预期准备。
我使用绑定参数的查询:
SELECT
b.name,
Y(Transform(b.geom, 4326)) "y",
X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText(?, 4326), 27700)) > 0
我为绑定参数提供的值:
let rectWkt = """
POLYGON((
-0.1381030196154711 51.51132617405723,
-0.12929698038450965 51.51132617405723,
-0.12929698038450965 51.50863378616471,
-0.1381030196154711 51.50863378616471,
-0.1381030196154711 51.51132617405723
))
"""
以及具有硬编码值的查询,该查询有效:
SELECT
b.name,
Y(Transform(b.geom, 4326)) "y",
X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText('POLYGON((
-0.1381030196154711 51.51132617405723,
-0.12929698038450965 51.51132617405723,
-0.12929698038450965 51.50863378616471,
-0.1381030196154711 51.50863378616471,
-0.1381030196154711 51.51132617405723
))', 4326), 27700)) > 0
最后,这是 的输出sqlite3_expanded_sql()
,我在绑定上述值后调用它:
SELECT
b.name,
Y(Transform(b.geom, 4326)) "y",
X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText('POLYGON((
-0.1381030196154711 51.51132617405723,
-0.12929698038450965 51.51132617405723,
-0.12929698038450965 51.50863378616471,
-0.1381030196154711 51.50863378616471,
-0.1381030196154711 51.51132617405723
))', 4326), 27700)) > 0
除非我遗漏了什么,否则这些都是相同的语句,但是在执行它们时我会得到完全不同的结果。
我正在使用使用 Swift 绑定的 sqlite c API 执行查询,并且我已经加载了 Spatialite 扩展(用于几何函数)。
sqlite3_bind_text()
我用我的值检查调用是否为SQLITE_OK
. 当我尝试迭代结果行时没有错误,我只是得到SQLITE_DONE
第一个结果,即。结果集为空。
解决方案
我设法解决了它:我需要SQLITE_TRANSIENT
作为最后一个参数传递给我对sqlite3_bind_text
. 我相信这是因为我执行查询的调用在不同的范围内,并且到那时字符串值不在范围内。作为第五个参数传递SQLITE_TRANSIENT
指示 Sqlite 制作自己的值副本。
特别是在 Swift 中,这需要手动定义,
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
因为它是使用 Sqlite 头文件中的宏定义的(参见:https ://www.sqlite.org/c3ref/c_static.html )。
我之前绑定的电话是这样的:
guard
sqlite3_bind_text(stmnt, 1, rectangleWKT, -1, nil) == SQLITE_OK
else {
throw DatabaseError.Bind(message: errorMessage)
}
但它应该更像:
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
guard
sqlite3_bind_text(stmnt, 1, rectangleWKT, -1, SQLITE_TRANSIENT) == SQLITE_OK
else {
throw DatabaseError.Bind(message: errorMessage)
}
然后我可以将我准备好的语句传递给另一个函数来执行并获取结果:
return loadBuildings(forStatement: stmnt)