想象一下Postgres中的一张桌子:
Firstname Surname Age
------------------------------
Joe Bloggs 5
Sam Bloggs 7
Ellie Jones 4
Mike Smith 10
我想基于一对值(元组)对数组进行范围过滤:
{Surname=Bloggs && Age>=6 },
{Surname=Smith && Age>=10}
要返回:
Firstname Surname Age
------------------------------
Sam Bloggs 7
Mike Smith 10
我意识到我可以通过手动滚动这样的SQL语句来实现这一点:
SELECT * FROM MyTable t
WHERE (t.Surname = 'Bloggs' AND t.Age >= 6 )
OR (t.Surname = 'Smith' AND t.Age >= 10)
但是,我需要从C#调用它,并且我对解决方案感兴趣,避免必须为每个查询生成纯文本SQL语句。
是否可以使用' generic' SQL语句,传递某种元组/复合类型数组作为过滤器参数?
在其他RDBMS中,我可以,例如,使用值对填充临时表,并加入该表;或使用表值参数(在SQL Server中)。在Postgres + NpgSql中是否有等价物?
PS:我在this question中读到,使用临时表可能不是Postgres中的最佳做法
答案 0 :(得分:2)
我认为传递类似于表值参数的灵活方法是使用JSON传递用于条件的元组数组:
select t.*
from mytable t
join json_array_elements('[{"surname": "Bloggs", "age": 6},
{"surname": "Smith", "age": 10}]') x
on (x ->> 'surname') = t.surname and t.age >= (x ->> 'age')::int;
从您的应用程序中,您可以将JSON作为字符串传递。不确定如何在NpPgSQL中传递参数,在以下示例中?
是参数占位符:
select t.*
from mytable t
join json_array_elements(cast(? as json)) x
on (x ->> 'surname') = t.surname
and t.age >= (x ->> 'age')::int;
答案 1 :(得分:1)
对于它的价值,我在C#应用程序中拥有完全相同的场景,并执行您在临时表解决方案中描述的内容,只使用普通的物理表。我通过在表中添加一个userid字段来克服冲突,所以它看起来像这样:
create table user_data.user_list (
user_id varchar(20) not null,
item_1 text,
item_2 numeric
)
然后实际的C#实现(下面简化以演示)是:
清除之前的所有条目:
string user = Environment.GetEnvironmentVariable("USERNAME");
NpgsqlCommand cmd = new NpgsqlCommand("delete from user_data.user_list " +
"where user_id = :USER",
conn);
cmd.Parameters.AddWithValue("USER", user);
cmd.ExecuteNonQuery();
使用copy
:
using (var writer = conn.BeginBinaryImport(
"copy user_data.user_list from STDIN (FORMAT BINARY)"))
{
foreach (var tuple in userData)
{
writer.StartRow();
writer.Write(user);
writer.Write(tuple.Item1);
writer.Write(tuple.Item2, NpgsqlDbType.Numeric);
}
}
你的最终查询最终会看起来像这样:
select t.*
from
table1 t
join user_data.user_list ul on
t.surname = ul.item_1 and
t.age >= ul.item_2 and
ul.user_id = :USER_ID
与GTT相比,它具有额外的优势,因为它可以轻松调试,因为上次上传的值会在数据库中保留给所有用户。
答案 2 :(得分:0)
我们发现最好的解决方案是传递自定义类型的数组,然后您可以unnest
进入查询中的表格形式,然后加入。
CREATE TYPE predicate_type AS (
Surname text,
Age int);
SELECT * FROM MyTable t
JOIN unnest('{"(Bloggs, 6)","(Smith, 10)"}'::predicate_type[]) p(Surname, Age)
ON t.Surname = p.Surname AND t.Age >= p.Age
我在这里从字面上定义了数组参数,但是您可以将它们作为参数传递给查询。
例如,您可以将等效的C#类型映射到Npgsql中的Postgres类型,然后只需将这些类型的C#数组作为参数传递给命令:
https://www.npgsql.org/doc/types/enums_and_composites.html#mapping-your-clr-types