C# 封装 System.Data.SQLite
参考1:
关于如何使用System.Data.SQLite的入门:
http://www.dreamincode.net/forums/topic/157830-using-sqlite-with-c%23/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 using System.Data.SQLite; 5 using System.Globalization; 6 using System.Linq; 7 using System.Windows.Forms; 8 9 namespace Simple_Disk_Catalog 10 { 11 public class SQLiteDatabase 12 { 13 String DBConnection; 14 15 private readonly SQLiteTransaction _sqLiteTransaction; 16 17 private readonly SQLiteConnection _sqLiteConnection; 18 19 private readonly bool _transaction; 20 21 /// <summary> 22 /// Default Constructor for SQLiteDatabase Class. 23 /// </summary> 24 /// <param name="transaction">Allow programmers to insert, update and delete values in one transaction</param> 25 public SQLiteDatabase(bool transaction = false) 26 { 27 _transaction = transaction; 28 DBConnection = "Data Source=recipes.s3db"; 29 if (transaction) 30 { 31 _sqLiteConnection = new SQLiteConnection(DBConnection); 32 _sqLiteConnection.Open(); 33 _sqLiteTransaction = _sqLiteConnection.BeginTransaction(); 34 } 35 } 36 37 /// <summary> 38 /// Single Param Constructor for specifying the DB file. 39 /// </summary> 40 /// <param name="inputFile">The File containing the DB</param> 41 public SQLiteDatabase(String inputFile) 42 { 43 DBConnection = String.Format("Data Source={0}", inputFile); 44 } 45 46 /// <summary> 47 /// Commit transaction to the database. 48 /// </summary> 49 public void CommitTransaction() 50 { 51 _sqLiteTransaction.Commit(); 52 _sqLiteTransaction.Dispose(); 53 _sqLiteConnection.Close(); 54 _sqLiteConnection.Dispose(); 55 } 56 57 /// <summary> 58 /// Single Param Constructor for specifying advanced connection options. 59 /// </summary> 60 /// <param name="connectionOpts">A dictionary containing all desired options and their values</param> 61 public SQLiteDatabase(Dictionary<String, String> connectionOpts) 62 { 63 String str = connectionOpts.Aggregate("", (current, row) => current + String.Format("{0}={1}; ", row.Key, row.Value)); 64 str = str.Trim().Substring(0, str.Length - 1); 65 DBConnection = str; 66 } 67 68 /// <summary> 69 /// Allows the programmer to create new database file. 70 /// </summary> 71 /// <param name="filePath">Full path of a new database file.</param> 72 /// <returns>true or false to represent success or failure.</returns> 73 public static bool CreateDB(string filePath) 74 { 75 try 76 { 77 SQLiteConnection.CreateFile(filePath); 78 return true; 79 } 80 catch (Exception e) 81 { 82 MessageBox.Show(e.Message, e.GetType().ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error); 83 return false; 84 } 85 } 86 87 /// <summary> 88 /// Allows the programmer to run a query against the Database. 89 /// </summary> 90 /// <param name="sql">The SQL to run</param> 91 /// <param name="allowDBNullColumns">Allow null value for columns in this collection.</param> 92 /// <returns>A DataTable containing the result set.</returns> 93 public DataTable GetDataTable(string sql, IEnumerable<string> allowDBNullColumns = null) 94 { 95 var dt = new DataTable(); 96 if (allowDBNullColumns != null) 97 foreach (var s in allowDBNullColumns) 98 { 99 dt.Columns.Add(s); 100 dt.Columns[s].AllowDBNull = true; 101 } 102 try 103 { 104 var cnn = new SQLiteConnection(DBConnection); 105 cnn.Open(); 106 var mycommand = new SQLiteCommand(cnn) {CommandText = sql}; 107 var reader = mycommand.ExecuteReader(); 108 dt.Load(reader); 109 reader.Close(); 110 cnn.Close(); 111 } 112 catch (Exception e) 113 { 114 throw new Exception(e.Message); 115 } 116 return dt; 117 } 118 119 public string RetrieveOriginal(string value) 120 { 121 return 122 value.Replace("&", "&").Replace("<", "<").Replace(">", "<").Replace(""", "\"").Replace( 123 "'", "'"); 124 } 125 126 /// <summary> 127 /// Allows the programmer to interact with the database for purposes other than a query. 128 /// </summary> 129 /// <param name="sql">The SQL to be run.</param> 130 /// <returns>An Integer containing the number of rows updated.</returns> 131 public int ExecuteNonQuery(string sql) 132 { 133 if (!_transaction) 134 { 135 var cnn = new SQLiteConnection(DBConnection); 136 cnn.Open(); 137 var mycommand = new SQLiteCommand(cnn) {CommandText = sql}; 138 var rowsUpdated = mycommand.ExecuteNonQuery(); 139 cnn.Close(); 140 return rowsUpdated; 141 } 142 else 143 { 144 var mycommand = new SQLiteCommand(_sqLiteConnection) { CommandText = sql }; 145 return mycommand.ExecuteNonQuery(); 146 } 147 } 148 149 /// <summary> 150 /// Allows the programmer to retrieve single items from the DB. 151 /// </summary> 152 /// <param name="sql">The query to run.</param> 153 /// <returns>A string.</returns> 154 public string ExecuteScalar(string sql) 155 { 156 if (!_transaction) 157 { 158 var cnn = new SQLiteConnection(DBConnection); 159 cnn.Open(); 160 var mycommand = new SQLiteCommand(cnn) {CommandText = sql}; 161 var value = mycommand.ExecuteScalar(); 162 cnn.Close(); 163 return value != null ? value.ToString() : ""; 164 } 165 else 166 { 167 var sqLiteCommand = new SQLiteCommand(_sqLiteConnection) { CommandText = sql }; 168 var value = sqLiteCommand.ExecuteScalar(); 169 return value != null ? value.ToString() : ""; 170 } 171 } 172 173 /// <summary> 174 /// Allows the programmer to easily update rows in the DB. 175 /// </summary> 176 /// <param name="tableName">The table to update.</param> 177 /// <param name="data">A dictionary containing Column names and their new values.</param> 178 /// <param name="where">The where clause for the update statement.</param> 179 /// <returns>A boolean true or false to signify success or failure.</returns> 180 public bool Update(String tableName, Dictionary<String, String> data, String where) 181 { 182 String vals = ""; 183 Boolean returnCode = true; 184 if (data.Count >= 1) 185 { 186 vals = data.Aggregate(vals, (current, val) => current + String.Format(" {0} = '{1}',", val.Key.ToString(CultureInfo.InvariantCulture), val.Value.ToString(CultureInfo.InvariantCulture))); 187 vals = vals.Substring(0, vals.Length - 1); 188 } 189 try 190 { 191 ExecuteNonQuery(String.Format("update {0} set {1} where {2};", tableName, vals, where)); 192 } 193 catch 194 { 195 returnCode = false; 196 } 197 return returnCode; 198 } 199 200 /// <summary> 201 /// Allows the programmer to easily delete rows from the DB. 202 /// </summary> 203 /// <param name="tableName">The table from which to delete.</param> 204 /// <param name="where">The where clause for the delete.</param> 205 /// <returns>A boolean true or false to signify success or failure.</returns> 206 public bool Delete(String tableName, String where) 207 { 208 Boolean returnCode = true; 209 try 210 { 211 ExecuteNonQuery(String.Format("delete from {0} where {1};", tableName, where)); 212 } 213 catch (Exception fail) 214 { 215 MessageBox.Show(fail.Message, fail.GetType().ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error); 216 returnCode = false; 217 } 218 return returnCode; 219 } 220 221 /// <summary> 222 /// Allows the programmer to easily insert into the DB 223 /// </summary> 224 /// <param name="tableName">The table into which we insert the data.</param> 225 /// <param name="data">A dictionary containing the column names and data for the insert.</param> 226 /// <returns>returns last inserted row id if it's value is zero than it means failure.</returns> 227 public long Insert(String tableName, Dictionary<String, String> data) 228 { 229 String columns = ""; 230 String values = ""; 231 String value; 232 foreach (KeyValuePair<String, String> val in data) 233 { 234 columns += String.Format(" {0},", val.Key.ToString(CultureInfo.InvariantCulture)); 235 values += String.Format(" '{0}',", val.Value); 236 } 237 columns = columns.Substring(0, columns.Length - 1); 238 values = values.Substring(0, values.Length - 1); 239 try 240 { 241 if (!_transaction) 242 { 243 var cnn = new SQLiteConnection(DBConnection); 244 cnn.Open(); 245 var sqLiteCommand = new SQLiteCommand(cnn) 246 { 247 CommandText = 248 String.Format("insert into {0}({1}) values({2});", tableName, columns, 249 values) 250 }; 251 sqLiteCommand.ExecuteNonQuery(); 252 sqLiteCommand = new SQLiteCommand(cnn) { CommandText = "SELECT last_insert_rowid()" }; 253 value = sqLiteCommand.ExecuteScalar().ToString(); 254 } 255 else 256 { 257 ExecuteNonQuery(String.Format("insert into {0}({1}) values({2});", tableName, columns, values)); 258 value = ExecuteScalar("SELECT last_insert_rowid()"); 259 } 260 } 261 catch (Exception fail) 262 { 263 MessageBox.Show(fail.Message, fail.GetType().ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error); 264 return 0; 265 } 266 return long.Parse(value); 267 } 268 269 /// <summary> 270 /// Allows the programmer to easily delete all data from the DB. 271 /// </summary> 272 /// <returns>A boolean true or false to signify success or failure.</returns> 273 public bool ClearDB() 274 { 275 try 276 { 277 var tables = GetDataTable("select NAME from SQLITE_MASTER where type='table' order by NAME;"); 278 foreach (DataRow table in tables.Rows) 279 { 280 ClearTable(table["NAME"].ToString()); 281 } 282 return true; 283 } 284 catch 285 { 286 return false; 287 } 288 } 289 290 /// <summary> 291 /// Allows the user to easily clear all data from a specific table. 292 /// </summary> 293 /// <param name="table">The name of the table to clear.</param> 294 /// <returns>A boolean true or false to signify success or failure.</returns> 295 public bool ClearTable(String table) 296 { 297 try 298 { 299 ExecuteNonQuery(String.Format("delete from {0};", table)); 300 return true; 301 } 302 catch 303 { 304 return false; 305 } 306 } 307 308 /// <summary> 309 /// Allows the user to easily reduce size of database. 310 /// </summary> 311 /// <returns>A boolean true or false to signify success or failure.</returns> 312 public bool CompactDB() 313 { 314 try 315 { 316 ExecuteNonQuery("Vacuum;"); 317 return true; 318 } 319 catch (Exception) 320 { 321 return false; 322 } 323 } 324 } 325 }
参考2:
关于如何正确使用System.Data.SQLite提高性能
SQLite .NET performance, how to speed up things?
You definitely need a transaction. If you don't, SQLite starts its own transaction for every insert command so you're effectively doing 86000 transactions as is.
It looks you're also opening and closing the connection each time, along with resetting the CommandText each time. This is unnecessary and doubtless slowing you down, it'll go much faster if you:
- Open the connection once
- Build the command once , adding the parameters to it once.
- Start the transaction
- Loop through, changing the parameter values only before calling ExecuteNonQuery
- Commit the transaction.
- Close the connection.
I think you could reduce your 20 minutes down to just a few seconds this way.
Edit: this is what I mean:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public void InsertItems() 2 { 3 SQLiteConnection connection = new SQLiteConnection(SomeConnectionString); 4 SQLiteCommand command = connection.CreateCommand(); 5 SQLiteTransaction transaction = connection.BeginTransaction(); 6 7 command.CommandText = "INSERT OR IGNORE INTO Result " 8 + "(RunTag, TopicId, DocumentNumber, Rank, Score) " + 9 "VALUES (@RunTag, @TopicId, @DocumentNumber, @Rank, @Score)"; 10 11 command.Parameters.AddWithValue("@RunTag", ""); 12 command.Parameters.AddWithValue("@TopicId", ""); 13 command.Parameters.AddWithValue("@DocumentNumber", ""); 14 command.Parameters.AddWithValue("@Rank", ""); 15 command.Parameters.AddWithValue("@Score", ""); 16 17 foreach ( /* item to loop through and add to db */ ) 18 { 19 InsertResultItem(runTag, topicId, documentNumber, rank, score, command); 20 } 21 22 transaction.Commit(); 23 command.Dispose(); 24 connection.Dispose(); 25 } 26 27 public int InsertResultItem(string runTag, int topicId, string documentNumber, int rank, double score, SQLiteCommand command) 28 { 29 command.Parameters["@RunTag"].Value = runTag; 30 command.Parameters["@TopicId"].Value = topicId; 31 command.Parameters["@DocumentNumber"].Value = documentNumber; 32 command.Parameters["@Rank"].Value = rank; 33 command.Parameters["@Score"].Value = score; 34 return command.ExecuteNonQuery(); 35 }
It only uses one connection, one transaction and one command, so all you're changing is the parameter values each time.
我自己实现的一个简易版本:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data.SQLite; 7 8 namespace Utility 9 { 10 /*Multithread safety: No!*/ 11 public class SQLiteDB 12 { 13 private SQLiteConnection m_db_conn; 14 private SQLiteTransaction m_transaction; 15 private bool m_is_transaction; 16 private SQLiteCommand m_transaction_cmd; 17 18 public SQLiteDB() 19 { 20 m_is_transaction = false; 21 } 22 23 public SQLiteDB(string db_path) 24 { 25 m_is_transaction = false; 26 m_db_conn = new SQLiteConnection(string.Format("Data Source={0}", db_path)); 27 m_db_conn.Open(); 28 } 29 public void Open(string db_path) 30 { 31 m_db_conn = new SQLiteConnection(string.Format("Data Source={0}", db_path)); 32 m_db_conn.Open(); 33 } 34 public void Close() 35 { 36 if (m_db_conn.State != System.Data.ConnectionState.Closed) 37 { 38 m_db_conn.Close(); 39 m_db_conn.Dispose(); 40 } 41 if (null != m_transaction_cmd) 42 { 43 m_transaction_cmd.Dispose(); 44 } 45 } 46 47 public void BeginTransaction() 48 { 49 m_is_transaction = true; 50 m_transaction_cmd = m_db_conn.CreateCommand(); 51 m_transaction = m_db_conn.BeginTransaction(); 52 } 53 54 public void Commit() 55 { 56 if (m_is_transaction) 57 { 58 m_transaction.Commit(); 59 m_transaction_cmd.Dispose(); 60 m_is_transaction = false; 61 } 62 } 63 64 public void Rollback() 65 { 66 if (m_is_transaction) 67 { 68 m_transaction.Rollback(); 69 m_transaction_cmd.Dispose(); 70 m_is_transaction = false; 71 } 72 } 73 74 public int ExecuteNonQuery(string non_query_sql) 75 { 76 if (!m_is_transaction) 77 { 78 SQLiteCommand sql_cmd = new SQLiteCommand(m_db_conn); 79 sql_cmd.CommandText = non_query_sql; 80 return sql_cmd.ExecuteNonQuery(); 81 } 82 else 83 { 84 m_transaction_cmd.CommandText = non_query_sql; 85 return m_transaction_cmd.ExecuteNonQuery(); 86 } 87 } 88 } 89 }