Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi 'Fehler' im JoinTest von BetterADODataset (https://www.delphipraxis.net/110007-fehler-im-jointest-von-betteradodataset.html)

Peinhard 11. Mär 2008 16:02

Datenbank: MSSQL • Version: 8 • Zugriff über: ADO

'Fehler' im JoinTest von BetterADODataset
 
To whom it may concern...

'Fehler' kann man es vielleicht nicht unbedingt nennen, etwas 'schlampig' bzw voreilig ist der JoinTest schon (zumindest mit MSSQL und Provider SQLOLEDB). Hier der Original-Code aus IsJoinedRecordSet:

Delphi-Quellcode:
 
S:=VarToStr(RecordSet.Fields[0].Properties[cBaseTableName].Value);
For I:=Pred(RecordSet.Fields.Count) DownTo 1 Do With RecordSet.Fields Do
begin
  If (S<>VarToStr(Item[I].Properties[cBaseTableName].Value)) Then Begin
    Result:=jtJoined;
    Break;
    End;
Was ziemlich simpel darauf hinausläuft, dass bei einem Feld mit abweichendem Property 'BASETABLENAME' der JoinTest mit Ergebnis jtJoined beendet wird. Nun ist es aber so, dass vom Server berechnete Felder, zB mit folgendem SELECT (der zB verhindern soll, dass umfangreiche Memos oder Blobs für eine Gridanzeige tatsächlich geladen und zu einem client-seitigen Dataset transferiert werden):

SQL-Code:
SELECT ID, Name, [_Description] = CASE WHEN [Description] IS NULL THEN '<Memo>' ELSE '<MEMO>' END FROM ItemTable
in den Feld-Properties mit Value NULL (nach VarToStr also '') für BaseTableName geführt wird, der JoinTest also fälschlich jtJoined zurückgibt. Das kann man natürlich umgehen bzw 'austricksen', indem man die Unique_-Properties setzt, man könnte aber auch beim JoinTest selbst ansetzen, in diesem Fall und unter der Annahme, dass solcherart berechnete Felder immer mit einem Unterstrich beginnen zB so:

Delphi-Quellcode:
 
S := VarToStr(RecordSet.Fields[0].Properties[cBaseTableName].Value);
for I := pred(RecordSet.Fields.Count) downto 1 do
begin
  SI := VarToStr(RecordSet.Fields.Item[I].Properties[cBaseTableName].Value);
  if (S <> SI) and not ((SI = '') and (Pos('_',Fields[i].FieldName) = 1)) then
  begin
    Result := jtJoined;
    Break;
  end;
end;
Oder etwas allgemeingültiger und unter Berücksichtigung der Tatsache, dass der Treiber für die in Frage stehenden Felder auch die Property 'BASECOLUMNNAME' mit NULL zurückgibt:

Delphi-Quellcode:
S := VarToStr(RecordSet.Fields[0].Properties[cBaseTableName].Value);
for I := pred(RecordSet.Fields.Count) downto 1 do
begin
  SB := VarToStr(RecordSet.Fields.Item[I].Properties[cBaseTableName].Value);
  SC := VarToStr(RecordSet.Fields.Item[I].Properties[cBaseColumnName].Value);
  if (S <> SB) and not ((SB = '') and (SC = '')) then
  begin
    Result := jtJoined;
    Break;
  end;
end;
Die entsprechenden Variablen und Konstanten sind natürlich zu definieren. Es empfiehlt sich auch, für jede Kombination von DB und Treiber die Rückgabewerte der Properties nochmal gesondert zu untersuchen, wozu folgender Code dienen mag:

Delphi-Quellcode:
uses ..., ADO26_TLB, ...;

function VarToStrVerbose(const V: Variant): string;
begin
  if VarIsEmpty(V) then Result := '<not assigned>'
  else if VarIsArray(V) then Result := '<array>'
  else if VarIsNull(V) then Result := '<null>'
  else Result := VarToStr(V);
end;

procedure FieldProperties(ASet: TBetterADODataset; AList: TStrings; Index: integer=-1);

  procedure FieldProps(ASet: TBetterADODataset; AList: TStrings; Index: integer);
  var sN,sV: string;
      vV: Variant;
      i: integer;
  begin
    with ASet do
    begin
      for i:=0 to pred(RecordSet.Fields[Index].Properties.Count) do
      begin
        sN := RecordSet.Fields[Index].Properties[i].Name;
        vV := RecordSet.Fields[Index].Properties[i].Value;
        sV := VarToStrVerbose(vV);
        AList.Add(sN+'='+sV);
      end;
    end;
  end;

var i: integer;
begin
  AList.Clear;
  if (Index = -1) then
  begin
    for i:=0 to pred(ASet.Fields.Count) do
    begin
      AList.Add('['+ASet.Fields[i].FieldName+']');
      FieldProps(ASet,AList,i);
      AList.Add('');
    end;
  end else
  begin
    AList.Add('['+ASet.Fields[Index].FieldName+']');
    FieldProps(ASet,AList,Index);
  end;
end;
Der Code gibt eine INI-ähnliche Liste zurück, die mit den StringList-Eigenschaften Name und Value gut ausgewertet oder in einem Memo dargestellt werden kann. Auch sehr interessant ist nebenbei bemerkt das (prinzipiell gleiche) Auslesen der Properties des Connection- wie des RecordSet-Objektes. Dort wird bei besagtem Treiber übrigens im Zweifelsfall nicht NULL, sondern ein 'empty variant' für nicht gesetzte Werte zurückgegeben, daher der entsprechende Code in VarToStrVerbose.

Gruß an alle, Peinhard

P.S. Wenn das in die Code-Lib soll, bitte verschieben und/oder kurze Nachricht. Danke.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:55 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz