Announcement

Collapse
No announcement yet.

ADO und negativ BigInt Probleme

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • ADO und negativ BigInt Probleme

    Ich habe Probleme mit negativ BigInt Werten.
    Meine Konfiguration:
    Windows 2000 Professional SP2
    MS Sql Server 2000
    MDAC 2.6 SP1
    Delphi 6 Professional Update Pack 1

    Mit dem Beispeil von Andreas Kosch (21-Aug-01, ADO und BigInt) sehe ich:
    ein negativ Wert ist gezeigt als positiv
    wenn ich versuche ein negativ Wert einzugeben, bekomme ich die Fehlermeldung:

    Get EOleException - 'Multiple-step operation generated errors. Check each status value.'

    Aber ein einfach VbScript laeuft problemlos:
    <PRE>
    Dim cnPg1
    Dim rsBanks
    Set cnPg1 = CreateObject("ADODB.Connection")
    cnPg1.ConnectionString = "Provider=SQLOLEDB.1;" & _
    "User ID=xxx;Password=xxx;Initial Catalog=xxx;"
    cnPg1.Open
    Set rsBigInt = CreateObject("ADODB.Recordset")
    rsBigInt.Open "SELECT * FROM BigIntTest", cnPg1
    while (not rsBigInt.EOF)
    MsgBox(rsBigInt.Fields("wert").Value)
    rsBigInt.MoveNext
    wend
    </PRE>

    I'm sure it's no surprise that my German is awful.

  • #2
    Hallo,

    in der Tat scheinen die ADO Express-Komponenten von Delphi 6 an dieser Stelle eine Macke zu haben. Nur beim direkten Zugriff auf die nativen ADO-Objekte erhalte ich das korrekte Ergebnis. Meine Testumgebung (Delphi 6.01; Windows 2000 SP2, MDAC 2.6) sieht so aus:
    <pre>
    USE tempdb
    GO
    CREATE TABLE BigIntTest (
    TLB_ID INT NOT NULL IDENTITY PRIMARY KEY,
    Wert BIGINT NOT NULL)
    GO
    INSERT INTO BigIntTest (Wert) VALUES (1)
    INSERT INTO BigIntTest (Wert) VALUES (0)
    INSERT INTO BigIntTest (Wert) VALUES (-1)
    GO
    SELECT * FROM BigIntTest
    </pre>

    Fall 1: Zugriff über die nativen ADO-Objekte (Recordset mit implizitern Connection-Objektinstanz):
    <pre>
    uses ADOInt, Variants;

    procedure TForm1.Button1Click(Sender: TObject);
    resourcestring
    cCS = 'Provider=SQLOLEDB.1;Persist Security Info=False;' +
    'User ID=sa;Initial Catalog=tempdb;Data Source=W2KSRV;';
    var
    aRS : _Recordset;
    begin
    aRS := CoRecordSet.Create;
    aRS.CursorLocation := adUseClient;
    try
    aRS.Open('select TLB_ID, Wert from dbo.BigIntTest', cCS, adOpenStatic, adLockReadOnly, adCmdText);
    aRS.MoveFirst;
    while not aRS.EOF do
    begin
    ShowMessage('Ergebnis: ' + VarToStr(aRS.Collect[1]));
    aRS.MoveNext;
    end;
    finally
    aRS.Close;
    end;
    end;
    </pre>
    Ergebnis: <br>
    1<br>
    0<br>
    <b>-1</b>

    Fall 2: TADOConnection + TADODataSet

    Ergebnis im TDBGrid: <br>
    1<br>
    0<br>
    <b>1</b>

    Wenn die Kombination von TADODataSet und negative BIGINT-Werte benötigt wird, könnte man folgendes machen:
    <pre>
    procedure TForm1.Button2Click(Sender: TObject);
    var
    aRS : _Recordset;
    begin
    aRS := ADODataSet1.Recordset;
    aRS.MoveFirst;
    while not aRS.Eof do
    begin
    ShowMessage('Ergebnis: ' + VarToStr(aRS.Collect[1]));
    aRS.MoveNext;
    end;
    end;
    </pre>
    Da ich mir direkt einen Interface-Zeiger auf das darunterliegende Recordset-Objekt hole, erhalte ich auch hier die richtigen Ergebnisse (1, 0, <b>-1</b>).
    &#10

    Comment


    • #3
      Danke fuer ihre schnelle und informative Antwort! Ich habe ihr Beispeil probiert und moechte das aRs.Collect[1] in eine int64 Variable gespeichert wird. Koennten Sie mir bitte sagen, was dafuer die schnellste Methode ist?

      Ich meine das StrToInt(VarToStr()) waere gar nicht so schnell.

      Ich habe probiert

      <PRE>
      var
      i64: int64;
      ...
      i64 := aRS.Collect[1];
      </PRE>

      aber ich bekomme EVariantError: Invalid variant type conversion. Es
      sieht aus als ob der Variant Type nicht definiert ist (Wert ist 14)

      Comment


      • #4
        Hallo,

        auch nach dem UpdatePack#1 ist Delphi 6 in der Behandlung von Int64-Variablen/Datentypen sowie der Variant-Funktionen nicht fehlerfrei. Der von Borland inoffiziell empfohlene Workaround besteht in der Zwischenstufe einer String-Variable mit anschliessendem <b>StrToInt64</b>-Aufruf. Das könnte dann so aussehen:
        <pre>
        procedure TForm1.ButtonWorkaroundClick(Sender: TObject);
        var
        aRS : _Recordset;
        s64 : String;
        i64 : Int64;
        begin
        aRS := ADODataSet1.Recordset;
        aRS.MoveFirst;
        while not aRS.Eof do
        begin
        s64 := aRS.Collect[1];
        i64 := StrToInt64(s64);
        ShowMessage(IntToStr(i64));
        aRS.MoveNext;
        end;
        end;
        </pre&gt

        Comment


        • #5
          Thanx again

          Comment


          • #6
            Hallo

            eine andere Möglichkeit ist die Unit ADODB der VCL zu editieren.

            <PRE>
            function TCustomADODataSet.GetFieldData(....);
            begin
            ...

            procedure VarToBuffer;
            begin
            case Field.DataType of
            ..
            {
            im Falle einer negativen Int64-Zahl wird das Vorzeichen Byte sign aus Decimal gesetzt (d.h. <> 0).
            Da Int64 in der OleVariant-Struktur (als Typ 14)in der Decimal-Notation vorliegt muß zur
            korrekten Ermittlung des Int64 das Vorzeichen geprüft werden.
            Die alte Zugriffslogik LargeInt(Buffer^) := Decimal(Data).Lo64 nahm bisher keine Rücksicht darauf.
            }

            // ftLargeInt : LargeInt(Buffer^) := Decimal(Data).Lo64;

            ftLargeInt : begin
            LargeInt(Buffer^) := Decimal(Data).Lo64;
            if Decimal(Data).sign <> 0 then
            LargeInt(Buffer^) := LargeInt(Buffer^) * -1;
            end;

            ..

            end;
            end;

            ...

            begin
            .
            .
            .
            end;

            </PRE>

            Um die neue Fassung zu verwenden muß der Suchpfad für das Projekt erweitert werden (Runtime-Packages nicht verwenden)

            Comment


            • #7
              Hallo,
              wir arbeiten hier mit Oracle 9.2 und Delphi6.2. Wir stellen zur Zeit unsere Quellen auf ADO um und sind damit sehr zufrieden.
              Jetzt haben wir zum ersten Mal auch negative Werte in der OracleDB. Bei der Abfrage und Darstellung via ADOQuery, TDatasource und TDBGrid werden die negativen Werte leider als positiv dargestellt.
              Als Loesung haben wir jetzt eine to_char-Wandlung sql-seitig realisiert, aber besonders befriedigend ist das ja nicht. Gibts da Loesungen ? In Newsgroups habe ich nur den Tip gefunden, ein Delphi-Update zu installieren.
              (auch die manuelle Aenderung von Nemo nuetzte nichts).
              Das Feld hat auf Oracle number(2).

              vielen dank fuer die Hilfe

              Comment


              • #8
                Hallo,

                nur die Borland-Komponenten von ADO Express (alias dbGo) sind fehlerhaft - die nativen ADO-Objekte liefern den richtigen (negativen) Wert zurück. Im Fall der Fälle kann man also immer noch auf den nativen Weg ausweichen, wie das folgende Beispiel zeigt:

                SQL Server 2000-Datenbank:
                <pre>
                USE tempdb
                GO
                CREATE TABLE BigIntTest (
                TLB_ID INT NOT NULL IDENTITY PRIMARY KEY,
                Wert BIGINT NOT NULL)
                GO
                INSERT INTO BigIntTest (Wert) VALUES (1)
                INSERT INTO BigIntTest (Wert) VALUES (0)
                INSERT INTO BigIntTest (Wert) VALUES (-1)
                GO
                SELECT * FROM BigIntTest
                </pre>
                <b>Fall 1</b>: Zugriff über die nativen ADO-Objekte (Recordset mit implizitern Connection-Objektinstanz):
                <pre>
                uses ADOInt, Variants; procedure TForm1.Button1Click(Sender: TObject);
                resourcestring
                cCS = 'Provider=SQLOLEDB.1;Persist Security Info=False;' +
                'User ID=sa;Initial Catalog=tempdb;Data Source=W2KSRV;';
                var
                aRS : _Recordset;
                begin
                aRS := CoRecordSet.Create;
                aRS.CursorLocation := adUseClient;
                try
                aRS.Open('select TLB_ID, Wert from dbo.BigIntTest', cCS, adOpenStatic, adLockReadOnly, adCmdText);
                aRS.MoveFirst;
                while not aRS.EOF do
                begin
                ShowMessage('Ergebnis: ' + VarToStr(aRS.Collect[1]));
                aRS.MoveNext;
                end;
                finally
                aRS.Close;
                end;
                end;
                </pre>
                Ergebnis: <br>
                1<br>
                0<br>
                -1 <br>

                <b>Fall 2</b>: TADOConnection + TADODataSet

                Ergebnis im TDBGrid:<br>
                1<br>
                0<br>
                1 <br>

                <b>Workaround</b>: Wenn die Kombination von TADODataSet und negative BIGINT-Werte benötigt wird, könnte man folgendes machen:
                <pre>
                procedure TForm1.Button2Click(Sender: TObject);
                var
                aRS : _Recordset;
                begin
                aRS := ADODataSet1.Recordset;
                aRS.MoveFirst;
                while not aRS.Eof do
                begin
                ShowMessage('Ergebnis: ' + VarToStr(aRS.Collect[1]));
                aRS.MoveNext;
                end;
                end;
                </pre>
                Da ich mir direkt einen Interface-Zeiger auf das darunterliegende Recordset-Objekt hole, erhalte ich auch hier die richtigen Ergebnisse (1, 0, -1).

                Comment

                Working...
                X