Pass data from database table to an array - c#

i am having some trouble trying to stock one row (Id) of my table (contas) to my array (ids[ ]), according to the requisites (posicao like 'Saúde', convenio like convenio and i can't have any duplicated Id's). The variables 'e' and 'i' are merely accountants. I'm kinda new to coding so please don't judge me hard. Also it's my first post on this site + I don't know english very well.
This is my code:
// Here I select the number of Id's that are compatible with my requisites, so far so ok
cmd = new SqlCommand("select COUNT(Id) from contas where posicao like 'Saúde' and convenio like '" +convenio+"'", con);
cmd.Parameters.AddWithValue("#Id", Id);
numeroidentista = (cmd.ExecuteScalar()).ToString();
// Here I create my arrays to store the data
int[] ids = new int[Convert.ToInt32(numeroidentista)];
string[] idst = new string[Convert.ToInt32(numeroidentista)];
string[] inst = new string[Convert.ToInt32(numeroidentista)];
// And here I tryied so hard and it doesn't even matter
while (e < Convert.ToInt32(numeroidentista))
{
SqlCommand cmdao = new SqlCommand(inst[i].ToString(), con);
inst[i] = "SELECT Id FROM contas where posicao like 'Saúde' and convenio like '" + convenio + "' and Id > '" + ids[i] + "'";
SqlDataReader reader = cmdao.ExecuteReader();
if (reader.Read())
{
while (i < Convert.ToInt32(numeroidentista))
{
idst[i] = reader["Id"].ToString();
ids[i] = Convert.ToInt32(idst[i]);
i++;
}
}
e++;
reader.Close();
}

There are several problems with your code; however, the main one is, that you are using inst[i] before assigning it a value:
SqlCommand cmdao = new SqlCommand(inst[i].ToString(), con); // Using inst[i] here.
inst[i] = "SELECT Id FROM contas where posicao like 'Saúde' and convenio like '" + convenio +
"' and Id > '" + ids[i] + "'"; // But assigning it here.
Swap the lines
inst[i] = "SELECT Id FROM contas where posicao like 'Saúde' and convenio like '" + convenio +
"' and Id > '" + ids[i] + "'";
SqlCommand cmdao = new SqlCommand(inst[i].ToString(), con);
And since inst[] is a string array, no conversion to string is required:
SqlCommand cmdao = new SqlCommand(inst[i], con);
You have other superfluous conversions. You convert the COUNT(id), which is already an int to a string, just to convert it to and int again later:
//Not shown
string numeroidentista;
numeroidentista = (cmd.ExecuteScalar()).ToString();
int[] ids = new int[Convert.ToInt32(numeroidentista)];
Change it to
int numeroidentista = (int)cmd.ExecuteScalar();
int[] ids = new int[numeroidentista];
Instead of fixed size arrays I would use variable size lists (List<T>). This would make the first SELECT COUNT(Id) superfluous as well.
Another question is what type is Id in the table? You sore it in an int array, but the SQL surrounds it with apostrophes, suggesting that it has a text type. It should be and int in the table as well, in which case there should be no apostrophes in the SQL: ... and Id > " + ids[i];. You are also using ids[i] before assigning it a value.
It would be preferred to use command parameters everywhere instead of string concatenation. But since you are storing the resulting SQL, you might want to keep it this way for later reference.
Why are you using 3 different arrays. By using a class string with the 3 fields, the code would become easier. Using this class
public class ContaInfo
{
public int Id { get; set; }
public string IdString { get; set; }
public string Instruction { get; set; }
}
You could declare a list like
var contas = new List<ContaInfo>();
and then add items with
contas.Add(new ContaInfo { Id = ..., IsString = ..., Instruction = ...});
Finally, using-statements automatically close and dispose resources.
I tried to rewrite the code; however, I do not understand the logic behind all this. You are creating an array of a fixed size, but then you have 2 nested loops, potentially creating more entries than the size of the arrays.

1- You have several potential issues in your code. When you are doing the conversions, you are trusting the type in the string be convertable to your desired type for example int. Examples are Convert.ToInt32(numeroidentista) , Convert.ToInt32(idst[i]) , the recommended way is to do these using TryParse, for example :
if(!Int32.TryParse(numeroidentista, out int numeroidentistaInt))
{
Console.WriteLine($"Error converting {numeroidentista} to int value");
return;
}
2- Also for efficiency, do the conversions only once, you have repeated Convert.ToInt32(numeroidentista) 5 times . Just do it once and before your while loop.
3- And finally as pointed in the comment above, you have not initialized your inst array before using it.
4- As another tip, try to use $"" convention when forming a string using constants and variables. For example you can replace your first string with $"select COUNT(Id) from contas where posicao like 'Saúde' and convenio like '{convenio}'"

Related

C# SQLite multiple keyword with like command

I used SQLite. The user will pull the days from checkbox and I'll show it in data grid view but the date is recorded as day and time so I have to use like instead of in command.
DataSet dataSet122;
listBox1.Items.Clear();
SQLiteConnection connection = new SQLiteConnection("Data Source =log.sqlite;Version=3;");
string search = checkBoxComboBox1.Text;
string[] array = search.Split(',');
for (int i = 0; i < array.Length; i++)
{
array[i] = "'" + array[i] + "'";
}
string names = String.Join(",", array);
listBox2.Items.Add(names);
string query = "SELECT * FROM Gemkay1 WHERE ZAMAN LIKE (" + names + ")";
command = new SQLiteCommand(query, connection);
connection.Open();
adapter = new SQLiteDataAdapter(command);
dataSet122 = new DataSet();
adapter.Fill(dataSet122, "Gemkay1");
dataGridViewSummary1.DataSource = dataSet122.Tables["Gemkay1"];
SQL syntax for all people where name ends with SMITH or WRIGHT:
WHERE name LIKE '%SMITH' OR name LIKE '%WRIGHT'
LIKE is not the same as IN - it accepts a single string argument on the right hand side. If you want multiple LIKEs you must repeat the LIKE clause separated by OR
IN can be used with multiple string but it does not accept wildcards:
WHERE name IN ('playwright', 'cartwright', 'shipwright')
If you try and put a wildcard in it will literally match that character.
-
As an aside, don't make SQL like you're doing there, with string concatenation of the values. Concatenate parameters in instead and give them values, for example:
var names = new []{"%wright", "%smith"};
var sql = new SqliteCommand("SELECT * FROM t WHERE 1=0 ");
for(int p = 0; p<names.Length; p++){
sql.CommandText += " OR name like #p" + p;
sql.Parameters.AddWithValue("#p"+p, names[p]);
}
This I what I mean when I say "concatenate parameters in, then give them a value".
If you ever work with sqlserver read this blog post
Use IN operator to select data where multiple values
"SELECT * FROM Gemkay1 WHERE ZAMAN IN ('2021-02-01','2021-02-02')";
to ignore time from date you can use date function:
"SELECT * FROM Gemkay1 WHERE date(ZAMAN) IN ('2021-02-01','2021-02-02')";
See SQLite date and time functions documentation for more info.

SQL Server Numeric, can create but not update using C#

Just a simple quick question, I'm using Microsoft SQL Server 2014 Express and now I have two functions to create a record and to update a record containing a numeric value.
For some unknown reason I can create the record using a numeric value with a number bigger than 0 after the decimal point (like 50.50), however, when trying to update this record with the numeric value it just says that my syntax is wrong after the decimal point. So tl,dr (50.00 works, 50.50 or something like that, doesn't).
My question now is: what am I doing wrong?
Here are my two functions:
public static void UpdateProduct(int id, string name, decimal price)
{
try
{
string query = "UPDATE dbo.Products SET Name = '" + name + "' , Price = " + price + " WHERE ProductID = " + id;
SqlCommand command = new SqlCommand(query, connection);
command.ExecuteNonQuery();
}
catch (SqlException e)
{
Console.WriteLine(e.Message);
}
}
public static void AddProduct(string name, decimal price)
{
string query = "INSERT INTO dbo.Products (Name, Price) VALUES (#name, #price)";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#name", name);
command.Parameters.AddWithValue("#price", price);
command.ExecuteNonQuery();
}
And here is my SQL create for this value
create Table Products
(
ProductID INT IDENTITY(1,1) PRIMARY key,
Name VARCHAR(255) NOT NULL,
Price NUMERIC(5,2) NOT NULL,
Active BIT DEFAULT 1
);
In your insert query you use query parameters, which, among other things, take care of correct formatting of your decimal value.
In you update query you use string concatenation to add you decimal to the query. Most certainly, your current culture formats the decimal point not as point but as comma, resulting in an syntactically incorrect query.
So your assignment of
string query = "UPDATE dbo.Products SET Name = '" + name + "' , Price = " + price + " WHERE ProductID = " + id;
Will result in a string like
UPDATE dbo.Products SET Name = 'somename' , Price = 50,5 WHERE ProductID = 3
Instead of
UPDATE dbo.Products SET Name = 'somename' , Price = 50.5 WHERE ProductID = 3
Use parametrized queries like in the insert and this problem -- and many potential others you didn't even notice yet -- will be gone.

Using parametrized parameters WITH sqlcommandbuilder [no dataadapter]

I am trying to use a parametrized query which takes 2 column names and a table name and retrieves the data from a sql server DB.
The problem is it is not possible to parametrize the table name so i found a solution using a sqlcommandbuilder.quoteIdentifer(tablename) and this bit works...but apparently they don't play nice together.
I get exception containing a single word which is the column name
If i put the column name by hand it works.
What is wrong here?
public List<ItemsWithDescription> GetItemsFromDB(string name, string desc, string tableName)
{
List<ItemsWithDescription> items = new List<ItemsWithDescription>();
try
{
Status = 1;
SqlCommandBuilder builder = new SqlCommandBuilder();
cmd = new SqlCommand("Select #Name, #Desc from "+ builder.QuoteIdentifier(tableName), conn);
cmd.Parameters.AddWithValue("#Name", name);
cmd.Parameters.AddWithValue("#Desc", desc);
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
items.Add(new ItemsWithDescription(dr[name].ToString(), dr[name].ToString() + " | " + dr[desc].ToString()));
}
}
items.Sort((x, y) => string.Compare(x.Item, y.Item));
}
catch
{
Status = -1;
}
return items;
}
Edit:
This works but I would prefer to know why both can't be used together:
cmd = new SqlCommand("Select" +
builder.QuoteIdentifier(name) + "," +
builder.QuoteIdentifier(desc) + "from " +
builder.QuoteIdentifier(tableName), conn);
You can't parameterize column names. You can't do that in regular SQL actually.
What you need is Dynamic SQL.
If you follow the various newsgroups on Microsoft SQL Server, you
often see people asking why they can't do:
SELECT * FROM #tablename
SELECT #colname FROM tbl
SELECT * FROM tbl WHERE x IN (#list)
For all three examples you can expect someone to answer Use dynamic
SQL and give a quick example on how to do it. Unfortunately, for all
three examples above, dynamic SQL is a poor solution. On the other
hand, there are situations where dynamic SQL is the best or only way
to go.
Also take a look Table-Valued Parameters if you use SQL Server 2008 and above.

Fetching data for particular Month and year

I tried Query(given below in code) But it is showing me this error
No value given for one or more required parameters.
but while debugging I am passing date as this
string monthYY = dateTimePickerMonth.Value.ToString("M-yy");
So what is the right format to check it ,how can I do it ?
Code for Query
public int GetDrID_MonthWise(string DrName,string monthYY,int refDrID)
{
int data = 0;
try
{
string sql = "Select d.DoctorID From Doctor_Master d where d.LastName + ' ' + d.FirstName = '" + DrName + "' AND Patient_registration.RegDate='" + monthYY + "' AND Patient_registration.DoctorID=" + refDrID;
cmd = new OleDbCommand(sql, acccon);
rs = cmd.ExecuteReader();
while (rs.Read())
{
data = Convert.ToInt32(rs[0]);
}
}
catch (Exception err)
{
MessageBox.Show(err.Message.ToString());
}
return data;
}
This piece of your SQL statement informs the db engine Doctor_Master is the data source:
From Doctor_Master d
However, the WHERE clause refers to 2 fields which are not present in Doctor_Master:
Patient_registration.RegDate
Patient_registration.DoctorID
I'm unsure what you actually need. My hunch is you should INNER JOIN those tables. But I think you should design and test the query in Access, leaving c# out of the picture until after you have the Access query working as you wish.
I'm not sure exactly how you are passing your parameters but you need to specify values for all three of your parameters listed
public int GetDrID_MonthWise(string DrName,string monthYY,int refDrID)

Using items from List<string> in SQL query

Ok, I have a list that consists of a bunch of values from a sql query, that part works fine. What I want to do is use the items in that list to tell another query what to look for. So, what it is saying is that, it should return all columns from CMMReports where PartNumber is like %listItem1..2...3%, Any advice?
List<string> ImportedParts = GetImportedPartNumbers();
string query = "SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') AND (FilePath NOT LIKE '%js91162%') AND PartNumber LIKE %" + ImportedParts + "% ORDER BY CreatedOn DESC;";
Not that I condone this as you should be using parameterized queries. However, this should work:
StringBuilder partNumbers = new StringBuilder();
foreach (string queryValue in ImportedParts)
{
string q = "PartNumber LIKE '%" + queryValue + "%'";
if (string.IsNullOrEmpty(partNumbers.ToString())
{
partNumbers.Append(q);
}
else
{
partNumbers.Append(" OR " + q);
}
}
string query = string.Format("SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') " +
"AND (FilePath NOT LIKE '%js91162%') AND ({0}) " +
"ORDER BY CreatedOn DESC;", partNumbers.ToString());
You might look up the IN clouse for SQL that way you get the answer for the parts that SQL Server can find in the database. Using WHERE x = y for all the items means that if one item can't be found the whole query returns nothing.
I would consider doing this in a stored procedure and passing in your list as an Xml parameter.
See the following article for more info on using Xml parameters in a stored proc:
Passing lists to SQL Server 2005 with XML Parameters - By Jon Galloway
Form there you can easily use your list data inside your stored proc using the Xml syntax and treat it almost as another table of data.
Untested, but you should get the idea:
List<string> ImportedParts = GetImportedPartNumbers();
SqlCommand cmd = myConnection.CreateCommand();
cmd.CommandText = "SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') AND (FilePath NOT LIKE '%js91162%') AND (";
int i = 0;
foreach (string part in ImportedParts) {
cmd.AddParameterWithValue("#param" + i.ToString(), "%" + part + "%");
if (i != 0) cmd.CommandText += " OR"
cmd.CommandText += " PartNumber LIKE #param" + i.ToString();
i++;
}
cmd.CommandText += ") ORDER BY CreatedOn DESC;";
This solution uses a parameterized query instead of just appending strings in the SQL, which is considered a potential security risk.

Categories

Resources