来自 C# 的 PostgreSQL 参数化查询中的解析错误,但在 pgAdmin III 中有效

Parse error in PostgreSQL parametrised query from C# but works in pgAdmin III

我正在尝试从 C# 执行参数化查询,但出现错误:

ERROR: XX000: parse error - invalid geometry

有提示

"POLYGON((:m" <-- parse error at position 11 within geometry

但是当我 运行 pgAdmin III 中的查询用它们的值替换参数时,查询有效。密码是

command.CommandText = "SELECT area_code FROM area WHERE ST_INTERSECTS(ST_GeographyFromText('POLYGON((:minx :miny, :minx :maxy, :maxx :maxy, :maxx :miny, :minx :miny))'), shape) AND area_type_code = :typecode";
command.CommandType = CommandType.Text;
var typeCodeParameter = new NpgsqlParameter
{
    DbType = DbType.String,
    ParameterName = "typecode",
    Value = _typeCode
};
var minxParameter = new NpgsqlParameter
{
    DbType = DbType.Double,
    ParameterName = "minx",
    Value = _minX
};
var minyParameter = new NpgsqlParameter
{
    DbType = DbType.Double,
    ParameterName = "miny",
    Value = _minY
};
var maxxParameter = new NpgsqlParameter
{
    DbType = DbType.Double,
    ParameterName = "maxx",
    Value = _maxX
};
var maxyParameter = new NpgsqlParameter
{
    DbType = DbType.Double,
    ParameterName = "maxy",
    Value = _maxY
};
command.Parameters.Add(typeCodeParameter);
command.Parameters.Add(maxxParameter);
command.Parameters.Add(maxyParameter);
command.Parameters.Add(minxParameter);
command.Parameters.Add(minyParameter);
using (var reader = command.ExecuteReader())
    while (reader.Read())
        areas.Add((string)reader["area_code"]);

工作查询是

SELECT area_code FROM area WHERE ST_INTERSECTS(ST_GeographyFromText('POLYGON((-1.0042576967558934 50.78431084582985, -1.0042576967558934 51.199216033050647, 1.9400782407441057 51.199216033050647, 1.9400782407441057 50.78431084582985, -1.0042576967558934 50.78431084582985))'), shape) AND area_type_code = 'County'

我做错了什么?我应该如何设置 minx、miny、maxx、maxy 参数?

问题是您在分离坐标。参数不只是在 SQL 中拼凑在一起(它不仅仅是模板替换),它需要是放置参数的语法有效位置。一个好的完整性检查是尝试相同的查询,但不是直接用值替换参数,而是使用过程 SQL 和变量。

您会发现问题在于 ST_GeographyFromText 函数不会在输入的字符串中扩展参数 - 这是预期的行为。如果你想使用这个函数,你不能在字符串内部使用参数——你仍然需要把所有的值都调高并将它们作为一个字符串传递——和你在 "removed the parameters" 时所做的一样。最简单的解决方案可能是将整个字符串作为参数传递,或者只是将参数添加到查询中的字符串(例如 ST_GeographyFromText('POLYGON((' || cast(:minx ...) || '), ' || ... || ')') 等)

您似乎正在使用参数化查询转换旧 "concatenate a bunch of strings" SQL。一定要坚持,但你需要考虑语法。就像你不能只把子查询放在一个参数中一样,你也不能像这样把一个值分成两个参数。

所以你需要使用更好的类型,或者进行一些辅助转换(比如一个函数需要两个浮点数,returns 你需要的类型)。

使用此参数化查询的正确方法是使用采用数字参数的函数来构建信封,例如 ST_MakeEnvelope(xmin, ymin, xmax, ymax)

SELECT area_code
FROM area
WHERE ST_Intersects(ST_MakeEnvelope(:minx, :miny, :maxx, :maxy)::geography, shape)
   AND area_type_code = :typecode;

其他格式化文本 (WKT) 的尝试速度较慢且有损,因为数字先转换为文本,然后再解析回数字。