首先,如果我使用了错误的术语,请原谅我,我对非常全新的PostgreSQL和NoSQL风格的数据存储。继续:
我正在使用npgsql将.NET项目连接到PostgreSQL数据库。我们将它用作NoSQL文档存储,我们的表由id
列中的主键data
列和jsonb对象组成,没有别的。
在data
列中,我们还想要一种生成id字段的方法,该字段在每个插入时自动递增。这个id不一定需要匹配主键,但这是我正在工作的假设。
我已经能够使用以下脚本在pgadmin中完成这项工作:
drop table public.testjson;
drop sequence public.testjson_id_seq;
CREATE SEQUENCE public.testjson_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE public.testjson_id_seq
OWNER TO login;
CREATE TABLE public.testjson
(
id bigint NOT NULL DEFAULT nextval('testjson_id_seq'::regclass),
data jsonb,
CONSTRAINT testjson_pkey PRIMARY KEY (id)
);
ALTER TABLE public.testjson
OWNER TO login;
-- Insert data using currval --
INSERT INTO testjson (data) VALUES( to_jsonb('{"jsonid": '||currval('testjson_id_seq'::regclass)::bigint||', moreStuff: "all the stuff!"}'));
INSERT INTO testjson (data) VALUES( to_jsonb('{"jsonid": '||currval('testjson_id_seq'::regclass)::bigint||', moreStuff: "all the stuff!"}'));
INSERT INTO testjson (data) VALUES( to_jsonb('{"jsonid": '||currval('testjson_id_seq'::regclass)::bigint||', moreStuff: "all the stuff!"}'));
INSERT INTO testjson (data) VALUES( to_jsonb('{"jsonid": '||currval('testjson_id_seq'::regclass)::bigint||', moreStuff: "all the stuff!"}'));
select * from testjson
-- Output --
id | data
---------------------------------------------------------
1 | "{\"jsonid\": 1, moreStuff: \"all the stuff!\"}"
2 | "{\"jsonid\": 2, moreStuff: \"all the stuff!\"}"
3 | "{\"jsonid\": 3, moreStuff: \"all the stuff!\"}"
4 | "{\"jsonid\": 4, moreStuff: \"all the stuff!\"}"
到目前为止,这么好。我想我将重构这一点,以便jsonid字段有自己的序列并使用nextval
而不是currval
(以防止竞争条件导致重复),但问题不在于此。我在代码方面无法复制这个问题。环顾其他questions让我相信这可行:
var crcmd = new NpgsqlCommand("INSERT INTO "+_schema+"."+table+" ("+JsonColumn+") VALUES (:json) RETURNING id;", _conn);
var jsonData = getSerializedJsonData(thing, primaryKey);
crcmd.Parameters.AddWithValue("json", NpgsqlDbType.Jsonb, jsonData);
returnId = (long) crcmd.ExecuteScalar();
...
private string getSerializedJsonData<T>(T thing, string primaryKey, string tableSeq)
{
var jsonThing = JsonConvert.SerializeObject(thing);
var bracketIndex = jsonThing.IndexOf('{');
var thingPrefix = jsonThing.Substring(0, bracketIndex + 1);
var thingData = jsonThing.Substring(bracketIndex + 1);
var pkEntry = "\"" + primaryKey +"\": currval('" + tableSeq + '::regclass)::bigint, ";
jsonThing = thingPrefix + pkEntry + thingData;
return jsonThing;
}
但是当我尝试测试它时,抛出以下异常:
Npgsql.PostgresException: 22P02: invalid input syntax for type json; Token "currval" is invalid
我出错的任何想法?
答案 0 :(得分:0)
我明白了。原来我需要解决两件事。
第一个是转换为jsonb。我之前没有注意到,但是如果你查看我的postgres输出,data
列将被保存为字符串而不是jsonb对象。将to_jsonb('...')
替换为('...')::jsonb
即可轻松解决此问题。
第二项任务是让它在C#中运行。为了做到这一点,我不得不改变创建NpgsqlCommand
的方式:为了访问currval
值,我必须将命令文本作为原始文本传递,而不是参数化正如我原本试图做的那样。所以最终的工作代码看起来更像是这样:
var jsonData = getSerializedJsonData(thing, primaryKey);
var crcmd = new NpgsqlCommand("INSERT INTO "+_schema+"."+table+" ("+JsonColumn+") VALUES (('+jsonData+')::jsonb) RETURNING id;", _conn);
returnId = (long) crcmd.ExecuteScalar();
...
private string getSerializedJsonData<T>(T thing, string primaryKey, string tableSeq)
{
var jsonThing = JsonConvert.SerializeObject(thing);
var bracketIndex = jsonThing.IndexOf('{');
var thingPrefix = jsonThing.Substring(0, bracketIndex + 1);
var thingData = jsonThing.Substring(bracketIndex + 1);
var pkEntry = "\"" + primaryKey +"\": currval('" + tableSeq + '::regclass)::bigint, ";
jsonThing = thingPrefix + pkEntry + thingData;
return jsonThing;
}
-- Output (truncated) --
id | data
--------------------------------------------------------
1 | { IsActive: true, "DisplayName": "configUser", "UserLoginId": 1", "PasswordHash":...
2 | { IsActive: true, "DisplayName": "WDCGVBNESSAULSAZLIVR", "UserLoginId": 2", "Pass...
3 | { IsActive: false, "DisplayName": "UJMFANMOSHPNDQSEUEGL", "UserLoginId": 3", "Pas...
4 | { IsActive: true, "DisplayName": "SOQDFTZVHPHXIXDVQJTS", "UserLoginId": 4", "Pass...
5 | { IsActive: true, "DisplayName": "WFPUMCRBRPDHSQALMKPW", "UserLoginId": 5", "Pass...
6 | { IsActive: true, "DisplayName": "HVBEGQSSFJWCJYCCLMXI", "UserLoginId": 6", "Pass...