I have a function to delete rows from a table in a SQLite database. Function doesn't throw an exception or crash but it also fails to delete anything. "cmd_1.ExecuteNonQuery()" returns zero but the row I am trying to delete exists. What am I doing wrong here?
public void deleteEntryInTable(string tableName, string pKeyName, string pKeyValue)
{
conDB.Open();
cmd_1 = conDB.CreateCommand();
cmd_1.CommandText = $"DELETE FROM '{tableName}' WHERE '{pKeyName}'='{pKeyValue}'";
System.Diagnostics.Debug.WriteLine(cmd_1.CommandText);
try
{
int rows = cmd_1.ExecuteNonQuery();
System.Diagnostics.Debug.WriteLine(rows.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
conDB.Close();
}
To expand on what Haldo said; don't ever surround identifiers (table names, column names) with single quotes. SQLite does support it, in that if you use it somewhere that an identifier is expected you don't get a complaint, but in SQL single quotes are for denoting strings. I think it highly likely that your where clause is being treated like WHERE 'a' = 'b' and "string a equals string b" is always false.
See this fiddle: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=3b8c9364bedb03138403fa741485779a
The first query selects a result, the second does not
Other things to consider if you're in a "why isn't my database changing?" scenario
SQLite is a file based database. Are you absolutely sure you're looking in the same db file that your code is altering? If I had a dollar for every time that a dev has been working with a file based db, looking in the db in c:\temp\my.db and going "why can't I see the changes??" not realizing that the program is editing the file in c:\repos\myproject\bin\debug\my.db I'd retire
The other thing; are you absolutely sure that the id you're quoting truly exists in the db? plenty of times I've run a delete, then come to run the same delete again but second time round there is nothing left to delete so the table row count doesn't change..
Lastly, as a few people have commented, don't make SQL like you have done. Prefer a form like:
public void DeleteEntryInTable(string tableName, string pKeyName, string pKeyValue)
{
conDB.Open();
cmd_1 = conDB.CreateCommand();
cmd_1.CommandText = $"DELETE FROM {tableName} WHERE {pKeyName} = #pKeyValue";
cmd_1.Parameters.AddWithValue("#pKeyValue", pKeyValue);
System.Diagnostics.Debug.WriteLine(cmd_1.CommandText);
try
{
int rows = cmd_1.ExecuteNonQuery();
System.Diagnostics.Debug.WriteLine(rows.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
conDB.Close();
}
Don't ever, ever allow a user to supply data for table name or column name; they may only ever supply a value for pKeyValue, for example picking which record to delete off a drop down list in a webpage. If you switch to using SQLServer db in future, avoid AddWithValue
It actually looks like you're trying to write something to make your database life easier; have you considered learning something like Entity Framework?
Related
I have a C# project which connects to a TSQL database. The project runs multiple sequential update statements on a single table, eg.:
private void updateRows() {
string update1 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
string update2 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
string update3 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
// execute updates one after the other
}
Ideally, these statements will be batched to avoid making multiple round-trips to/from the database:
string update = "
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
";
My question is, if the statements are batched, does this increase the chance of deadlock errors occurring due to table scans?
As there is less time between each statement, I imagine that this could increase chances of deadlocks as one update could introduce a row or page lock, which may not be released by the time the next statement is executed. However if the update statements were not batched, then there is more time for row or page locks to be released between each update statement, therefore less chance of deadlocks occurring.
I guess you're not going to like my answer, here are my 2 cents, let me try and explain
first, your rowlock might not work, you might end up getting a table lock of your DDL doesn't allow SQL server to apply row locking in your transaction.
SQL likes set operations, it performs great when you update large datasets in one time.
I have a similar issue, I need to update large volumes of user transactions but I have no spare IO in the systems. I end-up using an 'ETL like' update,
In C# I'm using a bulk insert to get all data in the database in one go. Here is my method.
protected void BulkImport(DataTable table, string tableName)
{
if (!CanConnect)
return;
var options = SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints |
SqlBulkCopyOptions.UseInternalTransaction;
using (var bulkCopy = new SqlBulkCopy(_con.ConnectionString, options))
{
bulkCopy.DestinationTableName = tableName;
bulkCopy.BulkCopyTimeout = 30;
try
{
lock(table){
bulkCopy.WriteToServer(table);
table.Rows.Clear();
table.AcceptChanges();
}
}
catch (Exception ex)
{
var msg = $"Error: Failed the writing to {tableName}, the error:{ex.Message}";
Logger?.Enqueue(msg);
try
{
var TE= Activator.CreateInstance(ex.GetType(), new object[] { $"{msg}, {ex.Message}", ex });
Logger?.Enqueue(TE as Exception);
}
catch
{
Logger?.Enqueue(ex);
}
}
finally
{
bulkCopy.Close();
}
}
}
Please note that DataTable is not thread safe, you need to lock the DataTable when interacting (insert rows, clear table) with it.
Then I dump the data in a staging table and use a merge statement to bring the data into the database where I need it.
I do +100k records per second on 50 or so tables and have not had any performance or deadlock issues till now.
I have a one column table to keep track of number of visits. In Global.asax.cs I attempt to increment this value by 1 inside application_start but the field does't get updated. I get no exceptions but number of rows affected is always zero.
I tried the same simple query in SSMS and I get the same thing: 0 rows affected.
There is one int column in that table called NumVisits.
This is part of Application_Start in Global.asax.cs:
Application.Lock();
int iNumVisits = SomeClass.GetNumVisits();
SomeClass.UpdateNumVists(iNumVisits + 1);
Application.UnLock();
This is in SomeClass (BLL):
public static void UpdateNumVists(int iNumVisists)
{
LocaleRepository oLocaleRepository = new LocaleRepository(new SqlDbContext());
oLocaleRepository.UpdateNumVists(iNumVisists);
}
and this is in DAL:
public void UpdateNumVists(int iNumVisits)
{
int iRet = 0;
try
{
dbContext.Open();
List<SqlParameter> spParams = new List<SqlParameter>();
string sQuery = "update Visits set NumVisits = #NumVisits";
spParams.Add(dbContext.CreateSqlParam("#NumVisits", SqlDbType.Int, 0, iNumVisits));
dbContext.ExecuteSqlNonQuery(sQuery, spParams, ref iRet);
}
catch (Exception e)
{
throw e;
}
finally
{
dbContext.Close();
}
return;
}
I use the following for all commands using executeNonQuery:
public void ExecuteSqlNonQuery(string sQuery, List<SqlParameter> spParams, ref int iRet)
{
using (SqlCommand command = new SqlCommand())
{
command.CommandType = CommandType.Text;
command.Parameters.AddRange(spParams.ToArray<SqlParameter>());
command.Connection = DbConnection;
command.CommandText = sQuery;
try
{
iRet = command.ExecuteNonQuery();
}
catch(Exception e)
{ }
}
}
when the update command is executed, iRet is zero. I can't see why a simple update query would not work.
This is the create script I got from SSMS:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Visits](
[NumVisits] [int] NULL
) ON [PRIMARY]
GO
In general there are a few possible reasons why an update would not happen.
First, if the field is also an identity or calculated field, an ordinary update is not going to work. This doesn't look to be your case, but it is good to know for the future.
Next if there is a trigger on the table, it may be preventing the update. SSMS doesn't necessarily script triggers out when you script a table, so I can't tell if you have a trigger.
Third And most common, your application may not be sending what you expect as the update statement or even communicating with the database at all when you expect it to. This is often true when there is a problem of nulls. If your variable is not properly populating, then you may indeed be updating a null value to a null value. Run Profiler to capture exactly what is being sent when you try to do the update. Often when you see the statement that is actually being run, you will see the problem. Sometimes it is a matter of a missing space, sometimes a variable that was not populated that you thought was populated, etc.
Another possibility is that the user running the code has no update rights to the table. You should have gotten a message if this were the case.
If you have run Profiler, try running that exact code in SSMS and see if it updates. Sometimes you get a better error bubble up when you do that. Especially if your error handling in the application code is not well designed.
Of course if the table has no data, you need to do an insert not an update. Or the update might not be finding any records to update, try doing a select using the same conditions and it may turn out there is not record to update.
It seems like there is no data in the table, are there any records in Visits?
Was trying to use a prepStatement to insert into database with multiple columns. I want the statement to adapt when I try to insert INSERT INTO prepared Statement ignoring non valid number (replacing with Null) and inserting Valid ones
string prepStatement = "INSERT INTO outright_data.sample (`date`,`last`, `lastqty`) VALUES(#date #last, #lastqty)";
_prepStmt.CommandText = prepStatement;
if (frun)
{
_prepStmt.Parameters.AddWithValue("#date", datetime_var);
_prepStmt.Parameters.AddWithValue("#last", last_var);
_prepStmt.Parameters.AddWithValue("#lastqty", lastQty_var);
_prepStmt.Prepare();
frun = false;
}
else
{
_prepStmt.Parameters["#date"].Value = datetime_var;
_prepStmt.Parameters["#last"].Value = last_var;
_prepStmt.Parameters["#lastqty"].Value = lastQty_var;
}
i++;
_counter += _prepStmt.ExecuteNonQuery();
Imagine that lastQty_var variable is not accepted in the lastQty (but the datetime_var and last_var are accepted into their columns) column of mySQL table. When I execute, it raises an exception and stops the code. Is there a way for it to insert the other values and replace the invalid value (lastQty_var) by NULL in the database?
I tried validating the variable using is (problem is, the variable is a double on C#, but not accepted in my double column, so validating in C# types is not a big help);
I wanted to avoid big validating structures, since speed is very important in the whole proccess.
Any advice?
Thanks,
Tomas
Try this solution
https://stackoverflow.com/a/19870942/7457316
If execute fails then you are trying to fulfill what it needs to run coreectly
Table CLASS(id,name) in SQL Server 2008 Database
I have a CreateClass function in my aspx page:
public void CreateClass(object sender, EventArgs e)
{
string idclass= txtID.Text;
string name= txtName.Text;
string sql="select * From CLASS where id="+idclass;
Datatable class= excute...;
if(class!=null)
{
LabelError.Text="ID class is existed already!";
return;
}
else
{
... write into database...
}
}
Build program, first I input: txtID.Text= "DDD121" and txtName.Text="Class 2"
It still has 1 record which has id= DDD121 in Database but the program pass if statement and jump inside else statement. Why? Of course, in the else statement, the new class which id= DDD121 cannot be inserted into Database, but I need the Error Message shows.
I check the sql query in SQL server query window: select * from CLASS where id='DDD121' it works fine, 1 record show with id= DDD121.
Try to debug:
sql= "select * FROM CLASS WHERE ID=DDD121";
class=null;
Help!!!! is if(class!=null)the right way to check if Datatable has no row???
When you try to debug, I suspect that you will see that class is not null, but it is an empty DataTable.
That should give you also a hint as to what to test instead of class != null.
and yes, SQL-injection. But that is outside the scope of this one. I would suggest not to ignore it though.
If class is null in debug, while you are sure that a record should exist, i would suggest checking if you actually are connected correctly to your database and if you execute your query correctly.
execute... is definitely not working, so maybe in the actual code something goes awry.
Ah, make sure you put your desired value in between ''. Otherwise it won;t work indeed.
Not doing that has little to do with SQL-Injection by the way.
Just a hint, once you get this working, instead of filling in DDD121 in your textbox, fill in ABC';DROP TABLE CLASS; and see what happens ;) That is SQLinjection.
if(class!=null && class.Rows.Count>0)
{
LabelError.Text="ID class is existed already!";
return;
}
else
{
... write into database...
}
This Code check null and empty datatable
I have an object, say a note, whose parameters I want to save in a table(content, size, width, height, etc).
With having the sql string updateNotes:
UPDATE NOTES
SET
CONTENT=#content,
POSX=#posX,
POSY=#posY,
COLOR=#color,
ORIENTATION=#orientation,
HEIGHT=#height,
WIDTH=#width,
AUTHOR=#autho
WHERE SESSION =#sid
public void save()
{
SQLiteConnection conn = new SQLiteConnection(connectionString);
foreach (Note note in notes)
{
using (SQLiteCommand comm = new SQLiteCommand(updateNotes, conn))
{
comm.Parameters.AddWithValue("#content", note.getNoteItemContent());
comm.Parameters.AddWithValue("#posX", note.getSvi().Center.X);
comm.Parameters.AddWithValue("#posY", note.getSvi().Center.Y);
comm.Parameters.AddWithValue("#sid", sid);
comm.Parameters.AddWithValue("#color", note.getColor());
comm.Parameters.AddWithValue("#orientation", note.getSvi().Orientation);
comm.Parameters.AddWithValue("#height", note.getSvi().ActualHeight);
comm.Parameters.AddWithValue("#width", note.getSvi().ActualWidth);
comm.Parameters.AddWithValue("#author", note.getAuthor());
try
{
conn.Open();
comm.ExecuteNonQuery();
comm.Parameters.Clear();
}
catch (Exception e) { Microsoft.Surface.UserNotifications.RequestNotification("Database error", "Could not write to the database:" + e.Message); }
finally
{
if (conn != null) { conn.Close(); }
listLoaded = false;
}
}
}
}
The method above does indeed update the rows in the table but with one value for all the rows that result from the query.
As a solution I thought of reading the note id first and then iterate (i++), but given that some notes(represented as rows in the table) may be deleted, the id will not necessarily follow a consecutive numbering.
Another solution was to query the database for all the rows(notes) for the given session(sid) and store their id in an array and the update the notes whose id can be found in the array.
Do you know of any other better optimised solution? Or do you reckon I should use the array for storing the id's of the rows to be updated and the apply the query.
Thanks!
We seem to have resolved this in the question's comments but just to be clear on the answer (and hopefully give you something to accept), here is a summary:
When creating note objects, the data constituting those notes is selected from the database. As well as selecting outward facing data such as the note colour and content, we also select the primary key of each note and store this as a property of the note object.
Now, when a note object is updated, you can include its id in the where clause for the corresponding update statement and hence only update the one row corresponding to the modified note.
Glad I could provide some "enlightenment" -- as you put it.