Create a list of distinct values from a gridview - c#

Right now I have code that gets the distinct value from running SQL code on a table. I now use a stored proc to fill my Gridview instead of pulling from a table and can't do select distinct on a table since it's not there. I wanted to know if someone could point me in the right direction to create my list by using the values of a gridview.
Here is the current code I want to switch.
connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ToString());
string strSQL = string.Empty;
switch (strColumnName)
{
case "SiteID":
strSQL = #"SELECT distinct SiteID, SiteID FROM Sites ";
break;
case "OrderDate":
strSQL = #"SELECT distinct OrderDate, CONVERT(VARCHAR(11), OrderDate, 106) AS [OrderDate] FROM Sites ";
break;
}
SqlCommand command = new SqlCommand();
command.CommandText = strSQL;
command.Connection = connection;
command.Connection.Open();
SqlDataReader dataReader = command.ExecuteReader(CommandBehavior.CloseConnection);
IList<FilterValueSet> filterValueList = new List<FilterValueSet>();
while (dataReader.Read())
{
filterValueList.Add(new FilterValueSet
{
Id = dataReader[0].ToString(),
Value = dataReader[1].ToString()
});
}
connection.Close();
return filterValueList.ToArray<FilterValueSet>();

You can use Linq Distinct() method to filter the results like
return filterValueList.Distinct(equalityComparer).ToArray();
You need to provide an equality comparer for your FilterValueSet class, something in this way:
class MyEqualityComparer : EqualityComparer<FilterValueSet>
{
public override int GetHashCode(FilterValueSet obj)
{
return obj.Id.GetHashCode();
}
public override bool Equals(T x, T y)
{
return x.Id.Equals(y.Id);
}
}

Related

Get an identity value in a controller for INSERT (Execute Insert command and return inserted Id in Sql)

Currently, I am using MVC on creating a project. Now I want to insert an Identity ID value into an INSERT statement.
In my controller:
string payment = #"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount)
VALUES('{0}','{1}',{2})";
int pay = DBUtl.ExecSQL(payment, "Cash", currency,total);
if (pay == 1)
{
string pid = "SELECT TOP 1 Payment_id FROM Payment ORDER BY Payment_id DESC";
int paymentid = DBUtl.ExecSQL(pid);
if (cart.Count() != 0)
{
string order = #"INSERT INTO [Order](Order_Name,Order_Description,Order_Quantity,Payment_Id)
VALUES('{0}','{1}',{2},{3})";
Now, I want to the payment_id that already been inserted into the payment table which is the first statement and retrieve the payment_id and use it into the INSERT statement for the Order table
How can I achieve that?
Please Help
Thank you
Actually, DBUtil code consists of:
public static int ExecSQL(string sql, params object[] list)
{
List<String> escParams = new List<String>();
foreach (object o in list)
{
if (o == null)
escParams.Add("");
else
escParams.Add(EscQuote(o.ToString()));
}
DB_SQL = String.Format(sql, escParams.ToArray());
int rowsAffected = 0;
using (SqlConnection dbConn = new SqlConnection(DB_CONNECTION))
using (SqlCommand dbCmd = dbConn.CreateCommand())
{
try
{
dbConn.Open();
dbCmd.CommandText = DB_SQL;
rowsAffected = dbCmd.ExecuteNonQuery();
}
catch (System.Exception ex)
{
DB_Message = ex.Message;
rowsAffected = -1;
}
}
return rowsAffected;
}
And as you know ExecuteNonQuery denotes the numbers affecting the row
So, You can do as shown below:
FOR SQL SERVER 2005 and above
using (SqlConnection con=new SqlConnection(#"Your connectionString"))
{
using(SqlCommand cmd=new SqlCommand("INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount) output INSERTED.Payment_id VALUES(#Payment_Method,#Currency_Type,#Total_Amount)",con))
{
cmd.Parameters.AddWithValue("#Payment_Method", "Cash");
cmd.Parameters.AddWithValue("#Currency_Type", currency);
cmd.Parameters.AddWithValue("#Total_Amount", total);
con.Open();
int payment_id =Convert.ToInt32(cmd.ExecuteScalar());
if (con.State == System.Data.ConnectionState.Open)
con.Close();
return payment_id ;
}
}
also, you can change your query to:
"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount) VALUES(#Payment_Method,#Currency_Type,#Total_Amount)"; SELECT SCOPE_IDENTITY()"
For now, I tried something like this:
public IActionResult SaveDetail(List<Cart_Has_Services> cart,double total,string currency)
{
string payment = #"INSERT INTO Payment(Payment_Method,Currency_Type,Total_Amount)
VALUES('{0}','{1}',{2});";
int pay = DBUtl.ExecSQL(payment, "Cash", currency,total);
if (pay == 1)
{
object pid = DBUtl.GetList("SELECT Payment_id FROM Payment");
int paymentid = Convert.ToInt32(pid);
if (cart.Count() != 0)
{
string order = #"INSERT INTO [Order](Order_Name,Order_Description,Order_Quantity,Payment_Id)
VALUES('{0}','{1}',{2},{3})";
foreach (var item in cart)
{
int ord = DBUtl.ExecSQL(order, item.Cart_Service, item.Additional_Notes, item.Quantity,paymentid);
As for now, the codes will run by inserting the values into the payment table. After that, I want to get the payment_id for my next insert which is the order table.
The method that I tried to get the payment_id does not work.

DatetimePicker sort C#

I have two DateTimePicker control on my win form...and all i want is when the user select the datetimepicker(from: ) and ( To: ) the datagridview shows the selected date and year that the user selected..........here's my code.... i need the sort thing
P.S: i used STORED PROCEDURE
public void period()
{
cnn.Open();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "sp_insertcategory";
cmd.Connection = cnn;
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
while (rdr.Read())
{
int n = metroGrid1.Rows.Add();
metroGrid1.Rows[n].Cells[0].Value = rdr[0].ToString();
metroGrid1.Rows[n].Cells[1].Value = rdr[1].ToString();
metroGrid1.Rows[n].Cells[2].Value = rdr[2].ToString();
metroGrid1.Rows[n].Cells[3].Value = rdr[3].ToString();
metroGrid1.Rows[n].Cells[4].Value = rdr[4].ToString();
metroGrid1.Rows[n].Cells[5].Value = rdr[5].ToString();
metroGrid1.Rows[n].Cells[6].Value = rdr[6].ToString();
}
}
cnn.Close();
}
You can achieve the same from SQL query (In your case stored procedure itself). Just add below syntax in your SQL query to sort the data in descending order..
ORDER BY post_datetime DESC //post_datetime is columnName which contains DateTime
ASC can be used if you need sorting in ascending order.
Details about order by can be found at https://www.w3schools.com/sql/sql_orderby.asp

Sql Query Data Reader Returning True even it's False in C#

Good day!
It's taking me hours why my query returns a true even it's false.
Here's my code.
public SqlDataReader Check(BEL bel) {
SqlCommand cmd = new SqlCommand();
cmd.Connection = dbcon.getcon();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT SUM(Total) as OverallTotal FROM table WHERE Id=#Id AND DateFrom=#From AND DateTo=#To AND Status='Without'";
cmd.Parameters.AddWithValue("#Id",bel.CLEmpID);
cmd.Parameters.AddWithValue("#From", bel.CLPayrollFrom);
cmd.Parameters.AddWithValue("#To", bel.CLPayrollTo);
SqlDataReader dr = cmd.ExecuteReader();
return dr;
}
Suppose the parameterized values are:
#Id = 0001
#From = 1/28/2016
#To = 1/29/2016
Here's my method calling the datareader
SqlDataReader drCheck;
drCheck = bal.Check(bel);
if (drCheck.HasRows == true)
{
drCheck.Read();
// I'm inside the computation of OverallTotal
}else{
drCheck.Close();
// I'm out
}
drCheck.Close();
The problem is, when the value of my "To", for instance, is 1/30/2016, it suppose to go to the false which is out of the true condition but it is not.
Please help. Thanks in advance
In your query, you are have aggregate function. Whether your from & to returns no rows, as you have aggregate function, you always get one row with value at least zero.
In your case you don't need a Reader, you can use ExecuteScalar:
public decimal? Check(BEL bel)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = dbcon.getcon();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT SUM(Total) as OverallTotal FROM table WHERE Id=#Id " +
"AND DateFrom=#From AND DateTo=#To AND Status='Without'";
cmd.Parameters.AddWithValue("#Id", bel.CLEmpID);
cmd.Parameters.AddWithValue("#From", bel.CLPayrollFrom);
cmd.Parameters.AddWithValue("#To", bel.CLPayrollTo);
object obj = cmd.ExecuteScalar();
decimal? value = null;
if (obj != DBNull.Value)
value = Convert.ToDecimal(obj);
return value;
}
decimal? total = bal.Check(bel);
if (total.HasValue)
{
// do something with the total.Value
}
else
{
}
Look at simple example
SELECT SUM(total1) as tt1, SUM(total2) AS tt2, COUNT(*) AS [Nbr of rows]
FROM (
SELECT 10 AS total1, CAST(NULL AS INT) total2 --
) t
--WHERE 1=2
Generally when the SUMmed column is nullable only COUNT(*) will tell the difference are there any rows selected or no rows selected.
Your query always returns a value even if there are no matching records because you are summing them, so you get NULLin case of no records and in the case of all matching records' values are NULL. You'll get 0 if there are records and the sum-result is 0(including NULL values which will be counted as 0).
You could use following approach that uses a different query to return both informations:
public bool GetOverAll(int id, DateTime payrollFrom, DateTime payrollTo, out double? overall)
{
overall = null;
string sql = #"
;WITH Data AS(
SELECT t.*
FROM table
WHERE Id = #Id
AND DateFrom = #From
AND DateTo = #To
AND Status = 'Without'
)
SELECT HasRows = CAST(CASE WHEN EXIST( SELECT 1 FROM CTE )
THEN 1 ELSE 0 END AS bit),
OverallTotal = SUM(Total)
FROM CTE";
using (var con = new SqlConnection("connectionstring"))
using (var cmd = new SqlCommand(sql, con))
{
cmd.Parameters.AddWithValue("#Id", id);
cmd.Parameters.AddWithValue("#From", payrollFrom);
cmd.Parameters.AddWithValue("#To", payrollTo);
con.Open();
using (var rd = cmd.ExecuteReader())
{
if (rd.Read())
{
bool hasRows = rd.GetBoolean(0);
if (!rd.IsDBNull(1))
overall = rd.GetDouble(1);
return hasRows;
}
else
{
throw new Exception("This should never be the case!");
}
}
}
}
You now know if there were matching records or not:
double? overall;
bool hasRows = GetOverAll(id, payrollFrom, payrollTo, out overall);
if(hasRows && overall.HasValue)
{
// matching records and the sum of these values was not NULL (possible if nullable column and all values were NULL)
double total = overall.Value;
}

C# + mysql I want to select 1000 rows then update those selected rows

I am going to have multiple processes running at the same time so what I tried to do here is fetch 1000 rows and then update the rows i selected.. below are my Select and Update functions notice i call the update function right after closing the connection in the select function
public List<string> Select()
{
string set;
string query = "SELECT * FROM master WHERE attempted='0' LIMIT 1000";
List<string> list = new List<string>();
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (list.Count() < 1000)
{
dataReader.Read();
string email = dataReader["email"].ToString();
var m = dataReader["attempted"];
if (m.ToString() == "0")
{
list.Add(email);
}
}
dataReader.Close();
this.CloseConnection();
Update();
return list;
}
else
{
return list;
}
}
public void Update()
{
if (this.OpenConnection() == true)
{
string query = "UPDATE master SET attempted='1' WHERE ( SELECT * FROM master WHERE attempted='0' LIMIT 1000 )";
MySqlCommand cmd = new MySqlCommand(query, connection);
cmd.CommandText = query;
cmd.ExecuteNonQuery();
this.CloseConnection();
}
}
the exception i am getting it operand must contain 1 column(s)..
What am I doing wrong?
Why can't you just create a separate column, or even a table? Then write a basic Query at the SQL level or a Procedure to modify the value? Then the other applications can just test the value of the column or table.
Example:
UPDATE [dbo].[Customer]
SET [GotEmail] = 1
WHERE (
SELECT [Email]
FROM [dbo].[Customer]
);
Or something basic like that? Another example would be:
UPDATE accounts
SET (contact_last_name, contact_first_name) =
(SELECT last_name, first_name FROM salesmen
WHERE salesmen.id = accounts.sales_id);
Does a simple query like so not solve your issue?
If my response is not clear... I'll try and clarify my thought process.

How do I translate a List<string> into a SqlParameter for a Sql In statement? [duplicate]

This question already has answers here:
Pass Array Parameter in SqlCommand
(11 answers)
Closed 6 years ago.
I seem to be confused on how to perform an In statement with a SqlParameter. So far I have the following code:
cmd.CommandText = "Select dscr from system_settings where setting in #settings";
cmd.Connection = conn;
cmd.Parameters.Add(new SqlParameter("#settings", settingList));
reader = cmd.ExecuteReader();
settingsList is a List<string>. When cmd.ExecuteReader() is called, I get an ArgumentException due to not being able to map a List<string> to "a known provider type".
How do I (safely) perform an In query with SqlCommands?
You could try something like this:
string sql = "SELECT dscr FROM system_settings WHERE setting IN ({0})";
string[] paramArray = settingList.Select((x, i) => "#settings" + i).ToArray();
cmd.CommandText = string.Format(sql, string.Join(",", paramArray));
for (int i = 0; i < settingList.Count; ++i)
{
cmd.Parameters.Add(new SqlParameter("#settings" + i, settingList[i]));
}
You appear to be trying to pass a multi valued parameter, that SQL syntax isn't going to do what you expect. You may want to pass a table value parameter.
Read this: http://www.sommarskog.se/arrays-in-sql.html#iter-list-of-strings
specifically: http://www.sommarskog.se/arrays-in-sql-2008.html#ListSqlDataRecord
private static void datatable_example() {
string [] custids = {"ALFKI", "BONAP", "CACTU", "FRANK"};
DataTable custid_list = new DataTable();
custid_list.Columns.Add("custid", typeof(String));
foreach (string custid in custids) {
DataRow dr = custid_list.NewRow();
dr["custid"] = custid;
custid_list.Rows.Add(dr);
}
using(SqlConnection cn = setup_connection()) {
using(SqlCommand cmd = cn.CreateCommand()) {
cmd.CommandText =
#"SELECT C.CustomerID, C.CompanyName
FROM Northwind.dbo.Customers C
WHERE C.CustomerID IN (SELECT id.custid FROM #custids id)";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#custids", SqlDbType.Structured);
cmd.Parameters["#custids"].Direction = ParameterDirection.Input;
cmd.Parameters["#custids"].TypeName = "custid_list_tbltype";
cmd.Parameters["#custids"].Value = custid_list;
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
using (DataSet ds = new DataSet()) {
da.Fill(ds);
PrintDataSet(ds);
}
}
}
}
If you're using Sql Server 2008 or later, you can make use of table valued parameters - this allows you to pass in a table of values as a parameter. From .net you define a "structured" type SqlParameter and set the value to something that implements IEnumerable.
See the full MSDN reference with examples here: http://msdn.microsoft.com/en-us/library/bb675163.aspx
I ever use my own function to create Parameters like this:
public void SomeDataFunction() {
ArrayList params = GetParameters(someEntity);
CommandObject.Parameters.AddRange(parameters.ToArray());
}
public static ArrayList GetParameters(ISomeEntity entity) {
ArrayList result = new ArrayList {
OleDbUtility.NewDbParam("#Param1", OleDbType.Integer, , entity.Parameter1),
OleDbUtility.NewDbParam("#Param2", OleDbType.VarChar, 9, entity.Parameter2),
}
}
public static OleDbParameter NewDbParam(string parameterName, OleDbType dataType,
int size, object value) {
OleDbParameter result = new OleDbParameter(parameterName, dataType, size, string.Empty);
result.Value = value;
return result;
}
Use XML, it's plenty fast for this scenario. You would turn your list into XML and simply pass a string:
CREATE TABLE #myTempTable
( Letter VARCHAR(20) )
INSERT INTO #myTempTable (Letter) VALUES ('A'), ('B')
Declare #xml XML = '<a>A</a><a>B</a><a>C</a>'
Select * from #myTempTable
Where Letter in
(Select p.value('.', 'VARCHAR(40)') AS [Letter] from #xml.nodes('//a') as t(p))
DROP TABLE #myTempTable
I usually pass in the list as a comma separated string, and then use a table valued function to 'split' the string into a table that I can then use to join with in another query.
DECLARE #Settings TABLE (Sid INT)
INSERT INTO #Settings(Sid)
SELECT CAST(Items AS INT) FROM dbo.Split(#SettingsParameter, ',')
Unless of course you are using SQL Server 2008, then I would use the table valued parameters.

Categories