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.
Related
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?
I'm working on a project where I have to update a table in MySql by looping through it and update every row (one by one) in C#. NOTE: I have to update the value's of only one column in my table.
Now, I know this question has been asked (many times) before but after spending a lot of time searching around the internet I haven't found a solution for my problem.
More Explanation:
So I already know a few things that are necessary to make this work:
I know how to establish a (working) SSH connection.
I know how to establish a (working) MySql connection to my database.
I know how to run/execute a SELECT and UPDATE Query/Command in C#.
So the only thing that I need to make this whole thing work, is the loop itself.
Now, during my research I came across some answers that suggested using a Reader. I don't know if this is what I need and (if yes) how to use it correctly.
Below is the code that I have so far (for establishing the connections and executing query's).
My Code:
class ReaderDemo1
{
public static void Update()
{
Console.WriteLine("[Core] Opening Connection To Database...");
Database.openStockConn(Settings.databaseName, Settings.databaseUsername, Settings.databasePassword, Settings.sshHost, Settings.sshUsername, Settings.sshPassword);
if (Database.stockConn.State != ConnectionState.Open)
{
Database.openStockConn(Settings.databaseName, Settings.databaseUsername, Settings.databasePassword, Settings.sshHost, Settings.sshUsername, Settings.sshPassword);
}
Console.WriteLine("[Core] database connection is now open!\n");
MySqlCommand cmd = new MySqlCommand("SELECT value FROM catalog_product_entity_decimal", Database.stockConn);
try
{
Console.WriteLine("[Price] Updating Prices...");
MySqlCommand command = new MySqlCommand("UPDATE catalog_product_entity_decimal SET value= 1112 WHERE value_id= 4063", Database.stockConn);
command.ExecuteNonQuery();
Console.WriteLine("[Price] Prices Have Been Updated!");
}
catch
{
Console.WriteLine("Updating Failed!");
}
finally
{
if (Database.stockConn != null)
{
Database.stockConn.Close();
}
}
}
}
Just to give some extra context:
Database.cs is where I create the connections (SSH and MySql)
Settings.cs is where I have all the Login data for the SSH and MySql connections.
If you guys would want to know, I have already attempted a few things myself regarding the loop but (as I already mentioned) these attempts weren't successful.
My Attempt With Reader:
using (var reader = command.ExecuteReader())
{
var indexOfValue = reader.GetOrdinal("value");
while (reader.Read())
{
var price1 = reader.GetValue(indexOfValue);
Console.WriteLine("Executing Update Command...");
MySqlCommand cmd = new MySqlCommand("UPDATE catalog_product_entity_decimal SET value= 1222 WHERE entity_id= 759 AND entity_id= 839 AND entity_id= 881", con);
cmd.ExecuteNonQuery();
Console.WriteLine("Update Command Executed!");
}
}
As said, the code above didn't work how I wanted it to work (probably because it isn't actually doing anything now). Just to let you guys know, I am not stuck at an error. I'm just stuck on how to do this.
I hope one of you guys can help me with this or point me in the rigth direction and if you think that my question is a duplicate of another one, just tell me and I will look into it! :) Thanks in advance.
Kind regards,
LKS
EDIT:
In case you guys wanted to know, this is what my table looks like.
The table contains about 5600 rows, so these are just the top rows.
Like Frederiks answer, the thing that you are trying to do now is Looping over an table and execute the same Query over and over again.
Even if you manage to get your code working it would be very slow (because you have about 5600 rows to update).
So my suggestion is that you create an new table with the new value's in it (so the one's you wanted to have after your loop). Then just run a single update command to update your old table with values from your new table.
This option probably takes a few seconds and it will be done, so its much faster! :)
The Query you need should look something like this:
UPDATE old_table
INNER JOIN new_table
USING (column) --> // if you want to update a specific column
EDIT:
In addition/ update to my answer, this is how you can update your table more accurate:
UPDATE old_table
INNER JOIN new_table ON old_table.value_id = new_table.value_id // Use this to set your columns with unique values's
SET old_table.value = new_table.value // For the column with the value's you want to update
So, in the above code you update your old_table with the value's from your new_table. In this example you only update the value's from only one column (which you wanted).
You can expand the query for a different result.
I cannot think of any reason that you would want to read one line at a time and write one line at a time in a loop, but this is how you could do it.
Use one SqlCommand to read and a separate SqlCommand to update. Change the parameters in the loop for each update.
public static void Update()
{
Database.openStockConn(Settings.databaseName, Settings.databaseUsername, Settings.databasePassword, Settings.sshHost, Settings.sshUsername, Settings.sshPassword);
if (Database.stockConn.State != ConnectionState.Open)
{
Database.openStockConn(Settings.databaseName, Settings.databaseUsername, Settings.databasePassword, Settings.sshHost, Settings.sshUsername, Settings.sshPassword);
}
Console.WriteLine("[Core] database connection is now open!\n");
MySqlCommand cmd1 = new MySqlCommand("SELECT value FROM catalog_product_entity_decimal", Database.stockConn);
MySqlCommand cmd2 = new MySqlCommand("UPDATE catalog_product_entity_decimal SET value= 1112 WHERE value_id= 4063", Database.stockConn);
try
{
Console.WriteLine("[Price] Updating Prices...");
using (var reader = cmd1.ExecuteReader())
{
var indexOfValue = reader.GetOrdinal("value");
while (reader.Read())
{
var price1 = reader.GetValue(indexOfValue);
Console.WriteLine("Executing Update Command...");
cmd2.ExecuteNonQuery();
Console.WriteLine("Update Command Executed!");
}
}
}
catch
{
Console.WriteLine("Updating Failed!");
}
finally
{
if (Database.stockConn != null)
{
Database.stockConn.Close();
}
}
}
SQL is set based, which means that there is almost always a better approach then looping over a table.
That is also the case with your problem. You want to avoid looping over an entire table to update record-by-record, since this is terribly slow.
It is not obvious to me on what you're trying to achieve, since your code sample loops over a table and then inside your loop, you execute the same statement over and over again, but you're always updating the exact same records ?
If you want to update records in a table with information coming from another table, you might want to have a look at the UPDATE statement together with the JOIN clause as explained here.
You can try this change , by having the count of rows and then updating the records.
MySqlCommand cmd = new MySqlCommand("SELECT count(*) FROM catalog_product_entity_decimal", Database.stockConn);
try
{
var reader = cmd .ExecuteReader();
if(reader.Read()){
var totalRows=reader.GetInt32(0);
Console.WriteLine("[Price] Updating Prices...");
while(totalRows-->0){
MySqlCommand command = new MySqlCommand("UPDATE catalog_product_entity_decimal SET value= 1112 WHERE value_id= 4063", Database.stockConn);
command.ExecuteNonQuery();
}
Console.WriteLine("[Price] Prices Have Been Updated!");
}
}
catch
{
Console.WriteLine("Updating Failed!");
}
finally
{
if (Database.stockConn != null)
{
Database.stockConn.Close();
}
}
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?
I have been given the task of rewriting and old work application from classic .asp to ASP.NET that includes a database table that does not have an auto incremented primary key. We want to continue to use this table to maintain database integrity (it also has 80,000+ records!). The problem that I am running into is that I need to be able to pull the last item from the ID column of the database table regardless of how old the record is, increment that number and then include it in the new record to be inserted as the new record's ID number. How would I go about doing this? I have tried the ListItem, DataReader, DataTables, Generic Lists (as objects), and ArrayLists. I can pull the information and store it, but I cannot get the last item in the collection by itself. Any suggestions would be appreciated.
protected void GetPrimaryKey()
{
string strSQL = "";
try
{
OleDbConnection dbConn = new OleDbConnection();
dbConn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionString["ConnectionString"].ToString();
strSQL = "SELECT observationID FROM Observation";
OleDbCommand myCmd = new OleDbCommand(strSQL, dbConn);
OleDbReader reader;
ListItem item;
if (dbConn.State == ConnectionState.Colsed) dbConn.Open();
reader = myCmd.ExecuteReader();
while (reader.Read())
{
item = new ListItem();
item.Text = reader["observationID"].ToString();
}
reader.Close();
dbConn.Close();
myCmd.Dispose();
}
}
Populating the list is where this code is at. The last item still needs to be found then incremented, and the returned to the submit button event handler that starts this whole process. I know this code is missing a lot, but I didn't want to send my entire commented mess. Again, any help is appreciated. Thank You.
SELECT TOP 1 ObservationId FROM Observarion ORDER BY ObservationId DESC
This will return the last row id
If more than one person try to get this value to insert, you will run into an issue where you end up with the same Ids, unless that column is unique and will throw an error.
To minimize issues, you can do an inline select in your insert statement.
INSERT INTO Observation (ObservationId) VALUES(SELECT TOP 1 (ObservationId + 1) As NewObservationId FROM Observation ORDER BY ObservationId DESC)
Not sure if my syntax is completely correct but it should lead you in the right direction.
Try get the max observation ID in sql statement:
SELECT MAX(observationID) FROM Observation
Then increment it.
SELECT MAX(observationID) FROM Observation
will always return the max value regardless of how old the record is
just ask for next value autoincrement id from table:
SELECT IDENT_CURRENT('table_name');
enjoy xD.
Why don't you do your query like this?:
SELECT Top 1 observationID FROM Observation order by desc
If you have some sort of parameters or configuration table, I suggest you store the last value there and retrieve/update it each time you do an insert. This will prevent any issues in case you have 2 or more clients trying to insert a new record at the same time.
So im trying to write my list to a database and ive got some questions. First of all nothing seems to get saved into the database but theres no errors that would cause a crash sofar and I know for a fact that this piece of code has worked in the past for me:
public void saveToDb(int var1, string var2)
{
SqlCommand cmd = new SqlCommand("Insert into [table] (col1, col2) VALUES (#param1, #param2)", conn);
cmd.Parameters.AddWithValue("#param1", var1);
cmd.Parameters.AddWithValue("#param2", var2);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
I have a three piece layer going on, ui, logic and DAL. First of all it didnt work with the database and the app.config files in the DAL class library so those were moved to the main class library and now its not whining about the database already existing and it finds my connection string.
To send the data into this method im doing it this way:
for (int i = 0; i < myList.Count; i++)
{
da.saveToDb(myList.val1, myList.val2);
}
Its not giving me an error in the code but nothing seems to be saved or is resetted when I stop the program but I dont know which but I can see the variable being passed correctly by printing them before doing the insert so im guessing the Db is resetted? Is it putting my db into the debug folder and flushing it everytime or what?
Another thing is last time I did this it was a form so there was always going to be just one insert at a time, right now my list of objects could contain anywhere from 1 to alot of objects and with this way I would be opening and closing the db connection once for each object in my list right? How do you do bulk inserts? Been reading about datasets but they all seem to be about reading from a database not writing so im not sure about that. I did read from a database in another project using Dataset and adapter so that shouldnt be a problem but how do I bulk send a list into a table? To make it abit more tricky I cant just convert the entire list because of 10 propertys 8 is going to go into one table and the remaining 2 is to go in a second table so I would need to loop them and add the respective property to the respective dataset (or what you use).
edit
Well ive now made some adjustments and im trying two methods and none of which work but neither gives an error either which is getting frustrating.
First method:
public void saveToDb(int val1, string val2)
{
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["[MyConnectionString]"].ConnectionString);
SqlCommand cmd = new SqlCommand("Insert into [table] (val1, val2) VALUES (#param1, #param2)", conn);
cmd.Parameters.AddWithValue("#param1", val1);
cmd.Parameters.AddWithValue("#param2", val2);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}
And the second I try to sqlbulkcopy a table:
public void SaveToDb()
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["[MyConnectionString]"].ConnectionString))
{
conn.Open();
using (SqlBulkCopy bulk = new SqlBulkCopy(conn))
{
bulk.ColumnMappings.Add("col1", "col1");
bulk.ColumnMappings.Add("col2", "col1");
bulk.DestinationTableName = "[table]";
System.Diagnostics.Debug.Print("Open");
bulk.WriteToServer(tab);
foreach (DataRow row in tab.Rows)
{
foreach (var item in row.ItemArray)
{
System.Diagnostics.Debug.Print(item.ToString());
}
}
System.Diagnostics.Debug.Print("sending");
}
System.Diagnostics.Debug.Print("closing");
}
}
I shouldnt have to map it since the table contains the exact same columns as the table with the same naming (upper/lower case) but it gets weird with the primary key which increments so I mapped it so it should add the increment automatically but nothing gets inserted, the print sure enough shows the values but if I comment out the mapping I get an error saying that null cant be assigned (which is true my table wont take nulls) but the value shouldnt be null since its there in the table?
It seems you forgot the index.
for (int i = 0; i < myList.Count; i++)
{
da.saveToDb(myList[i].val1, myList[i].val2);
}
I think you are missing the connection object.
var cn = new SqlConnection().
Then you call open on the connection object.
connection.command will give access to the command object.