【问题标题】:Postgres integer arrays as parameters?Postgres整数数组作为参数?
【发布时间】:2010-10-08 21:30:12
【问题描述】:

我知道在 Postgres pure 中,您可以将整数数组传递给函数,但 .NET 数据提供程序 Npgsql 不支持此功能。

我目前有一个 DbCommand,我在其中加载对存储过程的调用,添加参数并执行标量以取回 Id 以填充对象。

现在需要将 n 个整数作为参数。这些用于创建子记录,通过它的 id 将新创建的记录链接到整数参数。

理想情况下,我宁愿不必为每个整数在我的 DbCommand 上进行多次 ExecuteNonQuery 调用,因此我将构建一个 csv 字符串作为将在数据库端拆分的参数。

我通常生活在 LINQ 2 SQL 中,喜欢 Db 抽象,在这个项目中使用手动数据访问它只是有点脏,人们通常如何将这些类型的参数传递给 postgres?

【问题讨论】:

  • 对于那些没有读过所选答案的人:.NET Npgsql 提供程序支持数组参数(尽管我不能说当问了问题)。见我的answer below

标签: arrays postgresql argument-passing


【解决方案1】:

您可以始终使用格式正确的字符串。诀窍在于格式。

command.Parameters.Add("@array_parameter", string.Format("{{{0}}}", string.Join(",", array));

请注意,如果您的数组是一个字符串数组,那么您需要使用array.Select(value => string.Format("\"{0}\", value)) 或等效项。我在 PostgreSQL 中将这种样式用于枚举类型的数组,因为数组没有自动转换。

就我而言,我的枚举类型有一些值,例如'value1''value2''value3',而我的 C# 枚举具有匹配的值。在我的例子中,最终的 SQL 查询最终看起来像 (E'{"value1","value2"}'),这是可行的。

【讨论】:

  • 当然,所有关于 SQL 注入的标准警告都适用
【解决方案2】:

我意识到这是一个老问题,但我花了几个小时才找到一个好的解决方案,并认为我会传递我所学到的 here 并为其他人省去麻烦。例如,尝试

    SELECT * FROM some_table WHERE id_column = ANY(@id_list)

其中@id_list 绑定到int[] 参数

    command.Parameters.Add("@id_list", NpgsqlDbType.Array|NpgsqlDbType.Integer).Value = my_id_list;

其中 command 是 NpgsqlCommand(在 Visual Studio 中使用 C# 和 Npgsql)。

【讨论】:

  • 不确定这是否会得到回答,但您的回答似乎直接解决了这种情况。在我的 C# 代码中,我正在构建一个 ID 列表来过滤 DGV 的 DataTable。 ID 列表看起来像1234,2345,3456。我以“通常”的方式添加参数:cmd.Parameters.Add(new NpgsqlParameter("idList", idFilter)); 但在用数据集填充数据适配器时出错,“运算符不存在:整数 = 文本””。我找到了你的代码并将其插入,但我'得到一个不同的错误:“无法将 System.String 类型写为 System.Int32 的数组'”。建议?
  • @marky 听起来您正在尝试为 idList 参数分配字符串文字 ("123,456") 而不是整数数组 ([123, 456])。 Npgsql 工具的要点是,您不是手动滚动带有注入文本值的 SQL 语句,而是使用需要类型对象的类型安全参数构建强类型查询。
  • 好的,这很有道理,那么我如何从一串数字 ("1234,2345, 3456") 转到一个整数数组?可能更好的是,如何将列表构建为整数数组(当前代码:idToAddToList = dt.Rows[rowIndex][0].ToString();)并将其传递给GetDgvData() 函数并将该数组作为参数添加?在这种情况下我可以使用List<int> 吗?
  • 嗯,我得到了 List<> 的 ID 到 GetDgvData(),但我不知道如何处理它以将值放入查询中。
  • @marky 答案中的第二个代码块指定 int[]Array<int> 如果您更喜欢该语法。停止调用.ToString(),只需将dt 列中的整数推送到一个数组中,然后传递它。我不认为 N​​pgsql 支持 List 类型,但是如果您对 Lists 更满意,您可以继续先构建 List<int> 然后调用 .ToArray(),但过程几乎相同,所以我会建议跳过中间步骤。然后你的参数(我称之为my_id_list)绑定如上图。
【解决方案3】:

完整的编码结构

postgresql 函数

CREATE OR REPLACE FUNCTION admin.usp_itemdisplayid_byitemhead_select(
    item_head_list int[])
    RETURNS TABLE(item_display_id integer) 
    LANGUAGE 'sql'

    COST 100
    VOLATILE 
    ROWS 1000
    
AS $BODY$ 
        SELECT vii.item_display_id from admin.view_item_information as vii
where vii.item_head_id = ANY(item_head_list);
    $BODY$;

型号

public class CampaignCreator
    {
        public int item_display_id { get; set; }
        public List<int> pitem_head_id { get; set; }
    }

.NET CORE 函数

DynamicParameters _parameter = new DynamicParameters();
                _parameter.Add("@item_head_list",obj.pitem_head_id);
                
                string sql = "select * from admin.usp_itemdisplayid_byitemhead_select(@item_head_list)";
                response.data = await _connection.QueryAsync<CampaignCreator>(sql, _parameter);

【讨论】:

    【解决方案4】:

    见:http://www.postgresql.org/docs/9.1/static/arrays.html

    如果您的非本地驱动程序仍然不允许您传递数组,那么您可以:

    • 传递一个数组的字符串表示形式(然后您的存储过程可以将其解析为一个数组——参见string_to_array

      CREATE FUNCTION my_method(TEXT) RETURNS VOID AS $$ 
      DECLARE
             ids INT[];
      BEGIN
             ids = string_to_array($1,',');
             ...
      END $$ LANGUAGE plpgsql;
      

      然后

      SELECT my_method(:1)
      

      使用 :1 = '1,2,3,4'

    • 依靠 Postgres 本身将字符串转换为数组

      CREATE FUNCTION my_method(INT[]) RETURNS VOID AS $$ 
             ...
      END $$ LANGUAGE plpgsql;
      

      然后

      SELECT my_method('{1,2,3,4}')
      
    • 选择不使用绑定变量,而是发出一个明确的命令字符串,其中包含所有参数的拼写(确保验证或转义来自外部的所有参数以避免 SQL 注入攻击。)

      CREATE FUNCTION my_method(INT[]) RETURNS VOID AS $$ 
             ...
      END $$ LANGUAGE plpgsql;
      

      然后

      SELECT my_method(ARRAY [1,2,3,4])
      

    【讨论】:

    • 谢谢!不,我没有,我依赖的是我的同事告诉我的。我试试看。
    • 您是否有任何证据表明 PG 中缺乏对准备好的语句的本地支持?我在manual 中找不到任何提及该事实的内容。至于日志记录,我猜服务器会在查询执行时记录查询,而不是从客户端传递查询,因此日志中没有“EXECUTE”语句。
    • @IhorKaharlichenko 措辞很差,而且离题了。编辑了回复。回覆。最初的说法是,驱动程序不会使用PREPARE,除非启用了服务器端准备好的语句(有时是明确的),并且在某些情况下,除非某个命令的执行次数超过了一定的阈值。如果在postgresql.conf 中启用了命令日志记录,那么您将看到实际的SELECT/INSERT/etc。不使用服务器端准备好的语句时的命令。
    猜你喜欢
    • 2020-12-11
    • 1970-01-01
    • 2014-09-28
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-24
    • 2013-09-14
    相关资源
    最近更新 更多