2017-04-19 4 views
0

Zuerst, bitte verzeih mir, wenn ich die falschen Begriffe verwende, bin ich sehr neu sowohl PostgreSQL und NoSQL-style Datenspeicherung. Weiter:JSON-Spalte mit automatisch inkrementierendem Feld in Postgres einfügen?

Ich benutze npgsql, um ein .NET-Projekt mit einer PostgreSQL-Datenbank zu verbinden. Wir verwenden es als NoSQL-Dokumentenspeicher, wobei unsere Tabellen aus einem Primärschlüssel id Spalte und einem Jsonb-Objekt in der Spalte data bestehen, sonst nichts.

In der Spalte data möchten wir auch eine Möglichkeit, ein ID-Feld zu generieren, das bei jedem Einfügen automatisch inkrementiert wird. Diese ID muss nicht unbedingt mit dem Primärschlüssel übereinstimmen, aber das ist die Annahme, von der ich arbeite.

Ich habe in der Lage gewesen, diese Arbeit in pgAdmin machen mit der folgenden Scripts:

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!\"}"

So weit, so gut. Ich denke, ich werde dies umgestalten, so dass das Jsonid-Feld seine eigene Sequenz hat und nextval anstelle von currval verwendet (um Race-Bedingungen zu vermeiden, die zu Duplikaten führen), aber die Frage ist nicht darüber. Ich habe Probleme, dies auf der Codepage zu replizieren. Umsah in anderen questions hier führte mich zu glauben, dass dies funktionieren könnte:

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; 
 
}

Aber wenn ich versuche, es zu testen, wird die folgende Ausnahme ausgelöst:

Npgsql.PostgresException: 22P02: invalid input syntax for type json; Token "currval" is invalid

Irgendwelche Ideen, wo ich falsch liege?

Antwort

0

Ich habe es. Es stellte sich heraus, dass es zwei Dinge gab, die ich beheben musste.

Die erste war die Konvertierung in Jsonb. Ich habe es vorher nicht bemerkt, aber wenn Sie sich meine postgres-Ausgabe anschauen, wird die data-Spalte als String anstelle eines jsonb-Objekts gespeichert. Dies wird einfach durch Ersetzen von to_jsonb('...') durch ('...')::jsonb behoben.

Die zweite Aufgabe war es, es in C# arbeiten zu lassen. Um das zu tun, musste ich die Art ändern, wie die NpgsqlCommand erstellt wird: Um Zugriff auf den currval Wert zu bekommen, muss ich den Befehlstext als Rohtext übergeben, anstatt ihn so zu parametrieren, wie ich es ursprünglich versucht hatte . So ist die letzte, sieht funktionierenden Code mehr wie folgt aus:

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...

+0

Einfügen der Daten in die Abfrage direkt als ein String bricht die Grundregel von Datenbanken. Haben Sie jemals eine bessere Lösung gefunden? – piojo