none
Codepage bei vorhanden Daten von 850 in 1252 konvertieren

    Frage

  • Hallo Forum,

    im Zuge einer Datenübernahme aus einer alten DOS Warenwirtschaft in eine neue Windows bzw. Microsoft SQL basierende Warenwirtschaft sind alle Sonderzeichen verloren gegangen.

    Ich habe u.a. hier im Forum schon diverse Hinweise darauf gefunden, wie die Codepage beim Flatfile Import von vorne rein korrekt eingestellt werden kann. Allerdings möchte ich gerne darauf verzichten das die Daten neu eingespielt werden da schon mit diesen gearbeitet wurde und diverse Korrekturen vorgenommen wurden. D.h. wenn ich neu einspiele, dann müsste ich die Daten erst exportieren da ich den 'ist'-Stand benötige.

    Testweise habe ich einen Datensatz kopiert und als txt Datei gespeichert. Mit einem Tool habe ich dann diese Datei von Codepage 850 in Codepage 1252 konvertiert und alle Zeichen waren korrekt.

    Daher meine eigentliche Frage: ist es möglich das ich die Daten in der vorhandene Datenbank konvertiere?
    Kann ich dem Server irgendwie sagen er soll jede Tabelle als Codepage 1252 neu speichern?

    Ich habe VBA und VB.Net Kenntnisse, allerdings bin ich im Microsoft SQL Server recht neu.

    Vielen Dank und viele Grüße,
    Mirko

    Dienstag, 12. Juni 2012 09:17

Alle Antworten

  • Mirko_H wrote:

    im Zuge einer Datenübernahme aus einer alten DOS Warenwirtschaft in eine
    neue Windows bzw. Microsoft SQL basierende Warenwirtschaft sind alle
    Sonderzeichen verloren gegangen.

    Ich habe u.a. hier im Forum schon diverse Hinweise darauf gefunden, wie
    die Codepage beim Flatfile Import von vorne rein korrekt eingestellt
    werden kann. Allerdings möchte ich gerne darauf verzichten das die Daten
    neu eingespielt werden da schon mit diesen gearbeitet wurde und diverse
    Korrekturen vorgenommen wurden. D.h. wenn ich neu einspiele, dann müsste
    ich die Daten erst exportieren da ich den 'ist'-Stand benötige.

    Testweise habe ich einen Datensatz kopiert und als txt Datei gespeichert.
    Mit einem Tool habe ich dann diese Datei von Codepage 850 in Codepage
    1252 konvertiert und alle Zeichen waren korrekt.

    Daher meine eigentliche Frage: ist es möglich das ich die Daten in der
    vorhandene Datenbank konvertiere?
    Kann ich dem Server irgendwie sagen er soll jede Tabelle als Codepage
    1252 neu speichern?

    Das sollte mit der COLLATE Klausel machbar sein. Evt. kannst Du die Tabelle so ändern, dass diese Texte in den Felder mit der Collation SQL_Latin1_General_Pref_CP437_CI_AS interpretiert werden. Nur vermute ich, dass bei der Änderung der Collation eines Feldes der Feldinhalt in der Tabelle automatisch konvertiert wird.
    Falls es geht, dann sollte es möglich sein, dies mit einem Update unter Angabe der Collation richtig zu stellen. Habe dieses aber noch nie versucht.

    Gruss
    Henry

    Dienstag, 12. Juni 2012 09:46
  • Hallo Henry,

    vielen Dank für die Antwort. Ich habe es mit folgendem Befehl versucht

    ALTER DATABASE <datenbank> COLLATE SQL_Latin1_General_Pref_CP437_CI_AS;
    

    aber leider keinen Erfolg gehabt. Ich bekomme zwar Rückmeldung das der Befehl ausgeführt wurde, jedoch sind die Sonderzeichen immer noch verschwunden.

    Wie gesagt bin ich im SQL Bereich Anfänger und kann daher nicht Ausschließen das ich etwas falsch gemacht habe.

    Viele Grüße,
    Mirko

    Dienstag, 12. Juni 2012 10:18
  • Die Sonderzeichen sind verschunden? Da stehen nicht einfach falsche Zeichen drin, wie grosse AE, etc?

    Kannst Du mal mit SUBSTRING() und CHARINDEX() eines der Sonderzeichen versuchen aus der DB auszulesen. Falls da jetzt ein Leerzeichen drin steht, dann wirst Du nicht darum herum kommen, die Daten neu zu importieren. Aus einem Leerzeichen lassen sich nicht einfach Sonderzeichen generieren.

    Gruss

    Henry

    Dienstag, 12. Juni 2012 10:57
  • Nein es sind schon Zeichen vorhanden, sorry das ich mich missverständlich ausgedrückt habe.

    Es steht z.B. 'B”nningstedt' statt 'Bönningstedt' in der Tabelle.

    Dienstag, 12. Juni 2012 11:11
  • ALTER DATABASE <datenbank> COLLATE SQL_Latin1_General_Pref_CP437_CI_AS;

    aber leider keinen Erfolg gehabt. Ich bekomme zwar Rückmeldung das der Befehl ausgeführt wurde, jedoch sind die Sonderzeichen immer noch verschwunden.

    Hallo Mirko,

    das ändern im Prinzip nichts, nur die Standard-Einstellung, wenn man neue Tabellenfelder anlegt ohne explizit die Collation definieren. Das solltest Du auch umgehend wieder rückgängig machen.

    Worauf Henry wohl raus wollte, war beim Selektieren die Collation zu ändern, um die Daten quasi zu konvertieren, um sie anschließend so wieder in die Tabelle zu schreiben:

    CREATE TABLE #test (Bezeichnung varchar(100) COLLATE Latin1_General_CI_AS);
    
    -- üä
    INSERT INTO #test VALUES (CHAR(129) + CHAR(132) + CHAR(149) );
    -- ÄÖU
    INSERT INTO #test VALUES (CHAR(142) + CHAR(153) + CHAR(154) );
    
    SELECT Bezeichnung
          ,Bezeichnung COLLATE SQL_Latin1_General_CP850_CI_AS
    FROM #test
    
    GO
    DROP TABLE #test;

    Bei meinen Test hat es bisher aber noch nicht richtig funktioniert.

    Grundsätzlich würde ich aber sagen, das Kind ist in den Brunnen gefallen. Du schriebst ja selbst, das zwischenzeitlich Daten aktualisiert wurden, also hast Du jetzt Mischmasch in der Datenbank. Du könntest noch versuchen, einzelne Ascii Zeichen auszutauschen, das wird aber ein großer Aufwand.
    Also: alles noch einmal mit der richtigen Codepage importieren.

    P.S.: So was testest man normalerweise vorher aus.


    Olaf Helper
    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing

    Dienstag, 12. Juni 2012 13:54
  • Hallo Olaf,

    ich habe jetzt auch schon den ganzen Nachmittag rumprobiert und bin zu dem Schluss gekommen das ich wohl den großen Aufwand 'ausbaden' muss den ich mir da eingeheimst habe und Zeichenweise ersetze was falsch ist. Damit werde ich hoffentlich eine gute Quto an korrekten Datensätzen hinbekommen.

    Vielen Dank euch für die Antworten und Hinweise.
    Und was das Testen angeht, das werde ich sicher nicht noch einmal vergessen :-)

    Viele Grüße,

    Mirko

    Dienstag, 12. Juni 2012 14:10
  • Hallo Mirko

    Mirko_H wrote:

    ich habe jetzt auch schon den ganzen Nachmittag rumprobiert und bin zu
    dem Schluss gekommen das ich wohl den großen Aufwand 'ausbaden' muss den
    ich mir da eingeheimst habe und Zeichenweise ersetze was falsch ist.
    Damit werde ich hoffentlich eine gute Quto an korrekten Datensätzen
    hinbekommen.

    Das kannst Du ja auch mit einem Update machen, der dann etwa so aussieht:

    UPDATE DeineTabelle SET DeinFeld = REPLACE(DeinFeld, CHAR(xxx), 'ä')

    Du musst nun nur herausfinden, was xxx ist. Dazu gibt's die Funktion ASCII()

    Nehmen wir mal denText 'B”nningstedt', dann möchtest Du nun das ” durch ein ö ersetzen.
    Den Wert für xxx für ” findest Du so (2 = zweites Zeichen):
    SELECT ASCII(SUBSTRING('B”nningstedt', 2, 1))
    Das Ergebnis ist 148. Der Replace wäre also nun folgendermassen:

    SET DeinFeld = REPLACE(DeinFeld, CHAR(148), 'ö')

    Damit hast Du alle ” in dem Feld der ganzen Tabelle durch ein ö ersetzt.

    Die Replace Funktion kannst Du auch verschachteln, dann kommst Du mit einem einzelnen UPDATE statement aus.

    Gruss
    Henry

    Freitag, 15. Juni 2012 04:36
  • Das kannst Du ja auch mit einem Update machen, der dann etwa so aussieht:

    Hallo Henry,

    naja, der Unterschied zwischen den beiden Codepages ist nicht gerade klein, und inzwischen hat er ja ein Mix an Daten, so dass es auch zu unerwünschten Ersetzungen kommen wird. Zudem hat eine ERP Datenbank erfahrungsgemäß viele Tabellen und die wieder viele varchar Felder; der Aufwand wäre sehr hoch und fehleranfällig, ein komplett neuer Import wäre weniger aufwendig.


    Olaf Helper
    * cogito ergo sum * errare humanum est * quote erat demonstrandum *
    Wenn ich denke, ist das ein Fehler und das beweise ich täglich
    Blog Xing

    Freitag, 15. Juni 2012 04:56
  • Hallo Mirko,

    da hast Du Dir wirklich was eingebrockt ;-)

    Zunächst zur Klärung: Das Wechseln einer Codepage alleine bringt überhaupt nichts.
    COLLATE steuert "nur" wie intern die Bytefolgen als Zeichen verglichen oder sortiert werden.
    Dazu wird intern eine Codetabelle verwendet, die jedem Byte (bzw. Bytefolge) eine Position für
    Vergleiche bzw. Sortierung zuweist. An den Daten selbst wird nichts geändert.

    Deswegen kommt man nicht drum herum, die Zeichen als Bytes in ihrer Rohform zu behandeln.

    Vor Urzeiten hatte ich mal ein Skript in der seligen SQL Server Newsgroup gepostet;
    hier nochmal (falls Google den Laden mal ganz dicht macht):

    CREATE PROC spOEMToAnsi
    (
        @inString    VARCHAR(255),
        @outString   VARCHAR(255) OUTPUT
    )
    AS
    /* Prozedur zum Konvertieren einer VARCHAR-Zeichenkette
     * vom OEM nach ANSI
     * Derzeit begrenzt auf deutsche Umlaute
     * Verwendet von spOEMToAnsiTable
     * Autor: Elmar Boye 01.09.2000 für SQL Server NG
     */
        SET NOCOUNT ON
    
        IF @inString IS NULL
        BEGIN
            SELECT @outString = NULL
            RETURN 1
        END
    
        SELECT @outString = CAST(@inString AS VARBINARY)
        SELECT @outString = REPLACE(@outString,
             CAST(132 AS VARBINARY(1)),
             CAST('ä' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(148 AS VARBINARY(1)),
            CAST('ö' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(129 AS VARBINARY(1)),
            CAST('ü' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(142 AS VARBINARY(1)),
            CAST('Ä' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(153 AS VARBINARY(1)),
            CAST('Ö' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(154 AS VARBINARY(1)),
            CAST('Ü' AS VARBINARY(1)))
        SELECT @outString = REPLACE(@outString,
            CAST(225 AS VARBINARY(1)),
            CAST('ß' AS VARBINARY(1)))
        IF @outString <> @inString
            RETURN 0
        ELSE
            RETURN 1
    GO
    
    CREATE PROC spOEMToAnsiTable
    (
        @TableName sysname
    )
    AS
    /* Prozedur zum Konvertieren OEM ANSI in Tabellen
     * Arbeitet derzeit nur mit VARCHAR Spalten
     * Benötigt die Prozedur spOEMToAnsi
     * Autor: Elmar Boye 01.09.2000 für SQL Server NG
     */
    
        DECLARE @ColumnName sysname
        DECLARE @ColumnType sysname
        DECLARE @ColumnLength int
        DECLARE @nIndex int
        DECLARE @CrLf varchar(2)
        -- Variablendefinitionen
        DECLARE @sqlDeclare varchar(8000)
        -- Spaltenliste für SELECT
        DECLARE @sqlSelect varchar(8000)
        -- Spaltenliste für FETCH
        DECLARE @sqlFetch varchar(8000)
        -- Updateanweisung
        DECLARE @sqlExec varchar(8000)
        -- Prozeduraufrufe
        DECLARE @sqlUpdate varchar(8000)
    
        SET NOCOUNT ON
    
        -- Konstante für CRLF
        SELECT @CrLf = CHAR(13) + CHAR(10)
        SELECT @nIndex = 0,
            @sqlDeclare = '',
            @sqlSelect = '',
            @sqlFetch = '',
            @sqlExec = '',
            @sqlUpdate = ''
    
        DECLARE curColumns CURSOR LOCAL FAST_FORWARD
        FOR
            SELECT c.name AS ColumnName,
                   t.name AS ColumnType,
                   c.length AS ColumnLength
            FROM syscolumns c
            INNER JOIN systypes t
            ON c.type = t.type
            WHERE c.id = OBJECT_ID(@TableName)
              AND t.name = 'VARCHAR'
            ORDER BY colorder
    
        OPEN curColumns
        FETCH curColumns
            INTO @ColumnName, @ColumnType, @ColumnLength
        WHILE @@fetch_status = 0
        BEGIN
            SELECT @nIndex = @nIndex + 1
            IF  @nIndex > 1
                SELECT @sqlSelect = @sqlSelect + ', ',
                       @sqlFetch = @sqlFetch + ', ',
                       @sqlUpdate = @sqlUpdate + ', ' + @CrLf
    
            SELECT @sqlDeclare = @sqlDeclare +
                'DECLARE @in' + @ColumnName +
                ' VARCHAR(' + CAST(@ColumnLength AS varchar) + '),' +
                ' @out' + @ColumnName +
                ' VARCHAR(' + CAST(@ColumnLength AS varchar) + ') ' + @CrLf
    
            SELECT @sqlSelect = @sqlSelect + @ColumnName,
                   @sqlFetch = @sqlFetch + '@in' + @ColumnName,
                   @sqlUpdate = @sqlUpdate + @ColumnName +
                        '= @out' + @ColumnName
    
            SELECT @sqlExec = @sqlExec +
                'EXEC spOemToAnsi @in' + @ColumnName + ', ' +
                      '@out' + @ColumnName + ' OUTPUT ' + @CrLf
    
            FETCH curColumns
                INTO @ColumnName, @ColumnType, @ColumnLength
        END
        CLOSE curColumns
        DEALLOCATE curColumns
    
        -- Nix gefunden
        IF @nIndex = 0 RETURN 1
    
    EXEC (
    'DECLARE @nCount int' + @CrLf +
    @sqlDeclare + @CrLf +
    'DECLARE curTable CURSOR LOCAL KEYSET FOR ' + @CrLf +
    'SELECT ' + @sqlSelect + ' FROM ' + @TableName + ' ' + @CrLf +
    'FOR UPDATE' + @CrLf +
    '--SET NOCOUNT ON' + @CrLf +
    'SELECT @nCount = 0 ' + @CrLf +
    'OPEN curTable' + @CrLf +
    'FETCH curTable INTO ' + @sqlFetch + @CrLf +
    'WHILE @@fetch_status <> -1 BEGIN' + @CrLf +
    '    IF @@fetch_status = -2 GOTO SkipDeleted' + @CrLf +
    '    SELECT @nCount = @nCount + 1 ' + @CrLf +
    @sqlExec +
    '    UPDATE ' + @TableName + ' SET ' + @CrLf +
    @sqlUpdate + @CrLf +
    '    WHERE CURRENT OF curTable' + @CrLf +
    'SkipDeleted:' + @CrLf +
    '    FETCH curTable INTO ' + @sqlFetch + @CrLf +
    'END' + @CrLf +
    'CLOSE curTable' + @CrLf +
    'DEALLOCATE curTable' + @CrLf +
    'SELECT  "' + @TableName + '" AS Tabelle, @nCount AS Zeilen'
    ) 

    Beachte, dass es für SQL Server 7 entstanden ist.
    Es werden nur VARCHAR Spalten mit maximal 255 Zeichen berücksichtigt
    und nur die deutschen Umlaute, weitere müsste man sich anhand der Codetabellen zusammen suchen.

    Anstatt einer Prozedur würde man heute eher eine Funktion verwenden
    und die einzelnen REPLACE sollte man der Geschwindigkeit zuliebe zusammenfassen.

    Und Testen solltest Du das sehr intensiv, ich hatte das damals auch nur zusammengebastelt,
    einen vollständigen Test konnte ich mangels fehlender "Falschdaten" auch damals nicht machen.

    Der dort genannte Alternativansatz (BCP oder jetzt SSIS) gefiele mir aber heute noch besser ;-)'

    Gruß Elmar



    Freitag, 15. Juni 2012 05:44
    Beantworter
  • Hallo Olaf

    So gross ist der Unterschied nun auch wieder nicht. Es geht vorallem um Sonderzeichen. In der Regel sind es 10-12 Zeichen, im rein Deuschen (ohne Akzente und Grosse Umlaute) sogar noch weniger (4 oder 7), die auch wirklich vorkommen.

    Es ist auch nicht so, dass es da ein Durcheinander geben könnte. Es handelt sich normalerweise um die Sonderzeichen, die in der Codepage 437 zwischen 128 und 165 liegen. Diese Zeichen kommen in diesen Positionen der Codepage 1252 nicht vor. Diese befinden sich dort in der Region 192 -255. Daher ist dieer Punkt irrelevant. Ein solcher Replace kann also durchaus auf teilweise bereits überschrieben Texten angewendet werden.

    Gruss

    Henry

    Freitag, 15. Juni 2012 06:12
  • Interessant dürften die Zeichen im Bereich 128 - 166 der CP 437 sein. Diese findet man hier:

    Codepage 437

    Damit sind dann auch die französischen und spanischen Sonderzeichen, sowie einige nordische abgedeckt.

    Gruss

    Henry

    Freitag, 15. Juni 2012 06:15
  • Hallo ihr alle,

    tatsächlich sind es eignetlich nur die Zeichen 132, 142, 148, 153, 129, 154, 225, 193 die gegen 228, 196, 246, 214, 252, 220, 223 ausgetauscht werden sollen.

    Da ich mit dem SQL Server nicht so vertraut bin habe ich mich für einen Lösungsansatz mit VB.NET entschieden in dem ich alle Tabellen durchgehen, dort alle Textfelder raussuche und in diesen dann mit dem auch von Hanry vorgeschlagenen Statement

    UPDATE TABELLE SET FELD = REPLACE(CAST(FELD AS varchar(max)), '™', 'Ö')

    ändere. Funktioniert soweit ganz gut, ich habe die Datenbank mal dupliziert und dort Testläufe gemacht und erwarte keine Probleme wenn ich am Wochenende die echte Datenbank bearbeite.

    Vielen Dank für eure Ideen und vor allem die Aussage das "dass Kind in den Brunnen gefallen ist" hat mich weiter gebracht.

    Ich sage hier noch mal bescheid wenn ich fertig bin und es geklappt hat.

    Viele Grüße aus Hamburg,

    Mirko

    Freitag, 15. Juni 2012 06:51