We have a C# mobile application with SQLite database.
We are having a larger inventory database, such as 30k or 100k items. The database file is 12MB on a flash memory card.
Running a simpler SELECT query with limit takes 10-15 seconds.
select id,invitem,invid,cost from inventory
where itemtype = 1 and
(invitem like '%5204d%' or invid like '%5204d%')
limit 25
sometimes a category is involved too,
select id,invitem,invid,cost from inventory
where itemtype = 1 and
categoryid=147 and
(invitem like '%5204d%' or invid like '%5204d%')
limit 25
Indexes are created on:
cmd.CommandText = "CREATE INDEX IF NOT EXISTS idx_inventory_categoryid ON " + this.TableName + " (categoryid);";
cmd.ExecuteNonQuery();
cmd.CommandText = "CREATE INDEX IF NOT EXISTS idx_inventory_itemtype ON " + this.TableName + " (itemtype);";
cmd.ExecuteNonQuery();
cmd.CommandText = "CREATE INDEX IF NOT EXISTS idx_inventory_invitem ON " + this.TableName + " (invitem);";
cmd.ExecuteNonQuery();
Those two fields in Like are VARCHAR, the others are NUMERIC.
Can this select query be optimized more on a mobile device?
The problem is the initial % in your where..like clause. The index cannot be used in theis query, so a table scan is the only way it can be done. Adding the category id will help - at least it can use that inxdex.
You can use EXPLAIN to check whether the indices are actually used. I would guess that the wildcard at the beginning of like '%5204d%' disables any use of indices on invitem.
Related
I want to use selected item from my combo box for my SqlDataReader.
Which wrong in my syntax?
string varfunction = cbFunctionClass.SelectedItem.ToString();
con.Open();
SqlCommand sqlFunName = new SqlCommand("SELECT " + varfunction + " FROM sdn_cd_allclass WHERE MIDCLASS_CODE = '" + cbMiddleClass.Text + "'", con);
SqlDataReader sqlFunNameReader = sqlFunName.ExecuteReader();
while (sqlFunNameReader.Read())
{ lbFunctionClassName.Text = sqlFunNameReader[varfunction].ToString(); }
sqlFunNameReader.Close();
con.Close();
I need to use varfunction to select SQL column.
If varfunction contains a single digit, your query would read something like
SELECT 7 FROM sdn_cd_allclass...
for example. This would not select a column named 7 but the literal 7, i.e. an integer with the value of 7. And the column in the result, that holds this integer has no name, especially isn't its name 7.
If you want to select a column named 7 (which probably isn't the best idea BTW) you have to quote it by putting square brackets around it, so that the query becomes
SELECT [7] FROM sdn_cd_allclass...
So try
... "SELECT [" + varfunction + "] FROM sdn_cd_allclass..." ...
And as an aside, like already commented many times, I also recommend you to rework this and use parameterized queries (for the literals, i.e. the value in the WHERE clause of your current query, it won't work with identifiers, i.e. the column name).
So I have a database that has student information and I want to add a randomly generated grade for each one of them.
I created a method that generates a random grade letter between A-F.
However, when I update my database table all the courses that the students are taking get the same grade I want to give each different course a different grade .
command.CommandText = "UPDATE CurrentCourses SET CurrenteGrade ='" +
RandomLetter(grades) + "'";
command.ExecuteNonQuery();
I believe that if there is a way to run this query for each different row it would solve my problem. But I couldn't really get it to work. I used Microsoft Access to create my database.
Thank you
The update statement affects all the rows in the database table:
"UPDATE CurrentCourses SET CurrenteGrade ='" +
RandomLetter(grades) + "'"
Without a WHERE clause, this code could be run in a loop 100 times with a random grade each time, but every row in the table will say whatever grade was randomized last. If the last loop picked 'E' as the random grade, then all rows in the table will be graded E, depite the fact that they have, in the previous 5 seconds, changed grade 99 times already (all rows change each time the code is run. They only stop changing when the code stops being executed)
If you want to change all course rows to the same grade:
sqlCommand.CommandText = "UPDATE CurrentCourses SET CurrenteGrade = ? WHERE Course_ID = ?";
And then populate the parameters of the SqlCommand:
sqlCommand.Parameters.AddWithValue("grade", RandomLetter(grades));
sqlCommand.Parameters.AddWithValue("course", "SoftwareEngineering101");
This is the sort of thing you'd run many times (in a loop maybe) with a different course ID each time. The idea is that you just change the parameter values, then re-run the query:
sqlCommand.CommandText = "UPDATE CurrentCourses SET CurrenteGrade = ? WHERE Course_ID = ?";
sqlCommand.Parameters.AddWithValue("grade", "a"); //dummy values
sqlCommand.Parameters.AddWithValue("course", "a"); //dummy values
//the loop does the real work, repeatedly overwiting param values and running:
foreach(var course in myCoursesArray){
sqlCommand.Parameters["grade"] = RandomLetter(grades);
sqlCommand.Parameters["course"] = course;
sqlCommand.ExecuteNonQuery()
}
With access, using ? for parameter placeholders in the SQL, it is important that you then add your paramters in the same order as the ? marks appear in the sql. The names are irrelevant - this is not so in more powerful DB systems like sqlserver, where the SQL has named parameters and the names given in the c# code do matter. In our Access based code though, the only thing that matters about the name is to use it when overwriting the parameter value with a new one in the loop
Note; there are good reasons to avoid using .AddWithValue, but I won't get into those here. It's more important to avoid using string concatenation to build values into your SQLs. See bobbytables.com for more info
First your exact question: You need a WHERE statement that filters the update down to just a single row. This is usually done with an ID number or other unique identifier for the specific row(student in this case).
Second: Concatenating strings together with raw data can lead to errors and also security issues. For example, things like having a single quote in your string data will cause havoc. You should use up SqlParameters. https://www.dotnetperls.com/sqlparameter
You could first query for each row in the table(s) that contain the students and the course.
And then for each row in the data set execute your method to update that row with a random letter grade.
for example
foreach (DataRow dr in ds.tables[0].rows)
{
command.CommandText = "UPDATE CurrentCourses SET CurrenteGrade ='" +
RandomLetter(grades) + "'" + "WHERE PRIMARYKEY = dr.id"
command.ExecuteNonQuery();
}
Try something like;
UPDATE CurrentCourses SET CurrenteGrade = (select top 1 gradeName from grades ORDER BY NEWID())
In your code, you are not setting different courses for students. Because before executing the query, your query takes just one grade and update all the rows.
You can perform it using SQL easily.
You can do it with pure T-SQL:
declare #idColumn int
DECLARE #MIN INT=1; --We define minimum value, it can be generated.
DECLARE #MAX INT=100; --We define maximum value, it can be generated.
select #idColumn = min( Id ) from CurrentCourses
while #idColumn is not null
begin
Update CurrentCourses
SET CurrenteGrade = #MIN+FLOOR((#MAX-#MIN+1)*RAND(CONVERT(VARBINARY,NEWID())));
select #idColumn = min( Id ) from CurrentCourses where Id > #idColumn
end
the code above is looping over all the records (replace Id with your primary key) and generates random number between 1-100 (see comments where you can set new values) and updates the random number in each CurrentGrade record.
format and concatenate that command into your CommandText and execute ExecuteNonQuery()
Using this UPDATE command without any WHERE-clause will affect every record each time. Note that the random letter is generated before the query is executed. Therfore the query will run with a single grade letter.
You could run this query for each course in turn with an appropriate WHERE-clause that selects one course each time. But this is not efficient.
Or, much better, you could apply a random function in SQL itself, that is evaluated for each record (i.e. let MySQL choose a random grade).
UPDATE CurrentCourses
SET CurrenteGrade = SUBSTRING('ABCDEF', FLOOR(RAND() * 6) + 1, 1)
In your code
command.CommandText = #"UPDATE CurrentCourses
SET CurrenteGrade = SUBSTRING('ABCDEF', FLOOR(RAND() * 6) + 1, 1)";
command.ExecuteNonQuery();
This requires no loops and no command parameters.
Note that RAND() returns a random number between 0.0 and 1.0 (including 0.0 but excluding 1.0). Therefore FLOOR(RAND() * 6) generates a whole number in the range [0 .. 5]. 1 is added to get a number in the range [1 .. 6] used as index in the string 'ABCDEF' for the SUBSTRING function that cuts out one letter.
Please put the where condition in your update query.Without where condition it will update all data in the table.
I have a problem while reading from SQL Server in C#. It is happening in SSIS, I have inserted a C# script in data flow.
I am using the code below:
using (SqlConnection connection = new SqlConnection(connectionString))
{
string vendorName = Row.VendorName.ToString().Substring(0,1).ToUpper() + Row.VendorName.ToString().Substring(1);
using (SqlCommand command = new SqlCommand("Select TOP 1 * from Logs where MessageId = '" + Row.id.ToString() + "'" +
"AND name = (Select Id from Names where vendor_name = '" + vendorName +
"order by CreatedDate desc", connection))
{
connection.Open();
string status = "";
using (SqlDataReader oReader = command.ExecuteReader())
{
while (oReader.Read())
{
status = oReader["Status"].ToString();
}
}
if (String.IsNullOrEmpty(status))
{
SaveDataToDB(Row.id, Row.VendorName, "Unknown");
}
}
}
In the Logs table, there are about 10000 rows, and the related datasource, where Row data belongs to, has around 9000 records. The problem is that, even though the query is working well, in the script it sometimes brings status value as null, because it cannot find the record in the SQL. I am getting the query and copy/pasting it to SQL, executing the query brings result there, but not in C# somehow. For example, I am running the C# two times in sequence, at the first time it says Status is null for the id: 354456, but when I run it at the second time it finds 354456 correctly but saying that status of 354499 is null.
Any idea for me to solve this issue? I really appreciate for any help.
According to me, this could be due to order of evaluation of user defined values embedded within the query. Could be the first dynamic value might be evaluated before the one in the inner query.
As I am not sure about the value to variable binding, however, I would recommend you to check following points;
a) externalise both your variable (vendor name and row id) outside and evaluate and ensure it has respective values
b) and then form your query statement with the evaluated values
May be you can debug and see the CommandText of command object just before call Execute.
your code is really inefficient, you should cache vendorname in a string and do all the substring operations on that.
for example:
string vendorName = Convert.ToString(Row.VendorName);
vendorName = vendorName.Substring(0,1).ToUpper() + vendorName.Substring(1);
instead of selecting all the columns, select the specific column for a speed up select Status from.
try to debug your code first, see which id you are getting and what is the result of your query.
its really hard to debug your code without any debug information.
change your code to this (Select Id from Names where vendor_name = '" + vendorName + "')" and put a blank space next to every " character e.g. " AND instead of "AND
I have a query to insert a row into a table, which has a field called ID, which is populated using an AUTO_INCREMENT on the column. I need to get this value for the next bit of functionality, but when I run the following, it always returns 0 even though the actual value is not 0:
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', " + bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ")";
int id = Convert.ToInt32(comm.ExecuteScalar());
According to my understanding, this should return the ID column, but it just returns 0 every time. Any ideas?
EDIT:
When I run:
"INSERT INTO INVOICE (INVOICE_DATE, BOOK_FEE, ADMIN_FEE, TOTAL_FEE, CUSTOMER_ID) VALUES ('2009:01:01 10:21:12', 50, 7, 57, 2134);last_insert_id();"
I get:
{"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'last_insert_id()' at line 1"}
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertStatement; // Set the insert statement
comm.ExecuteNonQuery(); // Execute the command
long id = comm.LastInsertedId; // Get the ID of the inserted item
[Edit: added "select" before references to last_insert_id()]
What about running "select last_insert_id();" after your insert?
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', "
+ bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ");";
+ "select last_insert_id();"
int id = Convert.ToInt32(comm.ExecuteScalar());
Edit: As duffymo mentioned, you really would be well served using parameterized queries like this.
Edit: Until you switch over to a parameterized version, you might find peace with string.Format:
comm.CommandText = string.Format("{0} '{1}', {2}, {3}, {4}, {5}); select last_insert_id();",
insertInvoice, invoiceDate.ToString(...), bookFee, adminFee, totalFee, customerID);
Use LastInsertedId.
View my suggestion with example here: http://livshitz.wordpress.com/2011/10/28/returning-last-inserted-id-in-c-using-mysql-db-provider/
It bothers me to see anybody taking a Date and storing it in a database as a String. Why not have the column type reflect reality?
I'm also surprised to see a SQL query being built up using string concatenation. I'm a Java developer, and I don't know C# at all, but I'd wonder if there wasn't a binding mechanism along the lines of java.sql.PreparedStatement somewhere in the library? It's recommended for guarding against SQL injection attacks. Another benefit is possible performance benefits, because the SQL can be parsed, verified, cached once, and reused.
Actually, the ExecuteScalar method returns the first column of the first row of the DataSet being returned. In your case, you're only doing an Insert, you're not actually querying any data. You need to query the scope_identity() after you're insert (that's the syntax for SQL Server) and then you'll have your answer. See here:
Linkage
EDIT: As Michael Haren pointed out, you mentioned in your tag you're using MySql, use last_insert_id(); instead of scope_identity();
I have a query to insert a row into a table, which has a field called ID, which is populated using an AUTO_INCREMENT on the column. I need to get this value for the next bit of functionality, but when I run the following, it always returns 0 even though the actual value is not 0:
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', " + bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ")";
int id = Convert.ToInt32(comm.ExecuteScalar());
According to my understanding, this should return the ID column, but it just returns 0 every time. Any ideas?
EDIT:
When I run:
"INSERT INTO INVOICE (INVOICE_DATE, BOOK_FEE, ADMIN_FEE, TOTAL_FEE, CUSTOMER_ID) VALUES ('2009:01:01 10:21:12', 50, 7, 57, 2134);last_insert_id();"
I get:
{"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'last_insert_id()' at line 1"}
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertStatement; // Set the insert statement
comm.ExecuteNonQuery(); // Execute the command
long id = comm.LastInsertedId; // Get the ID of the inserted item
[Edit: added "select" before references to last_insert_id()]
What about running "select last_insert_id();" after your insert?
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', "
+ bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ");";
+ "select last_insert_id();"
int id = Convert.ToInt32(comm.ExecuteScalar());
Edit: As duffymo mentioned, you really would be well served using parameterized queries like this.
Edit: Until you switch over to a parameterized version, you might find peace with string.Format:
comm.CommandText = string.Format("{0} '{1}', {2}, {3}, {4}, {5}); select last_insert_id();",
insertInvoice, invoiceDate.ToString(...), bookFee, adminFee, totalFee, customerID);
Use LastInsertedId.
View my suggestion with example here: http://livshitz.wordpress.com/2011/10/28/returning-last-inserted-id-in-c-using-mysql-db-provider/
It bothers me to see anybody taking a Date and storing it in a database as a String. Why not have the column type reflect reality?
I'm also surprised to see a SQL query being built up using string concatenation. I'm a Java developer, and I don't know C# at all, but I'd wonder if there wasn't a binding mechanism along the lines of java.sql.PreparedStatement somewhere in the library? It's recommended for guarding against SQL injection attacks. Another benefit is possible performance benefits, because the SQL can be parsed, verified, cached once, and reused.
Actually, the ExecuteScalar method returns the first column of the first row of the DataSet being returned. In your case, you're only doing an Insert, you're not actually querying any data. You need to query the scope_identity() after you're insert (that's the syntax for SQL Server) and then you'll have your answer. See here:
Linkage
EDIT: As Michael Haren pointed out, you mentioned in your tag you're using MySql, use last_insert_id(); instead of scope_identity();