Fragensteller
Sql Abfrage nicht immer extrem langsam!

Frage
-
Hallo
Ich habe ein kleines (großes Problem) mit linq mit sql Compact Server 3.5
Ich habe eine datebank mit Beziehungen erstellt und habe
mit den Linq-Vb code durch SqlMetal erstellen lassen.
(wenn ich diesen vb code im Vs einbinde erhalte ich viele Fehlermeldungen von falschen deklarationen usw. also habe ich es wieder im vs ausgeblendet weiß nicht ob das eine Rolle spielt??!!!, Aber Linq funktioniert grundsätzlich!)
Dann habe ich folgenden code probiert und da habe ich das Problem
Das ich beim ersten Durchlauf circa 7 Sekunden brauche,
und beim 2 durchlauf keine spürbare verzögerung habe.
Meine Datenbank besteht circa aus 30 AlarmDatensätzen und circa 2 Alarme haben circa 2 SendItems,
also kann mann nicht wirklich von einem Datenvolumen sprechen.
Was kann ich hier Falsch machen.
Die zweite Schleife habe ich auch mal durch eine 2 Linq abfrage erstellen lassen da dauert aber
jeder Testversuch circa 7 sec.
Ich habe auch mal kurz ein Tool zum zeit messen verwendet und bin glaube zum schluss gekommen
dass die zeit in der ersten Schleife draufgeht als sie den enumerator holt (bin nicht sicher).
(Und nur die erste Schleife alleine läuft super schnell nur in Verbindung mit der zweiten gibs probleme)
Dim AlarmsList = From c In _db.Alarms Select c
For Each MyAlarm In AlarmsList
' Dim SendItems = From v In _db.SendItems Select v
For Each MySendItem In MyAlarm.SendItems
MsgBox("1")
Next
Next
Windows 7
Visual Studio 2008
danke für Hilfe
Alle Antworten
-
Hallo,
was die Fehlermeldungen angeht:
Welche sind das und um sie beurteilen zu können müsste man den Code (ausschnittsweise) sehen.
Oder noch besser, die Datenbankdefinition haben und die Anweisung mit der Du den Code generiert hast.
Was die Ausführung angeht:
Das mit dem Enumerator kommt schon hin.
Linq führt die Abfrage erst wenn das Ergebnis erzeugt wird, und das ist in der Regel der Enumerator.
Und die erste Abfrage ist schon deswegen langsamer, weil damit zum ersten Male die Datenbank
angesprochen wird, denn die Erzeugung der SQL Anweisungen erfolgt noch aus den Metadaten.
Bei geschachtelten Abfragen kann die LoadWith Option die Leistung verbessern.
Wobei mir die 7 Sekunden (auch für Compact) schon ziemlich happig vorkommen.
(Beim Testen solltest Du allerdings MsgBox durch ein Console.WriteLine oder ähnliches ersetzen)
Gruß Elmar -
Hallo
Habe hier Screenshoots und Code mit Datenbank uploaded
http://www.gigasize.com/get.php?d=vwwfbpoyhpf
Das mit LoadWith werde ich noch testen kann aber fast nicht der grund sein.
Achja habe das erste mal eine SQL Compact Datenbank erstellt, vieleicht habe ich irgent einen Typische fehler gemacht -
Hallo,
günstiger wäre ein vollständiges Projekt gewesen.
So bin ich erst heute dazu kommen, weil ich es erst einiges zusammenflicken mußte.
Zunächst zum Problem mit den Fehlermeldungen:
Das dürfte daraus resultieren das Du Dir sowohl eine Dbml wie eine Klasse generieren läßt.
Visual Studio hilft dabei unbeabsichtigt aus und stellt für die Dbml-Datei eine Codegenerierung ein.
Dadurch erhälst Du die Klassen zweimal.
Es reicht in Deinem Falle die eine Zeile mit:
SqlMetal.exe DBAlerts.sdf /language:vb /code:DBAlerts.vb /namespace:SQLCompact /pluralize
dadurch verwendet Linq2Sql die Attribute für das Mapping.
Oder entferne den Codegenerator in den Dateieigenschaften.
Wenn Du ein externes Mapping verwenden willst, müsstest Du etwas verwenden wie:
SqlMetal.exe DBAlerts.sdf /language:vb /code:DBAlerts.vb /map:DBAlerts.map /namespace:SQLCompact /pluralize
Die Mapping Datei sollte dann auf "Eingebettete Ressource" eingegestellt werden.
Siehe auch http://davidhayden.com/blog/dave/archive/2006/05/18/2947.aspx
(unten im Code habe ich eine VB-Fassung eingebaut).
Zur Geschwindigkeit:
Ich konnte da nichts ungewöhnliches feststellen. Mit unten stehendem Code
ergab sich als Zeitverlauf (auf einem Q9300) bei drei Durcläufen:
- Loop 1: 1123 ms
- Loop 2: 1351 ms
- Loop 1: 8 ms
- Loop 2: 1370 ms
- Loop 1: 7 ms
- Loop 2: 1370 ms
das Mapping (Attribute oder Xml) angewendet werden muß.
Die zweite Schleife ist erwartungsgemäß langsamer, da sie jedesmal
eine neue Abfrage auslöst.
Gruß Elmar
Der Testcode:
Imports SQLCompact Imports System.IO Imports System.Data.Linq.Mapping Public Class Form1 Private _db As SQLCompact.DBAlerts Public Sub New() InitializeComponent() ' DataContext während Laufzeit erhalten IntializeDb() End Sub Private Sub IntializeDb() _db = New SQLCompact.DBAlerts(My.Settings.DBAlertsConnectionString) End Sub ' Für eine Variante mit XmlMappingSource Private Sub InitializeDbWithMapping() Dim mappingSource As XmlMappingSource Const mappingSourceName As String = "DBAlerts.map" Using mappingStream As Stream = Me.GetType().Assembly.GetManifestResourceStream(mappingSourceName) mappingSource = XmlMappingSource.FromStream(mappingStream) End Using _db = New SQLCompact.DBAlerts(My.Settings.DBAlertsConnectionString, mappingSource) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim AlarmsList = From c In _db.Alarms Select c Dim sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList For Each MySendItem In MyAlarm.SendItems Console.WriteLine("Item 1: {0} - {1}", MySendItem.IDSendItem, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 1: {0} ms", sw.ElapsedMilliseconds) sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList Dim idAlert = MyAlarm.IDAlert Dim SendItems = From v In _db.SendItems Where v.IDAlert = idAlert Select v For Each MySendItem In SendItems Console.WriteLine("Item 2: {0} - {1}", MySendItem.IDSendItem, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 2: {0} ms", sw.ElapsedMilliseconds) Console.WriteLine() End Sub End Class
-
Hallo
1000 Dank für deine Hilfe, habe das mit der doppelten Class gelösst.
Mit der ersten Möglichkeit.
Habe aber das mit der lese Zeit Problem immer noch nicht gelöst genau mit diesen
CodePrivate _db As SQLCompact.DBAlerts Public Sub New() InitializeComponent() ' DataContext während Laufzeit erhalten IntializeDb() End Sub Private Sub IntializeDb() _db = New SQLCompact.DBAlerts(My.Settings.DBAlertsConnectionString) End Sub '' Für eine Variante mit XmlMappingSource 'Private Sub InitializeDbWithMapping() ' Dim mappingSource As XmlMappingSource ' Const mappingSourceName As String = "DBAlerts.map" ' Using mappingStream As Stream = Me.GetType().Assembly.GetManifestResourceStream(mappingSourceName) ' mappingSource = XmlMappingSource.FromStream(mappingStream) ' End Using ' _db = New SQLCompact.DBAlerts(My.Settings.DBAlertsConnectionString, mappingSource) 'End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim AlarmsList = From c In _db.Alarms Select c Dim sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList For Each MySendItem In MyAlarm.SendItems Console.WriteLine("Item 1: {0} - {1}", MySendItem.IDSendItem, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 1: {0} ms", sw.ElapsedMilliseconds) sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList Dim idAlert = MyAlarm.IDAlert Dim SendItems = From v In _db.SendItems Where v.IDAlert = idAlert Select v For Each MySendItem In SendItems Console.WriteLine("Item 2: {0} - {1}", MySendItem.IDSendItem, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 2: {0} ms", sw.ElapsedMilliseconds) Console.WriteLine() End Sub
und habe diese Werte damit ermittelt P4 3GHz HT
- Item 1: 40 - 57
- Item 1: 39 - 58
- Item 1: 47 - 152
- Item 1: 48 - 153
- Item 1: 46 - 154
- Loop 1: 7044 ms
- Item 2: 40 - 57
- Item 2: 39 - 58
- Item 2: 47 - 152
- Item 2: 48 - 153
- Item 2: 46 - 154
- Loop 2: 7692 ms
- Item 1: 40 - 57
- Item 1: 39 - 58
- Item 1: 47 - 152
- Item 1: 48 - 153
- Item 1: 46 - 154
- Loop 1: 33 ms
- Item 2: 40 - 57
- Item 2: 39 - 58
- Item 2: 47 - 152
- Item 2: 48 - 153
- Item 2: 46 - 154
- Loop 2: 7760 ms
- Item 1: 40 - 57
- Item 1: 39 - 58
- Item 1: 47 - 152
- Item 1: 48 - 153
- Item 1: 46 - 154
- Loop 1: 33 ms
- Item 2: 40 - 57
- Item 2: 39 - 58
- Item 2: 47 - 152
- Item 2: 48 - 153
- Item 2: 46 - 154
- Loop 2: 7723 ms
Jetzt weiß ich nicht mehr weiter, ich habe aber ein frisch installiertes Windows 7.
Irgenteine Idea? -
Ich habe nochmals etwas brobiert mit dem Code unten
und habe alle Beziehungen von Tabellen zu Alarm gelöscht
und habe mit Kopieren 168 Datenbunkte erstellt und habe diese Werte daraus bekommen
- Item int: 274ms - 57
- Item int: 330ms - 58
- Loop 2: 7344 ms
und mit keiner internen Schleife-Abfrage habe ich diese Werte- Loop 2: 292 ms
- Loop 2: 52 ms
- Loop 2: 36 ms
jetzt stellt sich mir die Frage ob Sql compact so langsam ist?
Denn eine Abfrage ca 52ms dauert * 168 einträge sind 8,4sec.
sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList Dim idAlert = MyAlarm.IDAlert Dim SendItems = From v In _db.SendItems Where v.IDAlert = idAlert Select v For Each MySendItem In SendItems Console.WriteLine("Item int: {0}ms - {1}", sw.ElapsedMilliseconds, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 2: {0} ms", sw.ElapsedMilliseconds) Console.WriteLine()
-
Hallo Gerry,
SQL Compact ist im Durchsatz grob vergleichbar mit einer Access Datenbank.
Zunächst wäre die erste Schleife immer vorzuziehen, denn setorientierte
Befehle, die alle benötigten Daten abrufen, sind auch bei Standard-SQL vorzuziehen.
Die zweite (Loop 2) hatte ich quasi als Worst Case eingebaut, denn mir waren 8ms einfach zu schnell ;-)
Generell sollte man das eher vermeiden, denn dabei werden etliche Befehle
mehr an die Datenbank gesendet. Und im Falle von Linq müssen die jedesmal
erstellt werden.
Mein Prozessor ist nun einige Tage neuer (Quad Core - von dem hier einer genutzt wird, dafür weniger Taktrate),
und vermutlich auch die Platte schneller (spielt bei dem Volumen aber kaum eine Rolle).
Woraus sich die besseren Zeiten ergeben dürften.
Das Äquivalent mit DataTable/ADO.NET für die zweite Variante liegt hier etwas unter 1000ms,
Was also gut ein drittel Aufschlag für Linq2Sql (1370ms) bedeutet.
Um Linq ebenfalls dort hinzubringen, kann man eine CompiledQuery verwwenden -
damit ist Linq2SQL bei mir etwa 20ms langsamer.
Unten Code zum Vergleichen und Testen auf Deiner Seite.
Gruß Elmar
' Kompilierte Abfrage für Loop2 Private Shared SendItemById As Func(Of SQLCompact.DBAlerts, Integer, IQueryable(Of SQLCompact.SendItem)) = _ CompiledQuery.Compile(Of SQLCompact.DBAlerts, Integer, IQueryable(Of SQLCompact.SendItem))( _ Function(context As SQLCompact.DBAlerts, idAlert As Integer) _ context.SendItems.Where(Function(i) i.IDAlert = idAlert)) Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim AlarmsList = From c In _db.Alarms Select c Dim sw = Stopwatch.StartNew For Each MyAlarm In AlarmsList Dim idAlert = MyAlarm.IDAlert For Each MySendItem In SendItemById(_db, idAlert) Console.WriteLine("Item 2: {0} - {1}", MySendItem.IDSendItem, MySendItem.IDAlert) Next Next sw.Stop() Console.WriteLine("Loop 2: {0} ms", sw.ElapsedMilliseconds) Console.WriteLine() End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click ' Grobes Äquivalent für die zweite Schleife Using connection As New SqlCeConnection(My.Settings.DBAlertsConnectionString) ' Entspricht From c In _db.Alarms Select c Dim alarmTable As New DataTable("Alarm") Using alarmAdapter = New SqlCeDataAdapter("SELECT * FROM Alarm", connection) alarmAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey alarmAdapter.MissingMappingAction = MissingMappingAction.Passthrough alarmAdapter.Fill(alarmTable) End Using Dim sw = Stopwatch.StartNew ' Entspricht From v In _db.SendItems Where v.IDAlert = idAlert Select v Dim sendItemTable As New DataTable("SendItem") Using sendItemAdapter = New SqlCeDataAdapter("SELECT * FROM SendItem WHERE IDAlert=@IDAlert", connection) sendItemAdapter.SelectCommand.Parameters.Add("@IDAlert", SqlDbType.Int) sendItemAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey sendItemAdapter.MissingMappingAction = MissingMappingAction.Passthrough sendItemAdapter.FillSchema(sendItemTable, SchemaType.Source) For Each alarmRow As DataRow In alarmTable.Rows sendItemAdapter.SelectCommand.Parameters("@IDAlert").Value = alarmRow("IDAlert") sendItemTable.Clear() sendItemAdapter.Fill(sendItemTable) For Each sendItemRow As DataRow In sendItemTable.Rows Console.WriteLine("Item 2: {0} - {1}", sendItemRow("IDSendItem"), sendItemRow("IDAlert")) Next Next End Using sw.Stop() Console.WriteLine("Loop 2: {0} ms", sw.ElapsedMilliseconds) End Using Console.WriteLine() End Sub
-
Hallo Elmar
Meine Versuche
- Erster Versuch Linq
- Item 2: 40 - 57
- Item 2: 39 - 58
- Loop 2: 5862 ms
- Zweiter versuch Linq
- Item 2: 40 - 57
- Item 2: 39 - 58
- Loop 2: 5476 ms
- Erster Versuch ADO.NET
- Item 2: 40 - 57
- Item 2: 39 - 58
- Loop 2: 5341 ms
- Zweiter Versuch ADO.NET
- Item 2: 40 - 57
- Item 2: 39 - 58
- Loop 2: 5292 ms
Beide Versuche mit
168 Eintrage in alarmTable ohne Beziehungen zu weiter Tabellen. (habe geändert wie oben geschrieben)
Wieso bin ich 5mal so langsam, so schlecht ist meine Cpu ja auch nicht?(cpu zu 70% ausgelasten auf beiden (unechten HT) Cores)
Aber das kann ja nicht normal sein das das so langsam ist.
Denn ich glaube eine Access Datenbank ist hier ja noch um Welten besser.
Da ist ja fast schon aus einer Textdatei auslesen schneller. -
Hallo Gerry,
ich werde morgen einen meiner älteren Rechner darauf ansetzen -
am Wochenende war es geplant, hat aber nicht geklappt.
Nur die Zeiten erscheinen mir generell zu hoch,
um ausschließlich von unterschiedlicher Hardware herzurühren.
Hast Du das .NET 3.5/VS 2008 Service Pack 1
und vor allem hier auch SQL Server Compact 3.5 SP1 installiert?
http://www.microsoft.com/Downloads/details.aspx?displaylang=de&FamilyID=dc614aee-7e1c-4881-9c32-3a6ce53384d9
Welche Software läuft (Virenscanner, sonstige Hintergrund-Software)?
Beende ggf. diese, insbesondere wenn der Speicher knapp ist.
Erstelle aus dem Code auch mal ein Konsolenprogramm,
und lasse es direkt ohne Debugging laufen (STRG+F5)
bzw. beende VS, vor allem wenn wenig Speicher verfügbar ist.
Gruß Elmar -
Hallo Elmar
Hallo hab nochmal mit Console ohne Debugging getestet
linq 5,3 ado.net 5,0 also circa alles gleich.
Speicher nur zu 50% ausgelastet 1,5gb
SQL Server Compact 3.5 SP1 ist installiert.
.NET 3.5/VS 2008 Service Pack 1 ist installiert.
Virenscanner ausgeschaltet bringt circa 0,1
Habe noch eine Spezialsoftware installiert die eine SPS auf dem Pc darstellt (Beckhoff Twincat)
das die Runtime vor Windows Startet aber die habe ich extra auf Stop gesetzt bringt auch nichts.
Sonst keine großeren Programme installiert auser VS und Office. -
Ich habe noch eine Frage zu diesen Tema.
Eigentlich wollte ich mit dieser Funktion einen Treeview laden.
Kann ich das irgentwie anders auch machen so das ich diesem Problem austellen kann.
(Die Verschachtelten SQL Abfragen waren jeweils für die SubNodes from Tree)