Announcement

Collapse
No announcement yet.

ADO und Refresh auf View unter MSSQL-7

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

  • ADO und Refresh auf View unter MSSQL-7

    Hallo,<br>
    ich habe einen ganz annehmbaren Weg gefunden, verknüpfte Tabellen als ein
    Recordset vom Server abzurufen und zu bearbeiten(!).<br>
    (Delphi5 Enterprise, ADO (OLE-DB Provider for MSSQL-Server))<br>
    Leider bekomme ich die Fehlermeldung<br>
    <b>Key value for this row was changed or deleted at the data store.
    The local row was deleted.</b><br>
    wenn ich versuche, die ADOQuery für diese View über Refresh zu aktualisieren.
    Was kann man tun, um dieser Erscheinung zu begegnen?<br>
    Vielen Dank für Tipps und viele Grüsse<br>
    Hermann Schmidt

  • #2
    Hallo,

    die folgende Beschreibung stammt aus den HTML-Unterlagen zu meiner ADO-Session auf der im September stattfinden Entwickler-Konferenz:

    Analog zu dem TQuery.Refresh-Problem der BDE-Datasets kann auch unter ADO die Datenmenge nicht immer über <b>Refresh</b> aktualisiert werden. In diesem Fall muss die Datenmenge neu aufgebaut werden (wiederum analog zu TQuery). Außerdem ist der Aufruf von Refresh in den meisten Fällen keine so gute Idee, wie das folgende Beispiel demonstriert.

    In der SQL Server 7-Datenbank OSSISOFT wurde die Tabelle LONGFETCH mit 100 000 Kundendatensätzen gefüllt. Immer dann, wenn die Checkbox WHERE ID < 1000 angekreuzt ist, verwendet das Programm nur die ersten 1000 Datensätze, indem die TADODataset-Eigenschaft <b>CommandText</b> neu zugewiesen wird. Zur Laufzeit wird nur der 472. Datensatz geändert und über Post bestätigt. Klickt man nun den Refresh-Button im DBNavigator an, erhält man für längere Zeit die Sanduhr zu sehen. Der Grund für dieses Verhalten wird im <i>SQL Server Profiler</i> sichtbar, der Aufruf von Refresh sorgt dafür, dass für jeden Datensatz (!) eine SELECT-Abfrage zum SQL Server geschickt wird:

    sp_executesql N'UPDATE "OSSISOFT".."LONGFETCH" SET "Name"=@P1 WHERE "ID"=@P2 AND "Name"=@P3',
    N'@P1 varchar(9),@P2 int,@P3 varchar(8)', 'Galligan2', 472, 'Galligan'<br>
    sp_executesql N'SELECT "ID","Name","Vorname","Ort","eMail" FROM "OSSISOFT".."LONGFETCH" WHERE "ID"=@P1', N'@P1 int', 1<br>
    sp_executesql N'SELECT "ID","Name","Vorname","Ort","eMail" FROM "OSSISOFT".."LONGFETCH" WHERE "ID"=@P1', N'@P1 int', 2<br>
    sp_executesql N'SELECT "ID","Name","Vorname","Ort","eMail" FROM "OSSISOFT".."LONGFETCH" WHERE "ID"=@P1', N'@P1 int', 3<br>
    ...

    Wird das Ganze aufgerufen, wenn die Checkbox WHERE ID < 1000 nicht markiert ist (d.h. es werden alle 100 000 Datensätze verwendet) kann man sofort Feierabend machen. Um diesem Problem aus dem Weg zu gehen, sollte anstelle von Refresh auf <b>Resync</b> zurückgegriffen werden. In diesem Fall wird nur der aktuelle Datensatz nachgeladen.
    <pre>
    procedure TForm1.ButtonResyncClick(Sender: TObject);
    begin
    ADODataSet1.Recordset.Resync(adAffectCurrent, adResyncAllValues);
    end;
    </pre>
    Ein kurzer Blick auf das Log des SQL Server Profilers beweist dies

    Comment


    • #3
      Hallo Herr Kosch,<br>
      die Schwierigkeiten mit der View nehmen scheinbar kein Ende. Versuche ich, einen Datnsatz zu löschen bekomme ich die Fehlermeldung<br>
      <b>multiple-step operations generated errors</b><br>.
      Mit Ihrem Ersatz für Refresh:<br>
      <pre>ADODataSet1.Recordset.Resync(adAffectCurren t, adResyncAllValues);</pre><br>
      bekomme ich wiederum die Fehlermeldung:
      <b>Key value for this row was changed or deleted at the data store. The local row was deleted.</b><br>
      Bestätigt man diese Meldung, erhält man bei jedem Scrollen die Meldung:<br>
      <b>Row handle referred to a deleted row or row marked for deletion</b><br>
      Als Cursor benutze ich clUseClient.<br>
      Vielen Dank<br>
      Hermann Schmid

      Comment


      • #4
        Hallo Herr Kosch,<br>
        die Schwierigkeiten mit der View nehmen scheinbar kein Ende. Versuche ich, einen Datnsatz zu löschen bekomme ich die Fehlermeldung:<br>
        <b>multiple-step operations generated errors</b>.<br>
        Mit Ihrem Ersatz für Refresh:<br>
        <pre>ADODataSet1.Recordset.Resync(adAffectCurren t, adResyncAllValues);</pre><br>
        bekomme ich wiederum die Fehlermeldung:
        <b>Key value for this row was changed or deleted at the data store. The local row was deleted.</b><br>
        Bestätigt man diese Meldung, erhält man bei jedem Scrollen die Meldung:<br>
        <b>Row handle referred to a deleted row or row marked for deletion</b><br>
        Als Cursor benutze ich clUseClient.<br>
        Was mache ich nur falsch ?<br>
        Vielen Dank<br>
        Hermann Schmid

        Comment


        • #5
          Hallo,

          ich haben den Fall einmal mit einer SQL Server 7-Datenbank nachgebaut:
          <pre>
          // Master-Tabelle
          CREATE TABLE TBLMASTER (
          ID INTEGER NOT NULL PRIMARY KEY,
          WERT VARCHAR(20))
          // Detail-Tabelle
          CREATE TABLE TBLDETAIL (
          ID INTEGER NOT NULL PRIMARY KEY,
          MID INTEGER NOT NULL,
          WERT VARCHAR(20))
          // View
          CREATE VIEW V_JOIN AS
          SELECT m.ID, m.WERT, d.ID AS D_ID, d.MID, d.WERT AS D_WERT
          FROM TBLMASTER m JOIN TBLDETAIL d ON m.ID = d.MID
          // View testen
          SELECT * FROM V_JOIN
          </pre>
          Wenn ich dann einige Testdatensätze in diese Tabellen eintrage, kann ich mit dem folgenden Testprogramm <br>
          a) sowohl das Feld D_WERT ändern, als auch <br>
          b) problemlos Refresh aufrufen <br>
          Es funktioniert auch dann, wenn TADODataSet durch TADOQuery ersetzt wird:
          <pre>
          bject Form1: TForm1
          Left = 192
          Top = 107
          Width = 696
          Height = 480
          Caption = 'Form1'
          Color = clBtnFace
          Font.Charset = DEFAULT_CHARSET
          Font.Color = clWindowText
          Font.Height = -11
          Font.Name = 'MS Sans Serif'
          Font.Style = []
          OldCreateOrder = False
          PixelsPerInch = 96
          TextHeight = 13
          object DBGrid1: TDBGrid
          Left = 16
          Top = 64
          Width = 649
          Height = 105
          DataSource = DataSource1
          TabOrder = 0
          TitleFont.Charset = DEFAULT_CHARSET
          TitleFont.Color = clWindowText
          TitleFont.Height = -11
          TitleFont.Name = 'MS Sans Serif'
          TitleFont.Style = []
          end
          object DBNavigator1: TDBNavigator
          Left = 128
          Top = 32
          Width = 240
          Height = 25
          DataSource = DataSource1
          TabOrder = 1
          end
          object ADOConnection1: TADOConnection
          Connected = True
          ConnectionString =
          'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initi' +
          'al Catalog=OSSISOFT;Data Source=(local);Locale Identifier=1031;C' +
          'onnect Timeout=15;General Timeout=0;Use Procedure for Prepare=1;' +
          'Auto Translate=True;Packet Size=4096;Workstation ID=SOLO9150'
          Provider = 'SQLOLEDB.1'
          Left = 16
          Top = 24
          end
          object DataSource1: TDataSource
          DataSet = ADODataSet1
          Left = 88
          Top = 24
          end
          object ADODataSet1: TADODataSet
          Active = True
          Connection = ADOConnection1
          CursorType = ctStatic
          CommandText = 'V_JOIN'
          CommandType = cmdTable
          Parameters = <>
          Left = 56
          Top = 24
          end
          end
          </pre&gt

          Comment


          • #6
            Lässt sich dieses Verfahren (Aktualisierung nur eines Records) auch auf eine MIDAS-Variante ausdehnen

            Comment


            • #7
              Hallo,

              sowohl ADO als auch MIDAS beackern in diesem Punkt das gleiche Gebiet. Ich habe das so noch nicht ausprobiert

              Comment


              • #8
                Hallo,<br>
                nach einigen Blicken in den SQL-Profiler habe ich jetzt des Rätsels Lösung, warum das<br>
                <b>ADODataSet1.Recordset.Resync(adAffectCurrent, adResyncAllValues);</b><br>
                nicht funktioniert. Ist ein Fremdschlüssel NULL (bei Verknüpfung über einen LEFT OUTER JOIN) steht im Profiler u.a. für eine der Detailtabellen (es werden alle an der View beteiligten Tabellen abgerufen !):<br>
                <pre>
                SELECT "Bezeichnung","Detailtabelle_ID" FROM "DATENBNANK".."Detailtabelle" WHERE "Detailtabelle_ID" IS NULL
                </pre>
                Und genau hier meldet ADO (bzw. OLEDB) <pre>Key value for this row was changed or deleted at the data store. The local row was deleted</pre>
                - und das passiert nur bei clientseitigem Cursor.<br>
                Gibt es hierfür vielleicht doch eine Lösung ?<br>
                Viele Grüsse<br>
                Herman

                Comment


                • #9
                  Noch ein Kommentar dazu,<br>
                  folgende Ursachen für obige Fehlermeldung konnte ich herausfinden:<br>
                  Bei einem LEFT OUTER JOIN in einer View werden auch z.B. Detailfelder mit NULL angezeigt wenn der entsprechende Fremdschlüssel in der Haupttabelle nicht belegt ist (also NULL ist). Bei dem 'ADODataSet1.Recordset.Resync(adAffectCurrent, adResyncAllValues);' wird nun ein Statement an den Server geschickt, in dem versucht wird, Detaildatensätze mit Wert NULL (is NULL) zu finden. Diese gibt es aber gar nicht und daher geht ADO davon aus, dass an den zugrundeliegenden Serverdaten etwas geändert sein müsste.<br>
                  Was könnte man tun ???<br>
                  Viele Grüsse<br>
                  Hermann

                  &#10

                  Comment

                  Working...
                  X