I have a simple SQL Server database that has two columns, States and Capitals. When I select button2, I would like to loop through the States column and randomly select a different state for a specified number of loops (the user will enter this number in another textbox) without selecting the same state twice.
The selected state must appear in textBox1. I've programmed textBox2 with a series of switch statement to capture the capitals. I'm having problems with seeing the same state more than once.
int count = 0;
private void button2_Click(object sender, EventArgs e)
{
if (count == Double.Parse(textBox3.Text) - 1)
this.Close();
count++;
String connectionString = #"Data Source=DESKTOP-N2F01G5\MRMARLEE;Initial Catalog=States_Capitals;Integrated Security=True";
con.ConnectionString = connectionString;
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "SELECT TOP 1 * FROM StatesandCapitals ORDER BY NEWID() ";
String result = "";
using (con)
{
con.Open();
cmd.ExecuteNonQuery();
result = cmd.ExecuteScalar().ToString();
}
textBox1.Text = result;
}
The typical solution to this problem is to grab as many as you want all in the same query. If they want 5 items, instead of selecting TOP 1 five times in a loop, just select TOP 5 once. Put the results into a List or array in memory you reference later.
Additionally, don't pull the states for TextBox2 separately. Get this all down to the one query that retrieves all the data you need. Generally speaking, the fewer round-trips to the DB, the better your application will perform.
Firstly, there is no need of ExcecuteNonQuery().
ORDER BY NEWID() - The sample size is small (No. of states would typically be < 100?) so it may happen that even though the values of NEWID() are different each time you run the query, the ORDER may get the same State. Also, NEWID() has performance issues.
I suggest a C# + T-SQL approach as follows:
Declare a global instance of Random Class - Random rnd = new Random();
Get the total number of states - SELECT COUNT(*) FROM StatesandCapitals into a C# int variable (intNumStates) using ExecuteScalar().
Now, get a random number between 1 and intNumStates and assign it to another C# int variable say intRndStateNum - int intRndStateNum = rnd.Next(1, intNumStates + 1)
Finally, run query using ROW_NUMBER() equating it with the random number:
WITH RandomState AS
(
SELECT States, RANDOM_NUMBER() OVER (ORDER BY States) AS RowNum
FROM StatesandCapitals
)
SELECT States, RowNum
FROM RandomState
WHERE RowNum = ?
Replace ? with intRndStateNum as a Parameter sent to the query (I am not writing this code assuming you know this).
Related
I'm a student on 3th year of IT and I've found so much help from this forum, I am stuck with my project creating program in C# so I need your help. I have 4 tables in my database but we will focus on 2 of them materijal(Material) table and Skart(Scrap) table:
materijal Table(original) which has:
idmaterijal(INT),
naziv_materijala(VARCHAR),
kolicina_materiala(FLOAT),
mjerna_jedinica(VARCHAR),
sifra_materijala(INT)
material Table(eng) :
idmaterial(INT) ,
name_material(VARCHAR) ,
quantity_material(FLOAT) ,
measuring_unit(VARCHAR) ,
code_material(INT)
skart Table (original) which has:
idskart(INT),
materijal_idmaterijal(INT)
scrap Table(eng) :
idscrap(INT),
material_idmaterial(INT).
They are connected with Non-Identifying Relation 1:1. Now I have a Combo Box in my C# Form presented down below. I need to take from table Materials (name_material, quantity_material, mesuring_unit, code_material) and subtract the quantity that I insert in the textBox on my Form. For example I have 108.15 kg of HR in my Material table and I want to put 45 kg to scrap table , how do I do it in C#...
Pictures:
https://prnt.sc/hdvGuXJWNI0Q - My Diagram in MySQL (It is on my language but I translated every cell name for you guys)
https://prnt.sc/lgIRVTCOe670 - Materials Panel
https://prnt.sc/t1IGxpgJ8GHl - Choosing material via combo box in scraps panel
https://prnt.sc/6InfCPpezZga - Example on how I want to subtract
Down here I had an Idea so you would take from the picture skart panel https://prnt.sc/6InfCPpezZga and insert name_material via comboBox1 (it lists all of the materials from material table ... here I chose HR name of the material) and quantity_material via textBox7 in my case 45 with save button aka button 13 I need to update my material table by taking the quantity_material - textBox7 value and place in my dataGridView3 (witch is showing idscrap and material_idmaterial) code_material, name_material, quantity_material(but quantity I insert in textBox7) and measuring_unit , I know that placing it and showing with dataGridView I have to do the Inner Join or left join but I cant quite understand it ...
private void button13_Click(object sender, EventArgs e)
{
konekcija.Open();
//OleDbCommand komanda = new OleDbCommand();
MySqlCommand komanda = new MySqlCommand();
komanda.Connection = konekcija;
komanda.CommandType = CommandType.Text;
komanda.CommandText = "update materijal set naziv_materijala='" + comboBox1 + "' where kolicina_materijala='" + textBox7.Text + "'";
komanda.ExecuteNonQuery();
konekcija.Close();
comboBox1.Text = "";
textBox7.Text = "";
MessageBox.Show("Uspjesno ste unijeli izabrani Škart");
}
The correct approach for code like this would be something like:
private void UpdateNameFromQuantityButton_Click(object sender, EventArgs e)
{
//konekcija.Open(); //do not keep a connection hanging around; keep a connectionString instead
var sql = "update materijal set naziv_materijala=#nm where kolicina_materijala=#km";
using var konekcija = new MySqlConnection(connectionString);
using var komanda = new MySqlCommand(sql, konekcija);
konekcija.Open();
komanda.Parameters.AddWithValue("#nm", materialNameComboBox.Text);
komanda.Parameters.AddWithValue("#km", quantityTextBox.Text);
var rowsAffected = komanda.ExecuteNonQuery(); //handy to capture this to know if nothing was changed (0 rows affected)
konekcija.Close();
materialNameComboBox.Text = "";
quantityTextBox.Text = "";
MessageBox.Show("Uspjesno ste unijeli izabrani Škart");
}
use using on your command and connection; make a new connection object when you need it. Reusing the same one can cause issues with some databases. The number of MySqlConnection objects in your code is unrelated to the number of actual TCP connections to the database server because TCP connections are pooled by a mechanism elsewhere. Your use of Open and Close actually just leases and returns a pooled connection
always use #parameters in your SQLs to put data into the command. Always*
close your connection as soon as possible after you're done with it
* Unless the "data" is something like a table name or column name in which case it cannot be parameterized; try and avoid such scenarios - it's usually possible
I am developing a cricket simulation and i need to retrieve certain statistics from a players data. I've got the following code.
public List<float> BattingData()
{
con.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString.ToString();
string query = "SELECT [INNS], [NOT OUTS], [AVG] FROM [" + batTeam + "] WHERE [Player] = '" + name + "';";
SqlCommand com = new SqlCommand(query, con);
con.Open();
using (SqlDataReader reader = com.ExecuteReader())
{
if(reader.HasRows)
{
while (reader.NextResult())
{
Innings = Convert.ToInt32(reader["INNS"]);
NotOuts = Convert.ToInt32(reader["NOT OUTS"]);
Avg = Convert.ToSingle(reader["AVG"]);
}
}
}
con.Close();
OutRatePG = (Innings = NotOuts) / Innings;
OutRatePB = OutRatePG / 240;
RunsPB = Avg / 240;
battingData.Add(OutRatePB);
battingData.Add(RunsPB);
return battingData;
}
The error that I am getting is that when I try to divie by 'Innings' it is saying cannot divide by zero, so I think the variables are being returned as zero and no data is being assigned to them.
This line is the issue:
while (reader.NextResult())
What this does is move the reader to the next resultset, ignoring the rest of the rows unread. To advance a reader to the next row, you need to call reader.Read() instead.
You have some other issues with your code:
You appear to have a separate table for each team. This is incorrect database design. You should create a Team table, with each team in it, and then foreign key your TeamResults table to it. Query it using INNER JOIN.
You are concatenating user-entered values to your query. This leaves you open to SQL injection attacks. Use parameters instead. (You cannot parameterize a table name, another reason you should do as above 1.)
You do not need to check for HasRows. If there are no rows, Read() will return false.
It looks like you only want one row. If that is the case you don't want a while(reader.Read()) loop, instead if(reader.Read()). (If you only need a single value, you can refactor the code to use command.ExecuteScalar().)
In database records check if value for Innings has 0
also you can try the below code before performing any operation.
> if(Innings>0) { OutRatePG = (Innings - NotOuts) / Innings; }
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 am creating a winform application in c#.and using sql database.
I have one table, employee_master, which has columns like Id, name, address and phone no. Id is auto increment and all other datatypes are varchar.
I am using this code to get the next auto increment value:
string s = "select max(id) as Id from Employee_Master";
SqlCommand cmd = new SqlCommand(s, obj.con);
SqlDataReader dr = cmd.ExecuteReader();
dr.Read();
int i = Convert.ToInt16(dr["Id"].ToString());
txtId.Text = (i + 1).ToString();
I am displaying on a textBox.
But when last row from table is deleted, still I get that value which is recently deleted in textbox
How should I get the next autoincrement value?
To get the next auto-increment value from SQLServer :
This will fetch the present auto-increment value.
SELECT IDENT_CURRENT('table_name');
Next auto-increment value.
SELECT IDENT_CURRENT('table_name')+1;
------> This will work even if you add a row and then delete it because IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
try this:
SELECT IDENT_CURRENT('tbl_name') + IDENT_INCR('tbl_name');
If you are using Microsoft SQL Server. Use this statement to get current identity value of table. Then add your seed value which you have specified at time of designing table if you want to get next id.
SELECT IDENT_CURRENT(<TableName>)
As for me, the best answer is:
dbcc checkident(table_name)
You will see two values (probably same)
current identity value , current column value
When you delete a row from the table the next number will stay the same as it doesnt decrement in any way.
So if you have 100 rows and you deleted row 100. You would have 99 rows but the next number is still going to be 101.
select isnull((max(AddressID)+1),1) from AddressDetails
the max(id) will get you maximum number in the list pf employee_master
e.g. id = 10, 20, 100 so max will get you 100
But when you delete the record it must have been not 100
So you still get 100 back
One important reason for me to say this might be the issue because you are not using order by id in your query
For MS SQL 2005 and greater:
Select Cast(IsNULL(last_value,seed_value) As Int) + Cast(increment_value As Int) As NextID
From sys.identity_columns
WHERE NAME = <Table_Name>
Just a thought, if what you wanted was the last auto-number that you inserted on an already open connection try using:
SELECT ##IDENTITY FROM...
from that connection. That's the best way to keep track of what has just happened on a given connection and avoids race conditions w/ other connections. Getting the maximum identity is not generally feasible.
SqlConnection con = new SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=databasename;User ID=sa;Password=123");
con.Open();
SqlCommand cmd = new SqlCommand("SELECT TOP(1) UID FROM InvoiceDetails ORDER BY 1 DESC", con);
SqlDataReader reader = cmd.ExecuteReader();
//won't need a while since it will only retrieve one row
while (reader.Read())
{
string data = reader["UID"].ToString();
//txtuniqueno.Text = data;
//here is your data
//cal();
//txtuniqueno.Text = data.ToString();
int i = Int32.Parse(data);
i++;
txtuid.Text = i.ToString();
}
I am working on a console application to insert data to a MS SQL Server 2005 database. I have a list of objects to be inserted. Here I use Employee class as example:
List<Employee> employees;
What I can do is to insert one object at time like this:
foreach (Employee item in employees)
{
string sql = #"INSERT INTO Mytable (id, name, salary)
values ('#id', '#name', '#salary')";
// replace #par with values
cmd.CommandText = sql; // cmd is IDbCommand
cmd.ExecuteNonQuery();
}
Or I can build a balk insert query like this:
string sql = #"INSERT INTO MyTable (id, name, salary) ";
int count = employees.Count;
int index = 0;
foreach (Employee item in employees)
{
sql = sql + string.format(
"SELECT {0}, '{1}', {2} ",
item.ID, item.Name, item.Salary);
if ( index != (count-1) )
sql = sql + " UNION ALL ";
index++
}
cmd.CommandType = sql;
cmd.ExecuteNonQuery();
I guess the later case is going to insert rows of data at once. However, if I have
several ks of data, is there any limit for SQL query string?
I am not sure if one insert with multiple rows is better than one insert with one row of data, in terms of performance?
Any suggestions to do it in a better way?
Actually, the way you have it written, your first option will be faster.
Your second example has a problem in it. You are doing sql = + sql + etc. This is going to cause a new string object to be created for each iteration of the loop. (Check out the StringBuilder class). Technically, you are going to be creating a new string object in the first instance too, but the difference is that it doesn't have to copy all the information from the previous string option over.
The way you have it set up, SQL Server is going to have to potentially evaluate a massive query when you finally send it which is definitely going to take some time to figure out what it is supposed to do. I should state, this is dependent on how large the number of inserts you need to do. If n is small, you are probably going to be ok, but as it grows your problem will only get worse.
Bulk inserts are faster than individual ones due to how SQL server handles batch transactions. If you are going to insert data from C# you should take the first approach and wrap say every 500 inserts into a transaction and commit it, then do the next 500 and so on. This also has the advantage that if a batch fails, you can trap those and figure out what went wrong and re-insert just those. There are other ways to do it, but that would definately be an improvement over the two examples provided.
var iCounter = 0;
foreach (Employee item in employees)
{
if (iCounter == 0)
{
cmd.BeginTransaction;
}
string sql = #"INSERT INTO Mytable (id, name, salary)
values ('#id', '#name', '#salary')";
// replace #par with values
cmd.CommandText = sql; // cmd is IDbCommand
cmd.ExecuteNonQuery();
iCounter ++;
if(iCounter >= 500)
{
cmd.CommitTransaction;
iCounter = 0;
}
}
if(iCounter > 0)
cmd.CommitTransaction;
In MS SQL Server 2008 you can create .Net table-UDT that will contain your table
CREATE TYPE MyUdt AS TABLE (Id int, Name nvarchar(50), salary int)
then, you can use this UDT in your stored procedures and your с#-code to batch-inserts.
SP:
CREATE PROCEDURE uspInsert
(#MyTvp AS MyTable READONLY)
AS
INSERT INTO [MyTable]
SELECT * FROM #MyTvp
C# (imagine that records you need to insert already contained in Table "MyTable" of DataSet ds):
using(conn)
{
SqlCommand cmd = new SqlCommand("uspInsert", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter myParam = cmd.Parameters.AddWithValue
("#MyTvp", ds.Tables["MyTable"]);
myParam.SqlDbType = SqlDbType.Structured;
myParam.TypeName = "dbo.MyUdt";
// Execute the stored procedure
cmd.ExecuteNonQuery();
}
So, this is the solution.
Finally I want to prevent you from using code like yours (building the strings and then execute this string), because this way of executing may be used for SQL-Injections.
look at this thread,
I've answered there about table valued parameter.
Bulk-copy is usually faster than doing inserts on your own.
If you still want to do it in one of your suggested ways you should make it so that you can easily change the size of the queries you send to the server. That way you can optimize for speed in your production environment later on. Query times may v ary alot depending on the query size.
The batch size for a SQL Server query is listed at being 65,536 * the network packet size. The network packet size is by default 4kbs but can be changed. Check out the Maximum capacity article for SQL 2008 to get the scope. SQL 2005 also appears to have the same limit.