2017-07-12 3 views
3

Ich habe einfache DB mit 4 Tabellen. Tabelle Results hat 18 Spalten. 3 von ihnen sind Fremdschlüssel. Ich versuche Anzahl aller Ergebnisse zu erhalten (ca. 800k) mit diesem Code:FSharp.Data.SqlProvider ist langsam

#I @"..\packages\SQLProvider.1.1.3\lib" 
#r "FSharp.Data.SqlProvider.dll" 
open FSharp.Data.Sql 


let [<Literal>] ConnectionStringmdf = @"Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=C:\Users\Me\Desktop\myDb.mdf;Integrated Security=True;Connect Timeout=10"  

type Sqlmdf = SqlDataProvider< 
       ConnectionString = ConnectionStringmdf, 
       DatabaseVendor = Common.DatabaseProviderTypes.MSSQLSERVER, 
       IndividualsAmount = 1000, 
       UseOptionTypes = true, 
       CaseSensitivityChange = Common.CaseSensitivityChange.ORIGINAL 
       > 
let dbm = Sqlmdf.GetDataContext() 

printfn "Results count:\t %i" (dbm.Dbo.Results |> Seq.length) 

Es dauert etwa 40 Sekunden Anzahl der Datensätze in einer Tabelle zu erhalten.

Warum ist es so langsam? Was mache ich falsch?

+0

'Seq.length' bewirkt, dass die gesamte Ergebnisse Sequenz ausgewertet werden verwenden schreiben kann. Die benötigte Zeit umfasst also das Abrufen aller Daten, nicht nur das Zählen der Zeilen. Wie viele Zeilen gibt es? – TheQuickBrownFox

+0

Frage aktualisiert - es ist etwa 800 000. – Alamakanambra

+2

Ihr Code ist * NOT * Ausführen von 'SELECT COUNT (*)'. Es lädt * alle * Daten im Speicher und zählt die zurückgegebenen Objekte. Ich würde sagen, Laden 800K Zeilen in 40 Sekunden ist schnell –

Antwort

4

Die Typen von SqlDataProvider zurück IQueryable implementieren, das bedeutet, dass Sie einen Abfrageausdruck oder Queryable.Count

open System.Linq 

dbm.Dbo.Results |> Queryable.Count 

oder

query { for it in dbm.Dbo.Results do 
     count 
     } 
+0

Ok. Also muss ich diese 'query {..}' verwenden, um Daten von db zu bekommen ... Aber ... gibt es eine Möglichkeit, wie man diese 'Abfrage {..} Syntax? Ich meine, die Verarbeitung von Daten mit 'pipe |>' und 'Seq.someFunction' ist wirklich nett, nicht wahr? – Alamakanambra

+1

Ich habe bereits geschrieben, wie Sie die LINQ-Operatoren verwenden. 'bm.Dbo.Results |> Queryable.Count'. 'dbm.Dbo.Results' ist ein IQueryable . Sie sollten die 'Queryable'-Funktionen verwenden, nicht die Enumerable/Seq-Funktionen –

3

Sie sollten nur die Abfrage direkt in der Tabelle ausführen, und der Server das Ergebnis an Sie zurückgeben. Zum Beispiel bekomme ich sofort eine 8M Zeilenzählung:

type dbSchema = SqlDataConnection<connectionString1> 

let dbx = dbSchema.GetDataContext() 
dbx.DataContext.ObjectTrackingEnabled <- false 
dbx.DataContext.CommandTimeout <- 60 
let table1 = dbx.MyTable 

table1.Count() 
//val it : int = 7189765 

Sie könnten es auch in eine Abfrage umbrechen.

Hier ist eine Abfrageversion, die (auch wenn sqlprovider nicht zählt) auch auf dem anderen TP funktionieren sollte. Auch hier ist die Geschwindigkeit fast sofort.

query { for row in table1 do 
     select row 
     count 
     } 

Ich testete das gleiche mit SqlDataProvider mit ähnlichen Ergebnissen. Öffnen Sie den Namensraum System.Linq, um bei Bedarf auf die Erweiterungsfunktion .Count() zuzugreifen.

+1

Das OP verwendet 'SqlDataProvider', nicht' SqlDataConnection' –

+0

@ PanagiotisKanavos ah, ja, ich habe vergessen, meine alten SQL Server DBs wurden mit dem ursprünglichen SQLDataProvider verdrahtet. Ich glaube, die Abfrageversion sollte funktionieren. Wenn Sie den System.Linq-Namespace öffnen, sollten auch die linq-Erweiterungsmethoden wie Count funktionieren. Der Punkt, den Sie kommentiert haben, ist, dass die Abfrage auf dem Server und nicht auf dem Client ausgeführt wird und Sie nur das Ergebnis erhalten. – s952163