首页 > 解决方案 > 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第一个结果,即。结果集为空。

标签: sqlsqlitespatialite

解决方案


我设法解决了它:我需要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)

推荐阅读