DBGrid, Bookmarks and SQLite very slow [duplicate] - sqlite

I recently read about SQLite and thought I would give it a try. When I insert one record it performs okay. But when I insert one hundred it takes five seconds, and as the record count increases so does the time. What could be wrong? I am using the SQLite Wrapper (system.data.SQlite):
dbcon = new SQLiteConnection(connectionString);
dbcon.Open();
//---INSIDE LOOP
SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);
nRowUpdatedCount = sqlComm.ExecuteNonQuery();
//---END LOOP
dbcon.close();

Wrap BEGIN \ END statements around your bulk inserts. Sqlite is optimized for transactions.
dbcon = new SQLiteConnection(connectionString);
dbcon.Open();
SQLiteCommand sqlComm;
sqlComm = new SQLiteCommand("begin", dbcon);
sqlComm.ExecuteNonQuery();
//---INSIDE LOOP
sqlComm = new SQLiteCommand(sqlQuery, dbcon);
nRowUpdatedCount = sqlComm.ExecuteNonQuery();
//---END LOOP
sqlComm = new SQLiteCommand("end", dbcon);
sqlComm.ExecuteNonQuery();
dbcon.close();

I read everywhere that creating transactions is the solution to slow SQLite writes, but it can be long and painful to rewrite your code and wrap all your SQLite writes in transactions.
I found a much simpler, safe and very efficient method: I enable a (disabled by default) SQLite 3.7.0 optimisation : the Write-Ahead-Log (WAL).
The documentation says it works in all unix (i.e. Linux and OSX) and Windows systems.
How ? Just run the following commands after initializing your SQLite connection:
PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL
My code now runs ~600% faster : my test suite now runs in 38 seconds instead of 4 minutes :)

Try wrapping all of your inserts (aka, a bulk insert) into a single transaction:
string insertString = "INSERT INTO [TableName] ([ColumnName]) Values (#value)";
SQLiteCommand command = new SQLiteCommand();
command.Parameters.AddWithValue("#value", value);
command.CommandText = insertString;
command.Connection = dbConnection;
SQLiteTransaction transaction = dbConnection.BeginTransaction();
try
{
//---INSIDE LOOP
SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);
nRowUpdatedCount = sqlComm.ExecuteNonQuery();
//---END LOOP
transaction.Commit();
return true;
}
catch (SQLiteException ex)
{
transaction.Rollback();
}
By default, SQLite wraps every inserts in a transaction, which slows down the process:
INSERT is really slow - I can only do few dozen INSERTs per second
Actually, SQLite will easily do 50,000 or more INSERT statements per second on an average desktop computer. But it will only do a few dozen transactions per second.
Transaction speed is limited by disk drive speed because (by default) SQLite actually waits until the data really is safely stored on the disk surface before the transaction is complete. That way, if you suddenly lose power or if your OS crashes, your data is still safe. For details, read about atomic commit in SQLite..
By default, each INSERT statement is its own transaction. But if you surround multiple INSERT statements with BEGIN...COMMIT then all the inserts are grouped into a single transaction. The time needed to commit the transaction is amortized over all the enclosed insert statements and so the time per insert statement is greatly reduced.

See "Optimizing SQL Queries" in the ADO.NET help file SQLite.NET.chm. Code from that page:
using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
{
SQLiteParameter myparam = new SQLiteParameter();
int n;
mycommand.CommandText = "INSERT INTO [MyTable] ([MyId]) VALUES(?)";
mycommand.Parameters.Add(myparam);
for (n = 0; n < 100000; n ++)
{
myparam.Value = n + 1;
mycommand.ExecuteNonQuery();
}
}
mytransaction.Commit();
}

Related

Slow down execution of bulk inserts

We have an Azure SQL database which is on the S1 pricing tier. Our site is extremely heavily cached, so database hits are absolutely minimal. Average DTU usage is only ~1.5%, this is great as our DB costs are a fraction of what they used to be on our old website (£20p/m vs £400 p/m!)
On the site however, we do have small scripts that require insertion of ~100k records or so (user notifications for when someone performs an action such as creates a new tutorial).
When this is triggered, DTU's spike at 100% for around 3-5 minutes.
The script is simply a loop which calls an insert:
using(var db = new DBContext())
{
foreach(var userID in userIDs)
{
db.ExecuteCommand(
"INSERT INTO UserNotifications " +
"(ForUserID, Date, ForObjectTypeID, ForObjectID, TypeID, Count, MetaData1)
VALUES ({0}, {1}, NULL, {2}, {3}, {4}, {5}, {6})",
userID, DateTime.Now.ToUniversalTime(), forObjectID, (byte)type, 1, metaData1.Value
);
}
}
Is there a faster way to do inserts than this?
Additionally, what would be the best way to slow down execution of this script so DTU usage doesn't choke everything up?
You are doing one row per insert - that is not efficient.
A TVP is a like a reverse datareader and is efficient.
Lower tech is to insert 900 rows at a time (1000 is the max). This alone is probably 400x more efficient.
StringBuilder sb = new StringBuilder();
string insert = "INSERT INTO UserNotifications " +
"(ForUserID, Date, ForObjectTypeID, ForObjectID, TypeID, Count, MetaData1) " +
"VALUES ";
sb.AppendLine(insert);
int count = 0;
using(var db = new DBContext())
{
foreach(var userID in userIDs)
{
sb.AppendLine(string.Format(({0}, {1}, NULL, {2}, {3}, {4}, {5}, {6}), ",
userID, DateTime.Now.ToUniversalTime(), forObjectID, (byte)type, 1, metaData1.Value);
count++;
if (count = 990)
{
db.ExecuteCommand(sb.ToString());
count = 0;
sb.Clear();
sb.AppendLine(insert);
//can sleep here to throttle down cpu
}
}
if (count > 0)
{
db.ExecuteCommand(sb.ToString());
}
}
Instead of insert entity by entity you can make an insert of 100 entities at the same time packing the entities in a JSON and writing a store procedure that use it like in this example:
INSERT INTO [dbo].[AISecurityLogs]
([IpAddress], [TimeRange], [Requests], [LogId])
SELECT *, LogId = #logId
FROM OPENJSON ( #json )
WITH (
IpAddress varchar(15) '$.IpAddress',
TimeRange DATETIME '$.TimeRange',
Requests int '$.Requests'
)
To slow down execution and doesn't lost anything you can put the logs in a queue, and then read this information with an azure Job, that allow you to configure the read interval, and insert in the database as I wrote before.
This approach allows a big load (I have some several in production environments) and if something goes wrong with the agent or with the database, the messages are stored in the queue until you move them to the database.

Is there any equivalent of SQLBulkCopy in TeraData

After some goоgling, I could not find a proper replacement of SQLBulkCopy from SQLClient in TeraData. Can any body suggest me anything like SQLBulkCopy for TeraData to be used in c#? I need to insert up-to a few millions of rows in TD
Need this to compare a set a rows retrieved from external DB and dumped into TD and compare with data already available in TeraData.
Any suggestion is appreciated.
I couldn't find an equivalent but this is acceptably fast for my purposes. Also I suspect that the UpdateBatchSize could be tweaked to match your particular data to increase speeds.
As written, your source and destination tables must have the same columns (like BulkCopy, although not necessarily in the same order).
TdConnection tdCon = new TdConnection(tdConString);
SqlConnection sqlCon1 = new SqlConnection(serverOneConString);
// Get schema for destination table
var query = "SELECT * FROM [Destination_Table] where 0 = 1";
using (TdDataAdapter insertAdapter = new TdDataAdapter(query, tdCon))
{
DataSet ds = new DataSet();
insertAdapter.Fill(ds);
// Load data from source table
using (SqlDataAdapter dataAdapter = new SqlDataAdapter("SELECT * FROM [Source_Table]", sqlCon1)) {
dataAdapter.SelectCommand.CommandTimeout = 240;
dataAdapter.Fill(dt);
}
// Move data from source to destination, matching column names
foreach (DataRow row in dt.Rows) {
var newRow = ds.Tables[0].NewRow();
foreach (DataColumn column in dt.Columns) {
newRow[column.ColumnName] = row[column.ColumnName];
}
ds.Tables[0].Rows.Add(newRow);
}
TdCommandBuilder builder = new TdCommandBuilder(insertAdapter);
insertAdapter.UpdateBatchSize = 250;
insertAdapter.Update(ds);
}
Teradata's .Net provider can be used for loading, you need to set TdDataAdapter.UpdateBatchSize as high as possible, at least a few hundred.
If this is not fast enough for larger amounts of data you might switch to Teradata's TPT-API

SQL Server performance: from .net via SqlConnection versus Management Studio

I have a strange phenomenon, while investigating the performance of a query, I noticed that it takes 6 seconds to run the query in sql server management studio, but it only takes .6 seconds when invoked from within a vb.net application.
I am getting the time stats from "set statistics time" in SSMS, and using System.Diagnostics.Stopwatch in vb.net application.
I have done everything I can think of to rule out caching, and verifying that the query within vb.net code is not deferred somehow. I've verified both are using the same database. I've restarted sql server, I've restarted the vb.net application, I've used
USE <dbname>;
GO
CHECKPOINT;
GO
DBCC DROPCLEANBUFFERS;
GO
When running the query in SSMS, flushing the cache (as above) makes no difference, indicating that the query does not benefit from any caching. The query takes abou 6 seconds every time. The same query takes .6 seconds when executed from vb.net via a SqlClient.SqlConnection. It goes that fast even after restarting both the sql server and the vb.net app.
I don't think the code of the query is terribly relevant, other than to note that it calls a function which in turn uses sql's version of "recursion".
Another note is that when executing the query from SSMS, I can speed it up by adding a needed index, after which it runs in about 1.4 seconds. When executed from vb.net app, it runs in .23 seconds with the index.
The question is, what could sql server be doing differently depending on whether a query is invoked from ssms vs. vb.nt, to result in this performance difference?
---edit to provide code---
Here is the query called in vb.net, yes it is embedded in a linq query in a rather otdd way, and yes I have verified that the linq query is not deferred for purposes of timing it.
fnName = "AT.fn_Plan_Item_Level_Sort_Reverse"
isParent = 1
foo = (From d In DBConn.sqlSelect(
"SELECT DENSE_RANK() OVER (ORDER BY SUBSTRING(Sort,1,3)) AS [GroupNumber]," & _
"Plan_ID, Master_Item_ID, Item_ID, Level, Sort " & _
"FROM " & fnName & "(#Plan_ID) ORDER BY Sort;" _
, GetCommandItem("#Plan_ID", SqlDbType.BigInt, _planId)).OfType(Of DataRowView)() _
Select New PlanItemRaw With { _
.PlanID = Wrapper(Of Long)(d.Item("Plan_ID")) _
, .MasterItemID = Wrapper(Of Long)(d.Item("Master_Item_ID")) _
, .ItemID = Wrapper(Of Long)(d.Item("Item_ID")) _
, .GroupNumber = Wrapper(Of Integer)(d.Item("GroupNumber")) _
, .Level = Wrapper(Of Integer)(d.Item("Level")) _
, .IsParent = (Wrapper(Of Integer)(d.Item("Level")) = isParent) _
, .Sort = Wrapper(Of String)(d.Item("Sort")) _
}).ToArray
Note that isParent is irrelevant to the SQL query, only used by the linq query.
DBConn.sqlSelect is homebrew, and a wrapper for the following, (items() is nothing in this case):
Public Function sqlSelect(ByVal sqlCommand As String, ByVal commandTimeout As Integer,
ByVal ParamArray items() As SQLCommandItem) As DataView
Using conn As New SqlClient.SqlConnection(_conn)
Using cmd As New SqlClient.SqlCommand(sqlCommand, conn)
cmd.CommandTimeout = commandTimeout
Call AddParamesterstoCommand(cmd, items)
Using da As New SqlClient.SqlDataAdapter
da.SelectCommand = cmd
Call logFullSQLStringForDebugging("sqlSelect", cmd)
Using custDS As New DataSet
da.Fill(custDS)
If custDS.Tables.Count = 0 Then
sqlSelect = Nothing
Else
sqlSelect =
custDS.Tables(custDS.Tables.Count-1).DefaultView
End If
End Using
End Using
End Using
conn.Close()
End Using
End Function
Now here is the query in SSMS:
set statistics time on
select DENSE_RANK() over (order by substring(sort,1,3)) as [groupnumber],
Plan_ID, Master_Item_ID, Item_ID, [level], Sort
from at.fn_Plan_Item_Level_Sort_Reverse(3538)
order by Sort
set statistics time off
Note that the parameter 3538 is identical to the value passed via #Plan_Id in the vb.net code. Here is the code for the function. This is basically using a recursive common table expression to collect and sort hierarchical data. It does call a couple other functions which are both quite simple.
RETURN
(
WITH Items
as
(
SELECT i.Plan_ID, mi.Item_ID [Master_Item_ID], i.Item_ID, Sort
FROM AT.Item i
LEFT OUTER JOIN AT.Mapping_Item mi ON i.Plan_ID = mi.Plan_ID AND i.Item_ID = mi.Master_Item_ID
WHERE i.Plan_ID = #Plan_ID
),
tmp
as
(
SELECT i.*, replicate('0',3-len(i.Sort)) + convert(varchar(max), i.Sort) 'Good_Sort', 0 as 'Level'
FROM AT.fn_Plan_Item_Leaves(#Plan_ID) r
JOIN Items i ON r.Plan_ID = i.Plan_ID AND r.Item_ID = i.Item_ID
UNION ALL
SELECT i.*, convert(varchar(max), ti.Good_Sort) + ' ' + replicate('0',3-len(i.Sort)) + convert(varchar(max), i.Sort) 'Good_Sort', ti.level + 1 as 'Level'
FROM Items i
JOIN tmp ti ON ti.Plan_ID = i.Plan_ID AND ti.Item_ID = i.Master_Item_ID
)
SELECT t.Plan_ID, t.Master_Item_ID, t.Item_ID, t.level,
CASE WHEN l.Plan_ID IS NOT NULL THEN convert(bit,1) ELSE convert(bit,0) END IsRoot, Good_Sort 'Sort'
FROM tmp t
LEFT OUTER JOIN AT.fn_Plan_Item_Roots(#Plan_ID) l ON t.Plan_ID=l.Plan_ID AND t.Item_ID=l.Item_ID
)

SqlCommand slow when executing a query with parameters

DbDataAdapter.Fill() is extremly slow when performing parameters!
I have a query with 2 parameters inside, and when I put those parameters hardcoded in the query it takes 1 second to execute (in a 470k table rows, returning only 20 rows).
I found many posts similars here and I tried all those solutions (set arithabort, option recompile, option optimize for, ...) with no luck.
I just perform a query (sql server 2008) and not a stored procedure, so the query with arithabort is like this:
string strSql = #"set ARITHABORT ON;
select TOP 20 ....
Also I tried to call set arithabort in the same transaction but performing that query first..
I don't know if I'm doing something wrong, but the sensation is the ado.net is performing a very bad execution plan in ado.net when I have defined parameters on it.
As a result of this bad choice, the execution time in SSMS is 1 second (after being cached) but in asp is like 9 seconds!
The query is something like this:
strSQL #="
select *
from Table1
where Name like #name";
And then:
DbProviderFactory factory = DbProviderFactories.GetFactory(mProvider);
DbCommand dbcmd = factory.CreateCommand();
if (CommandTimeout != null)
dbcmd.CommandTimeout = CommandTimeout.Value;
if(this.transaccion != null)
dbcmd.Transaction = this.transaccion;
dbcmd.Connection = dbc;
dbcmd.CommandText = strSQL;
if (parametros != null)
dbcmd.Parameters.AddRange(parametros);
DbDataAdapter dbda = factory.CreateDataAdapter();
dbda.SelectCommand = dbcmd;
DataTable dt = new DataTable();
dbda.Fill(dt);
return dt;
EDIT 14/01/2013 (18:44)
I'm not longer retrieve the connection from DbProviderFactory, insted I'm using directly SqlConnection and SqlCommand. I know DbCommand and DbProvider are a base clase... but I think there is something more in there.. because the performance drasticaly increase like 300%!
It's not the fill method, because I already tried in the code shown before..
Anyway, I don't know the reason why but using a SqlConnection is much faster! Any idea? Maybe isn't making that bad execution plan made before?
SqlCommand objCmd = new SqlCommand(strSQL, sqlConn);
if (CommandTimeout != null)
objCmd.CommandTimeout = CommandTimeout.Value;
if (this.transaccion != null)
objCmd.Transaction = SQLtransaccion;
if (parametros != null)
objCmd.Parameters.AddRange(parametros);
DbDataReader dbReader = objCmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dbReader);
dbReader.Close();
return dt;
Any help will be greatly appreciated,
Thanks,
I found the solution!
It was parameters!
I was using a wrong type in the the List!
Parametross.Add(bd.MakeParameter("#val", "%" + txtFind.Text + "%",
DbType.String));
DbType.String vs. DbType.AnsiString
Although both DbType.String and DbType.AnsiString deal with character data, these datatypes are processed differently, and using the wrong data type can have a negative effect on the application’s performance. DbType.String identifies the parameter as a 2-byte Unicode value and is sent to the server as such.DbType.AnsiString causes the parameter to be sent as a multibyte character string. To avoid excessive string conversions, use:
DbType.AnsiString for char or varchar columns and parameters.
DbType.String for unichar and univarchar columns and parameters.
Source:
http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc20066.0115/html/adonet/adonet49.htm
In my query there is a:
....
where Table.Col1 like #val
But the column type was varchar and I should use DbType.AnsiString, instead of DbType.String
Parametross.Add(bd.MakeParameter("#val", "%" + txtFind.Text + "%",
DbType.AnsiString));
In my huge table I was making a lot of unnecesary casts and this is the reason why the performance drastically fall down!
Hope this will help someone,

Informix ODBC Query Appends Time on a date field

I have a .aspx page that has query to and informix database. This query is done via an odbc connection and is put into a datatable. Then this datatable is used as the datasource for a radio button group.
My problem is that for whatever reason the time is being appended to the radio button as "12:00:00 AM". This is odd because the informix field is a date field that does not include the time. If I were to run the query outside of the webpage it returns it without the time... "2012-06-15"
So in summary... what I am getting is: "6/15/2012 12:00:00 AM" and what I want is "06/15/2012"
The query is as follows:
"select DATE(attend_date) as attend_date from soar_major_table where major =? and active<>'N'"
The code that creates the datatable:
string connString;
connString = ConfigurationManager.ConnectionStrings [ "ERP" ].ConnectionString;
OdbcConnection conn = new OdbcConnection ( );
conn.ConnectionString = connString;
string sql = "select DATE(attend_date) as attend_date from soar_major_table where major =? and active<>'N' ";
OdbcCommand command = new OdbcCommand ( );
command.CommandText = sql;
command.Parameters.Add ( new OdbcParameter ( "major", major ) );
command.Connection = conn;
DataTable dt = new DataTable ( );
OdbcDataAdapter dataAdapter = new OdbcDataAdapter ( );
dataAdapter.SelectCommand = command;
try
{
conn.Open ( );
dataAdapter.Fill ( dt );
}
finally
{
if ( conn != null && conn.State == ConnectionState.Open )
{
command.Dispose ( );
dataAdapter.Dispose ( );
conn.Close ( );
}
}
return dt;
And lastly the population of the radio btn group:
if ( dt.Rows.Count > 0 )
{
rdoDate.DataSource = dt;
rdoDate.DataTextField = "attend_date";
rdoDate.DataValueField = "attend_date";
rdoDate.DataBind ( );
}
The problem is upstream of the Informix data server, I believe.
When you execute:
SELECT DATE(attend_date) ...
the server will return that value as a 4-byte integer representing the number of days since 1899-12-31 (so 1900-01-01 was day 1), which is the internal representation of a DATE in Informix.
Something in a higher layer is then treating it as a 'date + time' value and assuming midnight is the time since there was no time component in the date, and is then rubbing salt in the wound by formatting it in am/pm notation.
This will involve client-side tracing of what's going on. My suspicion (not founded on anything except limited knowledge of the ODBC drivers) is that the problem is occurring in the .NET layers rather than the ODBC driver. However, you're way outside my area of expertise once you're above the ODBC layer (and I don't claim great expertise in ODBC).
You may be able to isolate the problem to the client code by using SQLIDEBUG=2:xyz in the environment (you might need to set that with SETNET32 for Windows). If it works at all on Windows (it does on Unix), then you'll end up with a file with a name starting xyz_ followed by various groups of digits and letters. That file can be analyzed by sqliprint and will show you what was sent to the Informix data server and returned to your client. Assuming the SQL was not hacked en route to the server, then you'll see the date returned as a simple date, and the problem is definitively client-side. If the SQL is hacked en route, then that too is a client-side problem.
It at least gives a starting point for debugging.
Look very carefully at the data types of the types your code is using. In particular, some DBMS have a DATE type that includes time information, and you may need to avoid that interpretation. The SQL standard has DATE (no time), TIME (no date) and TIMESTAMP (date and time) types.

Resources