Umstieg von MS-SQL auf PG, was kommt an möglichen Problemen?

Hallo,

wir beabsichtigen, eine Software von der Anbindung vom MS-Sql-Server auf PG umzustellen. Was kann denn an möglichen Problemen auf uns zukommen?
Die Software ist in C# geschrieben und verwendet die “SqlConnection” als Zugriffklasse. Es gibt einen kompletten Datenbankgenerator, welcher aus einer XML-Datei heraus die Datenbank erstellen und mit Inhalt vorbefüllen kann. Hier werden also alle Varianten von “Alter Table”, “Alter Column”, “Add Column” etc. benötigt. Im Code selber wird hie und da mit direkten SQL-Befehlen gearbeitet, üblicherweise simple Sachen der Art “Update xxx SET FieldA=0 WHERE FieldA=null” o.ä. Ansonsten werden Daten via der üblichen Konstruktion über ein DataSet/DataAdapter geladen und geschrieben und gelöscht.

Viele Grüße, Karsten Heimer.

Aus meiner Erfahrung stolpern die meisten bei einer Umstellung von SQL Server zu Postgres darüber, dass String Vergleiche standardmäßig Groß/Kleinschreibung beachten. 'Bla' ist also nicht gleich 'bla'

Anders als praktisch alle anderen relationalen Datenbanksysteme unterscheidet SQL Server nicht zwischen der Abfragesprache und der prozeduralen Sprache. Schleifen, IF Abfragen oder Variablen gibt es nur in prozeduralen Sprachen. In Postgres ist das üblicherweise PL/pgSQL.

Postgres hält sich stärker an den SQL Standard und verwendet demnach doppelte Anführungszeichen " anstatt der unsäglichen eckigen Klammern [] (die in SQL und bei Postgres für den Zugriff auf Array Elemente vorgesehen sind).

Das Transaktionshandling ist unterschiedlich. In einer Standardinstallation von SQL Server blockieren sich lesende und schreibende Zugriffe gegenseitig (was häufig mit der Holzhammermethode “with nolock” umgangen wird in SQL Server).

“Rechnen” mit Timestamp oder Date Werten geht völlig anders (z.B. - anstatt datediff())

Index-Strategien sind unterschiedlich. Postgres hat z.B. keinen “clustered index” (und ich stimme hier Markus Winand zu, dass diese als Primäschlüssel nicht wirklich eine bessere Wahl darstellen).

XML und JSON Funktionen sind komplett anders.

Hallo castorp,

danke für deine Antwort. Das klingt ja bis hierhin gar nicht übel. Wir haben bisher die Datenbank nur als Datenspeicher verwendet und uns von solchen Dingen wie Trigger, gespeicherten Prozeduren etc. ferngehalten. Das erledigen wir alles im Code, da wir früher schon mal Oracle parallel unterstützt hatten. Das bedeutet, solchen Sachen wir “Rechnen mit timestamp”, JSON etc. fallen bei uns schon mal weg. Der String-Vergleich freilich wird spannend.

Gibt es Empfehlungen zum Zugriffstreiber? Ich haben jetzt den OLEDB-Treiber von Intellisoft gesehen, weiterhin hat Devart einen ADO.NET Treiber und dann gibt es noch kostenlose Pakete wie Npg…

Was ist mit der SQL-Syntax zum Erstellen und Pflegen der Strukturen, also alles was so mit “Create/Alter/Add/Drop” etc. angeht? Gibt es da Fallen?

Gruß, Karsten.

Keine Ahnung, wir verwenden ausschließlich Java/JDBC

Was heisst “Fallen”?

Einige Datentypen sind unterschiedlich z.b. datetime2 vs. timestamp oder bit vs. boolean. Die bestehenden Skripte werden auf keinen Fall ohne Änderung laufen.

Du kannst Dir ja mal SQLServer2Pgsql ansehen.

Grundsätzlich würde ich auch empfehlen Postgres auf Linux laufen zu lassen und nicht auf einem Windows Server. Viele nützliche Tools und Erweiterungen gibt es halt nur für Linux

Hallo castorp,

danke für deine Antworten.

Mit “Fallen” meine ich zum Beispiel eine andere SQL-Syntax zum Erzeugen/Anpassen von Tabellen zum Beispiel. Ich habe schon gemerkt, dass es bei den Datentypen Unterschiede gibt. Mit Linux kann ich leider nicht umgehen, da fehlen mir die Kenntnisse. Der PG soll auch auf einem windows-Server laufen, weil er die Datenquelle für einen Webservice ist, der unter dem IIS läuft. Da brächte man sonst doch wieder zwei Server und eben Linuxkenntnisse.

Ich danke dir auf jeden Fall mal für deine Hilfe, ist nicht selbstverständlich an einem Sonntag :slight_smile:

Grüße, Karsten.

In PG wirst Du keine “Fallen” haben - sauberes SQL wird problemfrei mit PG funktionieren.

Dein Beipsiel:

Update xxx SET FieldA=0 WHERE FieldA=null

ist z.B.falsch, Du kannst NULL nicht mit etwas vergleichen (also =), sondern nur prüfen, ob etwas NULL ist oder nicht.

Ja, das war natürlich ein unglückliches Beispiel, es muss freilich heißen “FieldA IS NULL”.

Dennoch scheitere ich derzeit an scheinbar trivialen Dingen. Vielleicht kann mir jemand da ja noch helfen. Ich habe folgenden simplen Code:

string sql = "SELECT ID, Nummer FROM TestTable WHERE ID=1";
OleDbCommand cmd = new OleDbCommand(sql, con);
OleDbDataAdapter oleda = new OleDbDataAdapter(cmd);
OleDbCommandBuilder olecb = new OleDbCommandBuilder(oleda);

oleda.UpdateCommand = olecb.GetUpdateCommand();

Genau in der letzten Zeile scheitert das System und sagt:

System.Data.OleDb.OleDbException: "Undefined table testtable"

Das gibts doch nicht, oder? Die Tabelle ist da, habe sie mit dem “pgAdmin” selber angelegt.
Das Beispiel wäre mit dem OLEDB-Treiber von Intellisoft. Ein gleiches Beispiel habe ich mit dem Treiber von Devart:

string sql = "SELECT ID, Nummer FROM TestTable WHERE ID=1";
Devart.Data.PostgreSql.PgSqlCommand cmd = new Devart.Data.PostgreSql.PgSqlCommand(sql, con);
Devart.Data.PostgreSql.PgSqlDataAdapter oleda = new Devart.Data.PostgreSql.PgSqlDataAdapter(cmd);
Devart.Data.PostgreSql.PgSqlCommandBuilder olecb = new Devart.Data.PostgreSql.PgSqlCommandBuilder(oleda);

oleda.UpdateCommand = olecb.GetUpdateCommand();

Da sieht der Fehler dann so aus:

Devart.Data.PostgreSql.PgSqlException: "Relation »testtable« existiert nicht"

Wahrscheinlich haut auch irgendwas mit der Codepage noch nicht hin.
Kann sich da einer einen Reim drauf machen?

Gruß, Karsten.

"TestTable" ist ein anderer Name als TestTable (was das Gleiche ist, wie testtable).

Du hast offensichtlich beim Anlegen der Tabellen die Namen in doppelten Anführungszeichen geschrieben, was dazu führt, dass der Name nun Case-Sensitive ist (so wie der SQL Standard es fordert).

Am Besten verwendest Du niemals doppelte Anführungszeichen für Tabelle- oder Spaltenamen.

Hm, so ganz verstehe ich das nicht. Ich habe die Tabelle via “pgAdmin” angelegt und dort sieht sie so aus:


Spielt mir der “pgAdmin” hier einen Streich?
Wenn ich das Schema “Tables” auslesen, dann kommt sowas zustande:

Tables:database:base4IT_PG, schema:public, name:TestTable, tabletype:User, definer:postgres, oid:42380, acl:, hasoids:True, hassubtables:False, rownumber:0, comment:, columnnumber:2, triggersnumber:False, constraint:TestTable_pkey, columnconstrainsindexes:{1},

Überall ist die Tabelle “TestTable” mit großen "T"s geschrieben und ohne Gänsefüßchen…

Ich nutze pgAdmin nicht, aber ich glaube das fügt die Anführungszeichen hinzu sobald ein Name Groß/Kleinschreibung enthält. D.h. im Fall Deines Screenshots wird daraus ein create table "TestTable" (...) gemacht.

Grundsätzlich ist das Anlegen von Tabellen über eine GUI sowieso eine schlechte Idee. Das SQL Skript muss ja eh irgendwo gespeichert werden (idealerweise in einem Versionsverwaltungssystem wie Git), da kann man es auch gleich selber “eintippen” - vor allem wenn die GUI versucht schlauer zu sein, als der Anwender.

Ja, du hast schon Recht, die TastTable war auch nur ein Versuch. Unsere echte Datenbank wird natürlich über einen Generator erzeugt, das wäre jetzt der nächste Schritt für mich. Ich wollte halt mal die grundsätzliche Anbindung der Datenbank austesten. Aber ich werde jetzt unseren Generator mal drauf los lassen.

Also, der Tipp mit den Gänsefüßchen war goldrichtig. Die GUI “pgAdmin” hat die einfach hinzugedichtet. Unser DB-Generator erzeugt die Tabellen ohne diese Gänsefüßchen und alle Tabellen sind kleingeschrieben.

Was nicht funktioniert sind solche Befehle wie diese hier. Da ist irgendwie die Syntax falsch, aber was?

ALTER TABLE AB_Anlagenarten ALTER COLUMN Inventarnummer TYPE VARCHAR(20) DROP NOT NULL
ALTER TABLE AB_Bewegungen ALTER COLUMN BewegungsartID TYPE BIGINT SET NOT NULL

Gruß, Karsten.

Wie im Handbuch beschrieben, muss der Datentyp nicht angegeben werden wenn man nur das NULL/NOT NULL Attribut ändern möchte

ALTER TABLE ab_anlagenarten ALTER COLUMN inventarnummer DROP NOT NULL;
ALTER TABLE ab_bewegungen ALTER COLUMN bewegungsartid  SET NOT NULL;

Ach herrje, ich hab’s befürchtet. Da muss ich unseren Generator umbauen. Der baut derzeit einfach aus allen vorgegebenen Eigenschaften das ALTER Statement zusammen und feuert das ab. Wenn die bestehende Tabelle schon genau so aussieht wie das Statement das fordert, so ist bislang gar nix passiert.
Das wird auf jeden Fall spannend.
Danke und Gruß, Karsten.

Ich hätte nochmal bitte eine Frage. Offenbar gibt es ein Problem mit Umlauten, sowohl in Tabellennamen als auch in Feldnamen. Geht das generell nicht? Oder muss man das dem Server erst beibringen? Vom Sinn oder Unsinn der Umlaute mal abgesehen…
Es kommt jedenfalls folgende merkwürdige Meldung von einem “CREATE TABLE…” zurück:

ungültige Byte-Sequenz für Kodierung »UTF8«: 0xe4 0x74 0x20

Das ist ein Statement, wo ein Feldname ein “ö” enthält…

works for me:

edb=# create table umlaute_äöüß (ä int, ö int, ü int, ß int);
CREATE TABLE
edb=*# \d umlaute_äöüß
            Table "public.umlaute_äöüß"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 ä      | integer |           |          | 
 ö      | integer |           |          | 
 ü      | integer |           |          | 
 ß      | integer |           |          | 

edb=*# select * from umlaute_äöüß;
 ä | ö | ü | ß 
---+---+---+---
(0 rows)

edb=*#

da scheint was mit client_encoding nicht zu stimmen bei Dir …

Hm, wenn ich via “pgAdmin” eine Tabelle mit Umlauten anlegen, dann geht das auch…
Wo könnte ich denn das Encoding definieren? Im “Connectionstring” in C#?

du kannst das nach Aufbau der Verbindung setzen

Danke, habs gefunden, die Zeile mit “Unicode = true” tut es:

Devart.Data.PostgreSql.PgSqlConnection conOriginal = new Devart.Data.PostgreSql.PgSqlConnection(this.ConnectionString);
conOriginal.Unicode = true;

Viele Grüße…

1 „Gefällt mir“

Ich habe noch eine Inkompatibilität gefunden:

Das Begrenzen auf eine bestimmte Menge an Sätzen im SELECT haben wir früher mit “SELECT TOP xxx” gemacht, bei PG muss man offenbar ein “LIMIT xxx” hinten anhängen.