Verwendete Datenbanksysteme: Delphi mit dBase, C# mit MS SQL 2008 Express
Hallo,
Ich habe eine MS SQL Datenbank(Dienstbasierte Datenbank in C# 2008 erstellt) mit einer Tabelle mit ca. 5 Spalten mit 1.000.000 Datensätzen befüllen lassen. Wenn ich nun die Form starte in der sich ein BindingNavigator befindet, benötigt der Table Adapter ca. 30 Sekunden das Abbild der Tabelle in den Speicher zu laden. Das kann man dem Benutzer natürlich nicht zumuten.
Mein Vater programmiert Datenbankanwendungen in Delphi und dBase(Für die Datenbanken). In .Net wird von der verwendeten Tabelle - soweit ich das richtig verstanden habe - ein Abbild in den Hauptspeicher geladen. In Delphi dagegen wird auf der Platte selbst in die Datenbank eingesehen und dies funktioniert bei Delphi auch mit 1.000.000 noch gut und keine Ladezeiten. Dort gibt es keine Binding Source, man kann aber eine sogenannte TTable erzeugen die ihre Quelle hat und mit Next zum Beispiel den nächsten Datensatz auswählen. Ein DBEdit zeigt dort dann den speziellen Wert der Spalte in diesem Index an, somit also das Gegenstück einer TextBox die an eine BindingSource gekoppelt ist. Somit kommt dies dem BindingNavigator sehr nahe.
Ich wollte meinen Vater von C# überzeugen. Für mich ist Delphi eine aussterbende Sprache. Das was man in Delphi kann, geht auch in C# und C# kann noch viel mehr. Desweiteren muss man bei Delphi alle Versionen teuer bezahlen, C# bietet eine kostenlose alternative. Doch damit er das Potenzial von C# erkennt, müsste ich ihm zeigen, dass das was bei ihm in Delphi geht auch in C# geht und zwar genau so schnell. Mit diesen langen Ladezeiten geht das natürlich nicht.
Daher meine Frage: Ist es auch möglich in C# während der Laufzeit in der Datenbank zu wühlen, während man ein BindingNavigator benutzt? Wenn der BindingNavigator die Position ändert sollte das Programm den nächsten Datensatz also von der Platte lesen. Ist dies so wie in Delphi möglich?
Hallo FastGarrett,
das ist meines Erachtens kein Problem zwischen Delphi und .Net (naja ein bisschen vielleicht doch). Ich muss hier mal etwas mehr ausholen:
Betrachten wir zunächst die Datenbank:
Unterschiede sind hier in Folgenden Bereichen deutlich zu spüren:
Versuche mal mit einer Dateiendatenbank im Multi-User und Multi-Threaded Umfeld zu verwenden. Da kommen Dateidatenbanken schnell an Ihre Grenzen. Ebenfalls werden Dateidatenbanken bei Tabellenverknüpfungen und Filterungen deutlich langsamer. Das sollte im SQL Server erheblich schneller gehen.
Betrachtung der Programmierplattform:
Delphi arbeitet wohl Deiner Aussage nach mit Recordsets, .NET bietet hier eine weitere Abstraktion, das ADO.NET. ADO.NET liest Datenbankabhängig zunächst die Struktur der Datenbank aus (Abfragebezogen) und generiert im Speicher die so genannten DataSets, die wiederum aus DataTables und diese dann DataRows beinhalten. Ein direkter Zugriff auf die Daten ist mittels ADO.NET nicht möglich. Somit ist auch das Arbeiten mit RecordSets nicht möglich. Dafür garantiert diese Arbeitsweise transaktionale Sicherheit und eine Abstraktion der Daten (sollte damit auch flexibler in der Anbindung verschiedenster Datenquellen sein).
Wie bekommt man in Deinem Anwendungsfall eine Performanceverbesserung hin?
Es gibt mehrere Möglichkeiten. Du könntest z.B. auf ADO.NET verzichten und noch die veraltete Technik DAO verwenden, nur weiß ich überhaupt nicht, ob das mit dem SQL Server funktioniert und ich würde solch eine Technik nicht mehr einsetzen. Man kauft sich durch das Recordbasierte Arbeiten zu viele Nachteile ein.
Besser ist es eine Intelligenz bei der Dateninitialisierung einzubauen. 1 Mio Datensätze kann kein Mensch überblicken und schon gar nicht gleichzeitig darstellen. Deshalb ist mein Vorschlag immer nur die Datensätze zu holen, die einmal direkt sichtbar werden, und schon als Vorbereitung die, die per Useraktion als nächstes angezeigt werden können (z.B. die nächsten 10, die vorigen 10, die allerersten 10 und die allerletzten 10 Datensätze) [hängt von der UI Gestaltung ab)
Du könntest hier z.B. für die Performanceunterschiede folgendes ausprobieren:
Versuche mal mit Delphi und DBase einen Filter laufen zu lassen (auf die 1 Mio Datensätze). Sagen wir mal es gibt eine Spalte "Nachname". Du möchtest jetzt alle Datensätze sehen, die mit "ler" enden, wie z.B. "Mueller" oder "Keller" etc. So etwas solltest Du dann mal vergleichen mit SQL Server und ADO.NET.
Ansonsten kann man Dir hier auch sicherlich konkret in einzelnen Performanceoptimierungen zur Seite stehen.
Viele Grüße
Norman-Timo
Edit: Zu früh am Morgen, deshalb Grammatik- und Rechtschreibfehler
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Danke zunächst für diese Ausgezeichnete Ausführung. Habe es nun so probiert, wie du sagtest, habe mit SQL beauftragt von den 1.000.000 Datensätzen nur 10 auszusuchen (ganz einfach mit "WHERE ID > 10 AND ID < 20" ID ist in meinem Fall der Primärschlüssel.)
Das geht schon um einiges Schneller, sind aber immerhin noch 5 Sekunden, eigentlich aber schon sehr erstaunlich für mich das er so schnell 1.000.000 Datensätze durchforstet. Doch für meinen Vater wird das nicht reichen. Hmm, am besten lasse ich mir das mal von meinem Vater vorführen, mit 1.000.000 Datensätzen. Wenn ich keine Lösung finde, hat mein Vater gewonnen und er vertieft sich weiter in (Commerz) Delphi.^^
Hallo FastGarret
Der Vergleich einer DataTable mit einem Delphi RecordSet sind Äpfel und Birnen. Zusätzlich zu der von Norman-Timo bereits angebrachten Zwischenschicht über den DataReader basiert das RecordSet auf einem Array (oder einer Liste), die DataTable jedoch auf einem RedBlackTree und ermöglicht massiv optimierte Abfragen im Speicher (wobei Norman-Timo recht hat, 1,000,000 Zeilen will kein User sehen 😉 ).
Dass deine SQL Abfrage von 10 Datensätzen über den Primärschlüssel bei gerade mal 1,000,000 in der Datenbank (das ist für SQL Server nichts) 5 Sekunden dauert kann ich mir irgendwie nicht vorstellen. Hast du die Datenbank auf eine Floppy-Disk ausgelagert? 😁
Ich arbeite hier mit deutlich mehr als 1/2 Milliarde Datensätzen und habe Zugriffszeiten von knapp 9 Millisekunden.
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Kleine Ergänzung: Das ConnectionPooling sorgt dafür, dass der zweite, dritte usw. Zugriff (genauer: das Connection.Open) erheblich schneller gehen als der erste. Jürgen
//sprachlicher Fehler beseitigt
Wirklich so schnell Florian Reischl?
Wie machst du das. Also ich habe in C# 2008 eine Dienstbasierte Datenbank erstellt, also mit dem Wizzard. Soweit ich weis läuft das dann über den MS SQL Server 2008 Express, möglicherweise auch über den MS SQL Campact Server, das weiß ich nicht genau. Die Datenbank befindet sich natürlich auf meiner lokalen Festplatte. Es ist ein ganz npormaler laptop mit dual Core 2GHz und 2 GB Ram. Wie bekommst du das hin das es so schnell bei dir geht? Kannst du mir ein Beispiel geben, wie man so etwas so schnell bekommt, bzw. was mache ich anders?^^
Hallo FastGarrett,
30 Sekunden ist viel zu viel, passt nicht zu Deinem Namen !
In meiner Applikation zeige ich 1 Mio. Datensätze an, das dauert ca. 1 - 2 Sekunden. Die Tabelle hat ca. 200 Spalten.
Ich lese mit Datareader, das ist schneller als DataTable. Ich arbeite mit einem virtuellen Listview, deswegen lese ich effektiv nur 30 Datensätze (und ein reccount). Die restlichen 999970 Datensätze lese ich heimlich im Hintergrund 😉
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Kannst du mir ein Beispiel geben
Jupp 😃
Lege mal im SQL Server Management Studio eine Test-Datenbank an. (Bei mir gibt's immer eine "Sandbox"). Öffne ein Query-Fenster auf der Datenbank und führe folgendes Skript aus. Damit wird eine Tabelle TestData1M angelegt und 1.000.000 Testdatensätze eingefügt. Ich hab's mal ein bisschen kommentiert.
Am Ende wird beispielhaft ein SELECT ausgeführt und die SQL Server eigenen Zeitstatistiken ausgegeben.
--DROP TABLE TestData1M
SET NOCOUNT ON;
GO
---============================================================================
-- create table
IF (OBJECT_ID('TestData1M') IS NULL)
BEGIN
PRINT ('------------------------------------------------');
PRINT ('Creating table');
CREATE TABLE TestData1M
(
Id INT IDENTITY(1,1)
PRIMARY KEY CLUSTERED
,SomeInt INT
,SomeDate DATETIME
);
PRINT ('Table created');
END;
GO
---============================================================================
-- insert some test data
PRINT ('------------------------------------------------');
PRINT ('Insert some test data');
-- some variables
DECLARE
@count INT
,@i INT
,@batchSize INT;
-- configure the count of desired rows and the batch size to create the rows
SELECT
@count = 1000000
,@batchSize = 10000;
-- get the current count of rows
SELECT @i = COUNT(*)
FROM TestData1M;
-- create the rows
WHILE (@i < @count)
BEGIN
-- we need some numbers
WITH Numbers (Num) AS
(
SELECT TOP(@batchSize)
ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM master.sys.all_columns c1
CROSS JOIN master.sys.all_columns c2
)
-- use the numbers to create the test data
INSERT INTO TestData1M (
SomeInt
,SomeDate
)
SELECT TOP(@batchSize)
Num
,DATEADD(HOUR, Num, GETDATE())
FROM Numbers;
-- add the count of created rows to the complete count
SELECT @i = @i + @batchSize;
END;
SELECT @count = COUNT(*)
FROM TestData1M;
PRINT ('Test table contains ' + CONVERT(VARCHAR(10), @count) + ' rows');
GO
---============================================================================
-- sample select
PRINT ('------------------------------------------------');
PRINT ('sample select (with time statistics)');
DECLARE @someDate DATETIME;
-- activate time stats
SET STATISTICS TIME ON;
-- select a date for a specified Id (PK)
SELECT @someDate = SomeDate
FROM TestData1M WHERE Id = 600000;
-- deactivate time stats
SET STATISTICS TIME OFF;
PRINT ('');
PRINT ('We found date "' + CONVERT(VARCHAR(40), @someDate, 121) + '"');
Wenn du das Skript ausgeführt hast kannst du aus einer C# Konsolenapplikation folgendes Beispiel ausführen. Das ist jetzt nicht die schnellste Variante mit C#, aber die Performance dürfte als Beispiel durchaus ausreichen 😉:
// ---> CONFIGURE YOUR DATABASE <---
string database = "Sandbox";
string cnStr = string.Format("Server=(local);Database={0};Integrated Security=sspi;", database);
// create a connection, command and an adapter for the DataTable
using (SqlConnection cn = new SqlConnection(cnStr))
using (SqlCommand cmd = new SqlCommand("SELECT * FROM TestData1M WHERE Id BETWEEN 100000 AND 100010", cn))
using (SqlDataAdapter adap = new SqlDataAdapter(cmd))
{
cn.Open();
// destination table
DataTable table = new DataTable();
// start timing
Stopwatch watch = Stopwatch.StartNew();
// get the data from SQL Server
adap.Fill(table);
// stop timing and show client duration
watch.Stop();
Console.WriteLine("Duration (ms): {0}", watch.ElapsedMilliseconds);
// show sample output
Console.WriteLine();
Console.WriteLine("Result data");
foreach (DataRow row in table.Rows)
{
Console.WriteLine("Date: {0}", row["SomeDate"]);
}
}
Die Dauer dürfte bei nur 1.000.000 Zeilen deutlich unter 10 Millisekunden liegen. 😉
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Riesen Dank für dieses enorme Beispiel. Es übertrifft meine Erwartungen um einiges. Gerne möchte ich es ausprobieren, habe aber ein kleines Problem. Ich habe die gesamte Installation mit allen Services von dem MS SQL Server 2008 Express abgeschlossen und finde doch Partout nicht das Management Studio.
Kann mir jemand sagen, in welchem Verzeichnis genau ich dieses finde? Habe habe den Installationspfad von MS SQL 2008 nach allen .exe Dateien durchsucht, doch kein Erfolg. In der Schnellstartleiste findet sich nur die Option Daten zu Im- oder Exportieren und um Konfigurationen festzustellen.
Habe ich vielleicht die Installation vergeigt? Habe extra noch mal alles was mit SQL Server 2008 gehört runter geschmissen und die komplette Installation gewählt.
[...] und die komplette Installation gewählt.
Aha - dann hast du vermutlich "nur " den MS SQL Server 2008 Express Edition installiert.
Was du brauchst, nennt sich "Microsoft SQL Server 2008 Express Edition with Advanced Services" bzw. "... with Tools".
Ist auch beides kostenlos, bringt aber das Managementstudio Express mit; ersterer auch Volltextindices.