在 Postgres 中插入带有自动递增字段的 JSON 列?
Inserting JSON Column with auto-incrementing field in Postgres?
首先,如果我使用了错误的术语,请原谅我,我 非常 是 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
有什么地方出错了吗?
我明白了。结果我需要解决两件事。
第一个是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...
首先,如果我使用了错误的术语,请原谅我,我 非常 是 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
有什么地方出错了吗?
我明白了。结果我需要解决两件事。
第一个是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...