![]() |
Datenbank: MS SQL • Version: 2005 • Zugriff über: FireDAC
Datenbank-Metainformationen abfragen schlägt fehl
Hallo Gemeinde,
vorab: ich bin in Sachen Datenbanken relativ unbeleckt. Ich will von einer Datenbank die Tabellennamen und die Feldinformationen per FireDAC abfragen und als XML-Schema abspeichern. Delphi-Version ist Seattle Enterprise mit Update 1. Die Datenbank läuft auf einen MS SQL Server 2005. Sie hat 96 Tabellen mit verschiedenen vielen Feldern. Ich befolge den Tipp von Uwe Raabe ( ![]() Jedoch fliegt mir nach ca. fünf bis sechs geholten Tabellen und abgespeicherten XML-Schemta eine EMSSQLNativeException um die Ohren und ich weiß gerade nicht weiter. Weiß jemand Rat? Es folgt der Quelltext, Exception und Callstack:
Delphi-Quellcode:
procedure TDbStructureExporter.SaveDatabaseDetails(
const ACatalogName: string; const ASchemaName: string = ''; const APattern: string = ''); var Con: TFDCustomConnection; TableList: TStringList; TableName, FieldName: string; begin Con := FConnectionWrapper.Connecton; TableList := TStringList.Create; try Con.GetTableNames(ACatalogName, ASchemaName, APattern, TableList); for TableName in TableList do begin ExportTable(TableName); end; finally TableList.Free; end; end; procedure TDbStructureExporter.ExportTable(const ATableName: string); var LConnection: TFDCustomConnection; LTable: TFDTable; LTableName: string; begin CoInitialize(nil); try LTableName := ATableName; LConnection := TFDCustomConnection.Create(nil); LConnection.ConnectionDefName := cConnectionDef; LConnection.Connected := True; try LTable := TFDTable.Create(nil); LTable.Connection := LConnection; try LTable.Open(LTableName); // <--- hier knallts dann! LTable.FieldDefs.Update; if LTable.FieldDefs.Updated then LTable.SaveToFile('C:\Temp\' + LTableName + '.xml', TFDStorageFormat.sfXML); finally LTable.Free; end; LConnection.Connected := False; finally LConnection.Free; end; finally CoUninitialize; end; end;
Code:
Project DbStructureExportVCL.exe raised exception class EMSSQLNativeException with message '[FireDAC][Phys][ODBC][Microsoft][ODBC SQL Server Driver]Die Verbindung ist mit Ergebnissen von einem anderen hstmt belegt'.
:74e3dad8 KERNELBASE.RaiseException + 0x48 FireDAC.Stan.Error.FDException(???,???,???) FireDAC.Phys.ODBCWrapper.TODBCHandle.ProcessError(???) FireDAC.Phys.ODBCWrapper.TODBCHandle.Check(???) FireDAC.Phys.ODBCWrapper.TODBCMetaInfoStatement.Execute FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.OpenMetaInfo FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.InternalOpen FireDAC.Phys.TFDPhysCommand.OpenBase FireDAC.Phys.TFDPhysCommandAsyncOpen.Execute FireDAC.Stan.Async.TFDStanAsyncExecutor.ExecuteOperation(False) FireDAC.Stan.Async.TFDStanAsyncExecutor.Run FireDAC.Phys.TFDPhysCommand.ExecuteTask(TFDPhysCommandAsyncOpen($3277210) as IFDStanAsyncOperation,nil,False) FireDAC.Phys.TFDPhysCommand.Open(False) FireDAC.Phys.Meta.TFDPhysConnectionMetadata.FetchToCache(mkPrimaryKey,'MyDatabaseName','dbo','','MyTableName',0,$31B8CB0) FireDAC.Phys.Meta.TFDPhysConnectionMetadata.GetTablePrimaryKey('MyDatabaseName','dbo','MyTableName') FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.OpenMetaInfo FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.InternalOpen FireDAC.Phys.TFDPhysCommand.OpenBase FireDAC.Phys.TFDPhysCommandAsyncOpen.Execute FireDAC.Stan.Async.TFDStanAsyncExecutor.ExecuteOperation(False) FireDAC.Stan.Async.TFDStanAsyncExecutor.Run FireDAC.Phys.TFDPhysCommand.ExecuteTask(TFDPhysCommandAsyncOpen($3277050) as IFDStanAsyncOperation,nil,False) FireDAC.Phys.TFDPhysCommand.Open(False) FireDAC.Phys.Meta.TFDPhysConnectionMetadata.FetchToCache(mkIndexes,'','','','MyTableName',0,$31B8BC0) FireDAC.Phys.Meta.TFDPhysConnectionMetadata.GetTableIndexes('','','MyTableName','') FireDAC.Comp.Client.TFDTable.UpdateIndexDefs Data.DB.TDefCollection.UpdateDefs((FireDAC.Comp.Client.TFDTable.UpdateIndexDefs,$320EB70)) Data.DB.TIndexDefs.Update FireDAC.Comp.DataSet.TFDDataSet.OpenIndexes FireDAC.Comp.DataSet.TFDDataSet.InternalOpen Data.DB.TDataSet.DoInternalOpen Data.DB.TDataSet.OpenCursor(???) FireDAC.Comp.DataSet.TFDDataSet.OpenCursor(False) FireDAC.Comp.Client.TFDRdbmsDataSet.OpenCursor(False) FireDAC.Comp.Client.TFDTable.OpenCursor(False) Data.DB.TDataSet.SetActive(???) FireDAC.Comp.DataSet.TFDDataSet.SetActive(???) Data.DB.TDataSet.Open FireDAC.Comp.Client.TFDTable.Open(???) DbStructureExporter.TDbStructureExporter.ExportTable('MyTableName') DbStructureExporter.TDbStructureExporter.SaveDatabaseDetails('MyDatabaseName','dbo','') DbStructureExporter.View.TForm1.Button1Click($31FC9A0) |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Ich gehe davon aus, dass du einfach zu viele Connections auf die Datenbank hast. Nimm doch einfach immer ein und dieselbe Connection, statt jedesmal eine neue zu machen.
|
AW: Datenbank-Metainformationen abfragen schlägt fehl
Also wenn ich mich da nicht verkuckt habe hat er nur zwei Connections offen.
Eine für die Tabellennamen und eine um die Feldnamen zu holen, die wird aber ordentlich geschlossen nach jeder Abfrage. Vielleicht wäre es sinnvoll zuerst die Tabellennamen zu holen, und dann die Feldnamen? Gruß K-H |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Was ich meinte war: Für jede Tabelle wird eine Connection erzeugt, geöffnet, geschlossen und weggeworfen, keine Ahnung ob der Server das alles so schnell mitbekommt, zudem das unnötig Zeit vergeudet.
|
AW: Datenbank-Metainformationen abfragen schlägt fehl
Also diesen Teil würde ich erstmal ändern:
Delphi-Quellcode:
in etwa so:
try
LTable.Open(LTableName); // <--- hier knallts dann! LTable.FieldDefs.Update; if LTable.FieldDefs.Updated then LTable.SaveToFile('C:\Temp\' + LTableName + '.xml', TFDStorageFormat.sfXML); finally LTable.Free; end;
Delphi-Quellcode:
Und dann schauen wir erstmal, bei welcher Tabelle es kracht.
try
try LTable.Open(LTableName); LTable.FieldDefs.Update; if LTable.FieldDefs.Updated then LTable.SaveToFile('C:\Temp\' + LTableName + '.xml', TFDStorageFormat.sfXML); except // Die Fehlermeldungen sinnvollerweise irgendwie mitloggen, um dann ggfls. gezielter suchen zu können. on e : Exception do ShowMessage(LTableName + #13 + e.Message); end; finally LTable.Free; end; Meine Erfahrung ist, dass das Auslesen von Tabellendefinitionen nicht immer und unbegrenzt bei allen Datenbank funktioniert. Insbesondere bei Systemtabellen kann es schonmal krachen. Die meisten Datenbanken haben Views, mit denen man an die Tabellendefinitionen ... kommen kann. Beim SQL-Server ist es das ![]() Sofern Dein Programm nicht mit beliebigen Datenbanken zurechtkommen muss, sondern nur mit SQL-Server würd' ich, wenn alle Stricke reißen, dort suchen. Sinngemäß in etwa so:
Delphi-Quellcode:
Welche Tabelleninformationen zur Verfügung stehen, erfährt man hier unter
qry.SQL := Format('SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = %s',[QuotedStr(LTableName)]); ![]() Das Information-Schema gehört zum Ansi-Standard ( ![]() Ist jetzt nur hingedaddelt und nicht getestet. Wie Baumina schon anmerkt, würde ich Con mit an Exporttable übergeben und dann LTable zuweisen. Für jede Tabelle eine eigene Verbindung erstellen, aufmachen, Infos holen, Verbindung wegwerfen, halte ich nicht für wirklich sinnvoll. |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Zitat:
Das Connect und Disconnect gehört IMHO in die andere Procedure (SaveDatabaseDetails). Frank |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Selbst wenn ich ein- und dieselbe TFDCustomConnection-Instanz verwende, erhalte ich regelmäßig beim Versuch die sechste von 96 Tabellen zu öffnen die oben genannte Exception.
Delphi-Quellcode:
procedure TDbStructureExporter.SaveDatabaseDetails(
const ACatalogName: string; const ASchemaName: string = ''; const APattern: string = ''); var TableList: TStringList; TableName: string; begin TableList := TStringList.Create; try FConnectionWrapper.Connecton.GetTableNames(ACatalogName, ASchemaName, APattern, TableList); CoInitialize(nil); for TableName in TableList do begin ExportTable(TableName); end; CoUninitialize; finally TableList.Free; end; end; procedure TDbStructureExporter.ExportTable(const ATableName: string); var LTable: TFDTable; LTableName: string; begin LTableName := ATableName; LTable := TFDTable.Create(nil); LTable.Connection := FConnectionWrapper.Connecton; try if LTable.Connection.Connected then begin LTable.Open(LTableName); // <--- hier knallts dann! LTable.FieldDefs.Update; if LTable.FieldDefs.Updated then LTable.SaveToFile('C:\Temp\' + LTableName + '.xml', TFDStorageFormat.sfXML); end; finally LTable.Free; end; end; |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Wie lautet der Name dieser Tabelle? Name mit Leerzeichen/Sonderzeichen? Systemtabelle?
|
AW: Datenbank-Metainformationen abfragen schlägt fehl
Und was ist an der sechsten Tabelle anders als an den anderen?
Wenn Du die sechste Tabelle überspringst (Hat sie einen Namen? Was für 'ne Tabelle ist das denn?), kommst Du dann bis zur 96. Tabelle? Oder kommen dann noch weitere Fehler? Die beiden würd' ich nicht "alle Nase lang" aufrufen, sondern nur einmalig:
Delphi-Quellcode:
Oder wäre das nicht ausreichend?
Unit irgendwas;
... initialization CoInitialize(nil); finalization CoUninitialize; end. |
AW: Datenbank-Metainformationen abfragen schlägt fehl
Die sechste Tabelle habe ich schon durch skippen (if TableName = 'GenauDieseTabelle' then exit) ausgeschlossen.
Bei den darauffolgenden Tabellen passiert der Fehler auch und das sind meiner Meinung nach alles normale Tabellen ohne große Besonderheiten. Die Tabelle heißt 'ItemEntries' und hat 60 Felder. Vielen Dank aber bis hierhin für eure Mühe, aber ihr braucht euch darüber jetzt nicht weiter den Kopf zerbrechen. Ich habe das mit der Table sein gelassen und eine Query mit SQL '...where 1=2' verwendet (siehe Quelltext unten). Damit habe ich mein Ziel erfüllt. Falls noch jemand einen eleganteren Weg hat, nur her damit! PS: Das mit den Co(Un)Initialize deswegen, falls das in einen eigenen Threadkontext ausgeführt wird. Ich hatte nämlich versuchsweise jede Tabellenabfrage in einen eigenen Task und das schreiben der XML per MSXML ging dann nicht mehr.
Delphi-Quellcode:
procedure TDbStructureExporter.SaveDatabaseDetails(
const ACatalogName: string; const ASchemaName: string = ''; const APattern: string = ''); var TableList: TStringList; TableName: string; begin TableList := TStringList.Create; try FConnectionWrapper.Connecton.GetTableNames(ACatalogName, ASchemaName, APattern, TableList); CoInitialize(nil); try for TableName in TableList do begin ExportTable(TableName); end; finally CoUninitialize; end; finally TableList.Free; end; end; procedure TDbStructureExporter.ExportTable(const ATableName: string); var LQuery: TFDQuery; begin LQuery := TFDQuery.Create(nil); try LQuery.Connection := FConnectionWrapper.Connecton; LQuery.SQL.Clear; LQuery.SQL.Add('select * from ' + ATableName + ' where 1=2'); LQuery.Active := True; LQuery.SaveToFile('C:\Temp\' + ATableName + '.xml', TFDStorageFormat.sfXML); finally LQuery.Free; end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 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