[英]Haskell HDBC Elegance in F#?
我對Haskell的簡潔和優雅感到震驚。 但是我在一個.Net的房子里工作,所以當我能夠逃脫它時,我會使用F# - 我可能是全國數百名使用它的人中唯一一個。
ADO.NET或F#是否提供與executeMany
的executeMany
一樣簡潔優雅的executeMany
? 我正在通過真實世界Haskell 。 在第21章中,它提供了這個例子:
ghci> conn <- connectSqlite3 "test1.db"
ghci> stmt <- prepare conn "INSERT INTO test VALUES (?, ?)"
ghci> executeMany stmt [[toSql 5, toSql "five's nice"], [toSql 6, SqlNull]]
ghci> commit conn
ghci> disconnect conn
我想在我的F#中獲得這種優雅和簡潔。 我已經看到很多關於使用參數化查詢以避免SQL注入攻擊的炒作。 在這種情況下,我沒有使用它們有三個原因:
這是我的F#代碼:
module Data
open System
open System.Data
open System.Data.OleDb
open System.Text.RegularExpressions
type Period = Prior | Current
let Import period records db =
use conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + db + ";Persist Security Info=False;")
let execNonQuery s =
let comm = new OleDbCommand(s, conn) in
comm.ExecuteNonQuery() |> ignore
let enquote = sprintf "\"%s\""
let escapeQuotes s = Regex.Replace(s, "\"", "\"\"")
let join (ss:string[]) = String.Join(",", ss)
let table = match period with
| Prior -> "tblPrior"
| Current -> "tblCurrent"
let statements =
[| for r in records do
let vs = r |> Array.map (escapeQuotes >> enquote) |> join
let vs' = vs + sprintf ",\"14\",#%s#" (DateTime.Now.ToString "yyyy-MM-dd") in
yield sprintf "INSERT INTO %s ( [Field01], [Field02], [Field03] [Field04], [Field05], [Field06], [Field07], [Field08], [Field09], [Field10], [Field11], [Field12], [Field13], [Field14], [Field15], [Field16], [Field17], [Field18], [Field19], [Field20], [Field21], [Field22], [Field23], [Field24], [Field25], [Field26], [Field27], [Field28], [Field29], [Field30], [Field31], [Field32], [Field33], [Field34] ) VALUES (%s)" table vs' |] in
do conn.Open()
execNonQuery (sprintf "DELETE FROM %s" table)
statements |> Array.iter execNonQuery
出於安全原因,我已經重命名了表格的字段。
因為表上的所有字段都是文本,所以我可以輕松地將它們Array.map它們轉義並引用值。
每天要將9,000到10,000條記錄導入到兩個表中的每一個表中,我希望盡可能高效地執行此操作。 因此我對Haskell的executeMany
感興趣。 但是,我喜歡參數化查詢背后的想法,我喜歡Hasekll實現它們的方式。 在F#中,是否存在與簡潔和優雅相同的東西?
我同意@JonnyBoats評論,通常使用像SqlDataConnection (LINQ-to-SQL)或SqlEntityConnection (實體框架)這樣的F#SQL類型提供程序將比任何涉及手動構建insert語句字符串的解決方案更優雅。
但是,你的問題還有一個重要的限定符:“每天要將9,000到10,000條記錄導入到兩個表中的每一個,我希望盡可能高效地完成這項工作。” 在這樣的場景中,您將希望使用SqlBulkCopy進行有效的批量插入(它利用本機數據庫驅動程序功能,比使用executeMany
的executeMany
更快地插入)。
這是一個小例子,它可以幫助您開始使用帶有F#的SqlBulkCopy
: https : //stackoverflow.com/a/8942056/236255 。 請注意,您將使用DataTable來暫存數據 ,這些數據雖然陳舊但在F#中使用起來有些尷尬,但在我看來仍優於構建插入語句字符串。
更新以回應評論
下面是使用SqlBulkCopy
的一般化方法,該方法針對您的場景進行了改進(我們將列規范與行數據分開傳遞,並且兩者都是動態的):
//you must reference System.Data and System.Xml
open System
open System.Data
open System.Data.SqlClient
let bulkLoad (conn:SqlConnection) tableName (columns:list<string * Type>) (rows: list<list<obj>>) =
use sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, null, BatchSize=500, BulkCopyTimeout=1200, DestinationTableName=tableName)
sbc.WriteToServer(
let dt = new DataTable()
columns
|> List.iter (dt.Columns.Add>>ignore)
for row in rows do
let dr = dt.NewRow()
row |> Seq.iteri(fun i value -> dr.[i] <- value)
dt.Rows.Add(dr)
dt)
//example usage:
//note: since you know all your columns are of type string, you could define columns like
//let columns = ["Field1", "Field2", "Field3"] |> List.map (fun name -> name, typeof<String>)
let columns = [
"Field1", typeof<String>
"Field2", typeof<String>
"Field3", typeof<String>
]
let rows = [
["a"; "b"; "c"]
["d"; "e"; "f"]
["g"; "h"; "i"]
["j"; "k"; "l"]
["m"; "n"; "o"]
]
//a little funkiness to transform our list<list<string>> to list<list<obj>>,
//probably not needed in practice because you won't be constructing your lists literally
let rows = rows |> List.map (fun row -> row |> List.map (fun value -> value :> obj))
bulkLoad conn "tblPrior" columns rows
使用涉及反射的方法,你可以得到更好的/更簡潔的東西。 例如創建類似的類型
type RowData = { Field1:string; Field2:string; Field3:string }
並創建一個帶有簽名的bulkLoad
,該簽名采用list<'a>
參數,以便它反映屬性名稱和typeof<'a>
類型以構建DataTable
Columns
,並類似地使用反射來迭代所有屬性行實例,用於創建並向DataTable
添加新行。 事實上, 這個問題展示了如何制作一個通用的ToDataTable
方法(在C#中)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.