Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Möglicher Fehler in GetTableName (https://www.delphipraxis.net/183054-moeglicher-fehler-gettablename.html)

Neutral General 8. Dez 2014 11:45


Möglicher Fehler in GetTableName
 
Hallo,

Der Titel ist jetzt leider nicht gerade der Beste..
Habe folgende SQL:

Zitat:

SELECT
tabelle1.*,
(SELECT feld FROM tabelle_innen WHERE 1=1)
FROM tablle1
WHERE tabelle1.id = 4
TCustomDADataSet (unit DBAccess) ist nun die Basis für die Query-Komponente die ich benutze.
TCustomDADataSet besitzt eine Methode GetTableName die intern PSGetTableName aufruft, welche wiederrum in einigen Fällen die Funktion GetTableNameFromSQLEx aus der Unit Data.DBCommon aufruft.

Und diese GetTableNameFromSQLEx-Funktion hat in meinen Augen einen Fehler. Denn GetTableName und somit GetTableNameFromSQLEx liefern bei obiger Query als Ergebnis "TABELLE_INNEN" statt "TABELLE1".

In der Funktion selbst wird quasi das erste "FROM" gesucht und der Tabellenname dahinter zurückgegeben.

Meine 2 Fragen:

1) Könnt ihr das in eurer Delphi Version reproduzieren (habs mit XE7 probiert)
2) Stimmt ihr mir zu dass das Ergebnis oben falsch ist oder verstehe ich die Funktion nur falsch und TABELLE_INNEN ist zwar nicht das was ich will aber das was rauskommen sollte.

Dejan Vu 8. Dez 2014 11:53

AW: Möglicher Fehler in GetTableName
 
Zitat:

Zitat von Neutral General (Beitrag 1282605)
...
Und diese GetTableNameFromSQLEx-Funktion hat in meinen Augen einen Fehler. Denn GetTableName und somit GetTableNameFromSQLEx liefern bei obiger Query als Ergebnis "TABELLE_INNEN" statt "TABELLE1".

In der Funktion selbst wird quasi das erste "FROM" gesucht und der Tabellenname dahinter zurückgegeben.

Wenn das wirklich so implementiert wurde, müsste man den Programmierer teeren und federn. Mindestens. Ich kenne die Funktion nicht, aber ich ziehe aus deinen Ausführungen den Schluss, das 'GetTableName' das/die an der Query maßgeblich beteiligten Tabelle(n) liefern soll. Ich frage mich zwar, was der Sinn dahinter ist, aber egal: Ich würde das pro Feld sinnvoller finden.

Zum Verständnis: Welchen Rückgabewert erwartest Du bei dieser Query?
Code:
select a.Customer
   from Tabelle1 a
   join Tabelle2 b on a.ID = b.ID
Und hier?
Code:
select a.Customer
   from Tabelle2 b
        join Tabelle1 a on a.ID = b.ID
Und hier?
Code:
select a.Customer, b.Info
   from Tabelle2 b
        join Tabelle1 a on a.ID = b.ID
Und hier?
Code:
select *
   from Tabelle2 b
        join Tabelle1 a on a.ID = b.ID
Nicht falsch verstehen: Ich will nur verstehen, welchen Sinn diese Funktion haben soll.

Neutral General 8. Dez 2014 12:05

AW: Möglicher Fehler in GetTableName
 
Es gibt z.B. Methoden die generieren aus einem SELECT Statement automatisch ein Insert-Statement für die jeweilige Tabelle. Dafür ist das halt nützlich u.a. Daher würde ICH folgende Ergebnisse erwarten:

- Tabelle1
- Tabelle2
- Tabelle2
- Tabelle2

Ob die Methode allerdings auch so gedacht ist weiß ich nicht. Im Prinzip erwarte ich den (ersten) Tabellennamen des äußersten FROM.

Die Funktion aus der Unit Data.DBCommon:

Delphi-Quellcode:
function GetTableNameFromSQLEx(const SQL: string; IdOption: IDENTIFIEROption): string;
var
  Start: PChar;
  Token: string;
  SQLToken, CurSection: TSQLToken;
begin
  Result := '';
  Start := PChar(SQL);
  CurSection := stUnknown;
  repeat
    SQLToken := NextSQLTokenEx(Start, Token, CurSection, IdOption);
    if SQLToken in SQLSections then CurSection := SQLToken;
  until SQLToken in [stEnd, stFrom];
  if SQLToken = stFrom then
  begin
    repeat
      SQLToken := NextSQLTokenEx(Start, Token, CurSection, IdOption);
      if SQLToken in SQLSections then
        CurSection := SQLToken else
      // stValue is returned if TableNames contain quote chars.
      if (SQLToken = stTableName) or (SQLToken = stValue) then
      begin
        if AnsiRightStr(Token, 1) = ';' then
          Token := AnsiLeftStr(Token, Token.Length-1);
        if AnsiRightStr(Token, 1) = ')' then
          Token := AnsiLeftStr(Token, Token.Length-1);
        Result := Trim(Token);
        while (Start[0] = '.') and not (SQLToken in [stEnd]) do
        begin
          SQLToken := NextSqlTokenEx(Start, Token, CurSection, IdOption);
          Result := Result + '.' + Token;
        end;
        Exit;
      end;
    until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]);
  end;
end;

p80286 8. Dez 2014 12:27

AW: Möglicher Fehler in GetTableName
 
Zitat:

Im Prinzip erwarte ich den (ersten) Tabellennamen des äußersten FROM
Nun da das Ergebnis Deiner Abfrage aus den verschiedensten Tabellen zusammen gesetz wurde/werden kann, halte ich Dein Ansehen für sehr vermessen.
Falls du unbedingt einen Tabellennamen brachst, dann gib ihn doch Deiner Abfrage mit
SQL-Code:
select'tabelle1',tabelle1.*,'tabelle2',tabelle2.*    etc.
Gruß
K-H

Dejan Vu 8. Dez 2014 12:42

AW: Möglicher Fehler in GetTableName
 
Ok, verstanden (dann wäre eine Antwort zwar nicht korrekt, aber unwichtig).

Man kann bei einer Typ-2-Grammatik keinen nichthierarchischen Parser verwenden. Oder einfacher ausgedrückt: Die Sprache ist rekursiv formuliert (Queries können Queries beinhalten, es gibt Klammerebenen), also muss man einen Satz der Sprache (das 'SQL-Statement') auch rekursiv analysieren. Es reicht natürlich ein Stack, aber der ist ja auch rekursiv.

Ob sich das Problem mit RegEx lösen lässt, weiß ich nicht. Rein theoretisch nicht, denn RegEx kann 'eigentlich' nur Typ-3 Grammatiken parsen, aber mit dem ganzen Lookahead-Gedöns und dem bilden von Gruppen wäre es theoretisch denkbar, obwohl... nee. geht nicht (meine Meinung :mrgreen:)

Jedenfalls geht es nicht so. Garantiert nicht. Um es richtig zu machen, musst du einen Parser schreiben, der die kompletten SELECT-Syntax implementiert und dann im aufgespannten Syntaxbaum beim 'äußeren' FROM-Knoten nachschauen: Dort hängen dann die JOIN-Operatoren mit den in Frage kommenden Tabellen. Allerdings ist es ja so, dass die Veränderung nicht über eine Tabelle läuft, sondern über viele, d.h. alleine schon das Funktionsresultat "der Tabellenname" ist falsch, es müsste "die Tabellennamen" heißen und pro Feld angegeben werden.

Tipp: SQL-Server kann direkt auf einer View Operationen (DELETE, INSERT, UPDATE) ausführen. Andere RDBMS können das vielleicht/vermutlich auch. Wenn als *das* dein Problem ist, packe deine Query in eine View und führe das Update einfach auf der View aus.

Ansonsten bin ich persönlich kein Verfechter von 'Ich verstehe die Query und baue Dir ein INSERT draus'. Das geht nämlich manchmal auch in die Hose. Besser ist es, die DML explizit auszuformulieren.

Aber vermutlich ist das eh nicht dein Problem.

Sir Rufo 8. Dez 2014 13:56

AW: Möglicher Fehler in GetTableName
 
Den ersten Tabellennamen vom äußersten FROM ...
SQL-Code:
select
  *
from
  ( select * from tab1 )
Es wird schwierig immer den richtigen Tabellennamen zu treffen und aufzulösen.

Darum ist es auch besser, die Statements eben nicht automatisch generieren zu lassen. Den Aufwand den man damit betreibt um auch wirklich alle Ausnahmen zu berücksichtigen schmeisst man wieder in die Tonne, wenn die nächste noch unberücksichtigte Ausnahme kommt. Irgendwann ist man es leid.

Da baue ich mir doch lieber ein Repository und arbeite mit Objekten, oder meinetwegen auch eine DataSet-Factory, die mir die passenden Abfrage-Instanzen liefert.

jobo 8. Dez 2014 14:15

AW: Möglicher Fehler in GetTableName
 
..lieber ein Repository..
Dazu noch der Hinweis, dass selbest das "richtige" finden des Tabellennamens u.U. nicht das Ende der Fahnenstange ist. Werden hier nur Spalten aus der Update Table selektiert, ist vielleicht alles im grünen Bereich, sind aber Werte aus anderen Tabellen in den Ausgabespalten dabei(soll ja vorkommen), dann kann man die nicht updaten, zumindest nicht auf Basis einer Update Table.

Im Repository würde man also nicht nur die Table, sondern vielleicht auch die Spalten mitverwalten.

Sir Rufo 8. Dez 2014 14:29

AW: Möglicher Fehler in GetTableName
 
Zitat:

Zitat von jobo (Beitrag 1282619)
..lieber ein Repository..
Dazu noch der Hinweis, dass selbest das "richtige" finden des Tabellennamens u.U. nicht das Ende der Fahnenstange ist. Werden hier nur Spalten aus der Update Table selektiert, ist vielleicht alles im grünen Bereich, sind aber Werte aus anderen Tabellen in den Ausgabespalten dabei(soll ja vorkommen), dann kann man die nicht updaten, zumindest nicht auf Basis einer Update Table.

Im Repository würde man also nicht nur die Table, sondern vielleicht auch die Spalten mitverwalten.

Wieso denn jetzt Spalten mitverwalten?

Meine Repositories sehen ungefähr so aus
Delphi-Quellcode:
type
  TFooId = type Integer;
  TFooIdHelper = record helper for TFooId
    const
      null = 0;
  end;

  TFoo = class
  public
    constructor Create( Id : TFooId; const Title : string );
    property Id : TFooId read Fid;
    property Title : string read FTitle;
  end;

  IFooRepository = interface
    function Get( FooId : TFooId; out Foo : TFoo ) : Boolean;
    function GetAll : TArray<TFooId>;
    function Store( Foo : TFoo; Dispose : Boolean = False ) : Boolean;
    function NextFooId : TFooId;
  end;
Das Repository weiß, wo die Informationen gespeichert werden und eben auch, wo es die Informationen wieder holen kann (da wo man auch speichert?). Das Repository hat dann x Abfrage-Objekte und leitet die Anforderungen durch diese hindurch.

Neutral General 8. Dez 2014 14:32

AW: Möglicher Fehler in GetTableName
 
Leute ihr driftet total ab.

Es ist jetzt total egal ob man sich irgendwelche Statements generieren lassen sollte oder nicht oder welche Alternativen es dazu gibt.

Die Frage ist: Ist das ein Fehler in der RTL?
Sollte diese Funktion in dem von mir genannten Fall nicht "TABELLE1" statt "TABELLE_INNEN" zurückgeben?

Alles andere ist vollkommen irrelevant.

Uwe Raabe 8. Dez 2014 15:10

AW: Möglicher Fehler in GetTableName
 
Zitat:

Zitat von Neutral General (Beitrag 1282626)
Die Frage ist: Ist das ein Fehler in der RTL?
Sollte diese Funktion in dem von mir genannten Fall nicht "TABELLE1" statt "TABELLE_INNEN" zurückgeben?

Gibt es denn irgendwo eine Beschreibung, was die Funktion GetTableNameFromSQLEx bei bestimmten Eingabeparametern zurückliefern soll? Eine vollständige semantische Analyse des SQL-Statements kann man da wohl eher nicht erwarten.

Bei FireDAC ist das zumindest so gelöst, daß man über UpdateOptions.UpdateTableName den Namen angeben kann und sogar muss, wenn "FireDAC den Namen der zu aktualisierenden Tabelle nicht korrekt aus der Abfrage abrufen kann." Dabei ist es ziemlich unerheblich, warum FireDAC das nicht kann, weil der entsprechende Algorithmus vielleicht nur für simple SELECT-Anweisungen funktioniert. Ich habe jetzt die erwähnten DA-Komponenten nicht greifbar, aber vielleicht gibt es da ja einen ähnlichen Mechanismus.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:57 Uhr.
Seite 1 von 2  1 2      

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