PostgreSQL bytea流C#合并插入/更新脚本执行报错求助
问题原因
PostgreSQL的DO匿名代码块属于PL/pgSQL执行上下文,外部传入的命名参数(如@stream)无法直接在DO块内部访问。PostgreSQL会把@stream解析成一个运算符尝试和bytea类型做运算,自然找不到匹配的运算符,因此抛出42883错误。
解决方案
针对PostgreSQL 9.4,提供两种可行的解决方式:
方式一:使用带参数传递的DO块
通过DO块的USING子句传递外部参数,在块内部使用位置参数($1、$2等)引用,同时将所有动态值改为参数化传递(避免SQL注入风险):
string upsertCommand = @" do $$ begin UPDATE ic_plc_streams SET stream = $1, mytimestamp = $2, streamlength = $3 WHERE plcidx = $4; if not found Then INSERT INTO ic_plc_streams(plcidx, stream, mytimestamp, streamlength) VALUES($4, $1, $2, $3); End if; end$$ using @stream, @mytimestamp, @streamlength, @plcidx; "; using (var command = new NpgsqlCommand(upsertCommand, conn)) { command.Parameters.AddWithValue("@stream", NpgsqlDbType.Bytea, MyStream); command.Parameters.AddWithValue("@mytimestamp", DateTime.Now); command.Parameters.AddWithValue("@streamlength", MyStream.Length); command.Parameters.AddWithValue("@plcidx", PlcIdx); conn.Open(); command.ExecuteNonQuery(); }
方式二:用C#逻辑实现UPSERT(无需DO块)
先执行UPDATE,通过返回的影响行数判断是否需要执行INSERT,这种方式更直观,也避免了PL/pgSQL的参数作用域问题:
// 先执行UPDATE string updateCmd = @" UPDATE ic_plc_streams SET stream = @stream, mytimestamp = @mytimestamp, streamlength = @streamlength WHERE plcidx = @plcidx; "; using (var command = new NpgsqlCommand(updateCmd, conn)) { command.Parameters.AddWithValue("@stream", NpgsqlDbType.Bytea, MyStream); command.Parameters.AddWithValue("@mytimestamp", DateTime.Now); command.Parameters.AddWithValue("@streamlength", MyStream.Length); command.Parameters.AddWithValue("@plcidx", PlcIdx); conn.Open(); int affectedRows = command.ExecuteNonQuery(); // 如果没有更新到数据,执行INSERT if (affectedRows == 0) { string insertCmd = @" INSERT INTO ic_plc_streams(plcidx, stream, mytimestamp, streamlength) VALUES(@plcidx, @stream, @mytimestamp, @streamlength); "; using (var insertCommand = new NpgsqlCommand(insertCmd, conn)) { // 复用参数,或者重新添加 insertCommand.Parameters.AddRange(command.Parameters.ToArray()); insertCommand.ExecuteNonQuery(); } } }
额外提示
- 永远不要直接拼接字符串生成SQL语句(比如你原来代码里的
'{PlcIdx}'、'{DateTime.Now}'),这会导致SQL注入风险,所有动态值都应该通过参数传递。 - 如果后续能升级到PostgreSQL 9.5+,可以使用更简洁的
INSERT ... ON CONFLICT语法实现UPSERT,这是官方推荐的方式。
内容的提问来源于stack exchange,提问作者user1562809
相关产品推荐
相关产品推荐

