How to find number of rows when grouped - c#

I want to retrieve the number of rows grouped with condition for controlling the number of rows for display. With primary key there is no problem I get the count(*) but when in case of other fields such as date, nom where there is much rows with the same name and date I found a primitive way to find the number of rows wich will be displayed as below:
public static int GetRapportPgeNbr(string Qry, int param)
{
int counter = 0;
int result = 0;
using (MySqlConnection conn = new MySqlConnection(PublicVariables.cs))
{
using (MySqlCommand cmd = new MySqlCommand(Qry,conn))
{
conn.Open();
MySqlDataReader reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
result = result + Convert.ToInt16(reader["rows"]);
++counter;
}
}
catch(MySqlException e)
{
MessageBox.Show(e.Number.ToString() + " -> " + e.Message.ToString());
return result;
}
}
}
if (param == 1)
return counter;
else
return result;
}
The param variable guides me either I get the sum of rows (sometimes there is 2 or more tables with union) or counter.
Sqlfiddle
In this exemple I have 5 rows but I need only 4 rows so I take the result of the counter.
Is there a better way ?

I think you are looking for COUNT(DISTINCT nom) and remove the GROUP BY to get the total count:
SELECT COUNT(DISTINCT nom) AS rows
FROM ProdMacaron
SQL Fiddle Demo
This will give you 4 not 5.
You can, however, add the GROUP BY nom, but this is useless with COUNT(DISTINCT nom) this will give you ones for any values in your table.

Related

ASP.NET DropDown and SQL C#

I have a number in a database (e.g 12) and I want to display that number in the dropdown list on a webform, however, not the number itself but a range of numbers from 1 to 12 (so 1,2,3....12). Is there a property I can use or a way to get a list of numbers from SQL Statement?
Read the Number from Database
Display a full range of numbers from 1 to X (X = Number from Database)
Bind dropdown list to:
Enumerable.Range(1, <number from database>);
First create an array or list and then iterate till the number you read from database then add these numbers to your array or list then bind with your dropdown datasource
var numbers = new List<int>();
for(var i = DB_NUMBER; i >= 1; i--)
{
numbers.Add(i);
}
yourDropDown.DataSource = numbers;
yourDropDown.DataBind();
Hope it helps
Since, you wish to return the range of numbers from the SQL Query you might need a complex query, but it will always have some limit to it.
SQL Query as per your requirement:
Select Value from
(
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n as Value
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
) as tbl
Where value between 0 and 12
ORDER BY 1
I Modified above SQL from the Original Source : Answer
C#:
DropDownList.DataSource = DataTable;
DropDownList.DisplayField = Value;
DropDownList.ValueField = Value;
DropDownList.DataBind();
You just read the number from the database and then use a for loop:
for(int i = readNumber; i > 0; i--)
{
//Add i to your dropDown list or do anything you want with it
}
Based on the question
If I assume that you want SQL to generate and return the range then you want to use a recursive cte to build it from the value in your table ...
// construct the db connection and command object
var con = new SqlConnection("Your Connection String");
using(var cmd = new SqlCommand(con) { CommandType = CommandType.Text })
{
// tell the command what SQL query we want to execute
cmd.CommandText = #"
DECLARE #startnum INT=1
DECLARE #endnum INT= SELECT TOP 1 Number FROM ValueTable
;
WITH gen AS (
SELECT #startnum AS num
UNION ALL
SELECT num+1 FROM gen WHERE num+1<=#endnum
)
SELECT * FROM gen
option (maxrecursion 100)
";
// connect to the db and execute the command
con.Open();
using(var reader = cmd.ExecuteReader())
{
// build the range from the values generated by it
var range = new List<int>();
while(reader.Read()) { range.Add(reader.Read()); }
// bind the results to the drop down on the page
DropDownList.DataSource = range
.Select(i = > new { Key = i, Value = i })
.ToArray();
DropDownList.DisplayField = "Key";
DropDownList.ValueField = "Value";
DropDownList.DataBind();
}
con.Close();
}
The simplest approach
ok querying a db is a pretty well documented problem so I won't repeat that here.
But lets assume you have the following ...
// sourced from your db
int start = 1;
int end = 12;
... from there you can build a range of values ...
var range = Enumerable.Range(start, end)
.Select(i = > new { Key = i, Value = i })
.ToArray();
... and then bind that range to your drop down on the page ...
DropDownList.DataSource = range;
DropDownList.DisplayField = "Key";
DropDownList.ValueField = "Value";
DropDownList.DataBind();
Sources of information ...
How to generate a range of numbers between two numbers?
https://msdn.microsoft.com/en-us/library/fksx3b4f.aspx
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx
To achieve this you need to
Take the desire number from database
then you have to write a for loop.
private void InitializeDropDownList(int number)
{
for (int i = 0; i < number; i++)
{
ddlNumberRange.Items.Add(new ListItem { Text = (i + 1).ToString(), Value = (i + 1).ToString() });
}
}

How to sort dataGridView in descending order by a specific column and rank them?

I am trying to create a leaderboard based on player's High score.
Each player's scores are entered randomly into DB.
I need to sort them based on their high score and also rank them.
this is my table structure
Here i want to display player_ID, player_name, player_nick and HP in datagridview.
any help is appreciated.
string query1 = "SELECT player_ID'Player ID',player_name'Player
Name',player_nick'Nick Name',HP'High Score' FROM player_profile ORDER
BY HP DESC";
My work so far, i don't know how to rank
You can use SQL to provide the rank with a user variable (I think there are also some Rank() functions). From something like Workbench:
SET #rank=0;
SELECT Name, HP, #rank:=#rank+1 As Rank FROM Demo ORDER BY HP ASC
You can also do it from code, with one small change:
string SQL = #"SET #rank=0;
SELECT Name, HP, StartDate, #rank:=#rank+1 As Rank
FROM Demo ORDER BY HP DESC;";
using (MySqlConnection dbcon = mySqlDB.GetMySQLConnection())
using (MySqlCommand cmd = new MySqlCommand(SQL,dbcon))
{
dbcon.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
dgv1.DataSource = dt;
}
Results:
There is no Rank column in the table, that is added via the SQL statement above.
If you have ties, it gets more complicated. You'd have to introduce some other vars to track when the HP/Score changes and increment #rank only then. If you want to skip a rank on ties ({1,2,2,4} vs {1,2,2,3}) you'd have to also add a counter.
The one thing is that you have to allow user vars which can be specified in the connection string:
Server=SvrAddr;Database=myDB;Uid=myUsr;Pwd=myPass;Allow User Variables=True";
According to Connection Strings the option is available as of version 5.2.2
This Great Answer shows how to skip having to initialize the rank var:
string SQL = #"SELECT Name, HP, StartDate, #rank:=#rank+1 As Rank
FROM Demo, (SELECT #rank := 0) r ORDER BY HP DESC;";
Adding (SELECT #rank := 0) r prevents having to explicitly declare it. Very cool.
The following code adds the data to the dataGridView and then answers your question at the end.
// Add columns to the dataGridView
dataGridView1.Columns.Add("player_ID", "player_ID");
dataGridView1.Columns.Add("player_name", "player_name");
dataGridView1.Columns.Add("player_nick", "player_nick");
dataGridView1.Columns.Add("HP", "HP");
// Add some data to the dataGridView
object[] rowData = new object[dataGridView1.Columns.Count];
rowData[0] = 0; // Player_ID
rowData[1] = "Pancho"; // Player_Name
rowData[2] = "Speedy"; // Player Nick
rowData[3] = Convert.ToDecimal("58.7"); // HP
dataGridView1.Rows.Add(rowData);
rowData[0] = 1;
rowData[1] = "Ramon";
rowData[2] = "Sleepy";
rowData[3] = Convert.ToDecimal("39.6"); // HP
dataGridView1.Rows.Add(rowData);
rowData[0] = 2;
rowData[1] = "Cimitrio";
rowData[2] = "Grumpy";
rowData[3] = Convert.ToDecimal("41.2"); // HP
dataGridView1.Rows.Add(rowData);
rowData[0] = 3; // Player_ID
rowData[1] = "Panfilo"; // Player_Name
rowData[2] = "Gummy Bear"; // Player Nick
rowData[3] = Convert.ToDecimal("61.5"); // HP
dataGridView1.Rows.Add(rowData);
// Sort dataGridView by HP
dataGridView1.Sort(dataGridView1.Columns[3], ListSortDirection.Ascending);
// Add rank column
dataGridView1.Columns.Add("Rank", "Rank");
// Rank players
for (int i = 0; i < dataGridView1.Rows.Count-1; i++)
{
dataGridView1.Rows[i].Cells["Rank"].Value = Convert.ToString(i+1);
}

Improving performance of SQL SELECT in C#

I need some advice regarding updating a large number of records in a MySql database. I am currently storing a large number of words and their suffixes (a suffix array) into a database which results in a row count of approximately 4.3 million. Each record contains the primary key id, the actual word word, the document the word is in document, the offset of the word within the document 'offset', a flag which determines whether the record is a whole word or not flag and a link to the next record with the same value for word. Each record is initialized with a link value of -1.
This is my current code for updating the links in the database:
public void Link(object c)
{
DBConnection conn = (DBConnection)c;
rowcount = conn.GetRowCount();
string word;
int link;
List<Record> recordsList = new List<Record>();
List<Record> recordsMatched = new List<Record>();
for (int i = 0; i < rowcount; i++)
{
recordsList.AddRange(conn.ReadQuery("SELECT * FROM csa2018.words WHERE id = " + i));
word = recordsList[0].Word;
link = recordsList[0].Link;
recordsMatched = conn.ReadQuery("SELECT * FROM csa2018.words WHERE word = '" + word + "'");
for(int j = 0; j < recordsMatched.Count-1; j++)
{
if (recordsMatched[j].Link == -1)
{
conn.WriteQuery("UPDATE csa2018.words SET link = " + recordsMatched[j + 1].Id + " WHERE id = " + recordsMatched[j].Id);
}
else
{
break;
}
linkedRecords++;
}
linkedRecords++;
recordsMatched.Clear();
recordsList.Clear();
}
Form1.linkingFinished = true;
}
Overall, it has good performance when it finds words which are repeated frequently; however at around 60% the performance deteriorates because most of the remaining words are unique.
My guess is that this query:
recordsMatched = conn.ReadQuery(
"SELECT * FROM csa2018.words WHERE word = '" + word + "'");
shouldn't be like this because it is being called once for every row. Are there any better approaches like using stored procedures maybe?
P.S.: the ReadQuery method reads rows using the query supplied and constructs a Record object and adds each record to a List<Record>.
This is what my database looks like :
CREATE TABLE words ( id int(11) NOT NULL, word varchar(45) NOT NULL,
document varchar(45) NOT NULL, offset int(11) NOT NULL, flag int(11) NOT NULL,
link int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
if I understand your code correctly than this single sql-statement should do the job:
UPDATE csa2018.words as w1
left join
(select w2.id as id, min(w3.id) as linked_to
from csa2018.words w2, csa2018.words w3
where w2.word = w3.word and
w3.id > w2.id limit 1) w4
on (w1.id = w4.id)
SET w1.link = IFNULL(w4.linked_to, -1)
The inner select-statement gives the mapping from one dataset to the linked dataset. You should watch the result of the select-statement to see if everthing is fine.

How to count and sum total of DataTable with LINQ?

I have a DataTable which has a column "amount" for each rows and I'd like to have the total sum of all the rows. And also, I'd like to get total number of rows in the DataTable. Could anyone teach me how to have it done with LINQ instead of ordinary way?
Number of rows:
DataTable dt; // ... populate DataTable
var count = dt.Rows.Count;
Sum of the "amount" column:
DataTable dt; // ... populate DataTable
var sum = dt.AsEnumerable().Sum(dr => dr.Field<int>("amount"));
Aggregate allows you to avoid enumerating the rows twice (you could get the row count from the rows collection but this is more to show how to extract multiple aggregates in 1 pass):
var sumAndCount = table.AsEnumerable().Aggregate(new { Sum = 0d, Count = 0},
(data, row) => new { Sum = data.Sum + row.Field<double>("amount"), Count = data.Count + 1});
double sum = sumAndCount.Sum;
int count = sumAndCount.Count;
decimal[] Amount = {2,3,5 };
var sum = Amount.Sum();
var count = Amount.Count();
Based on Roy Goode's Answer you could also create an Extension
public static int Sum(this DataTable table, string Column)
{
return table.AsEnumerable().Sum(dr => dr.Field<int>(Column));
}
Unfortunately you can't be more generic her because there is no where T : numeric

How to create Total column and Total row using LINQ?

Given a datatable that does not have a total column or row. How can I use LINQ to add a Total Column and Total row to create the table below?
TextColumn, NumberColumn0, NumberColumn1 Total
A 1 4 5
B 2 55 47
C 2.3 DBNULL 2.3
D 4 3 7
Total 9.3 62 61.3
Thanks!
Um, for those who need code. I'm using this currently for total column:
public static void CreateTotalColumnByType(DataTable table, Type valueType)
{
// create expression
string expression = string.Empty;
// do not count the first column (used for string field)
for (int columnIndex = 1; columnIndex < table.Columns.Count; ++columnIndex)
{
if (columnIndex != table.Columns.Count - 1)
{
// add +
expression += String.Format("[{0}] + ", table.Columns[columnIndex].ColumnName);
}
else
{
// last column so don't add plus
expression += String.Format("[{0}]", table.Columns[columnIndex].ColumnName);
}
}
// add total column
DataColumn totalColumn = new DataColumn("Total", valueType, expression);
table.Columns.Add(totalColumn);
}
but I'd like to replace it with LINQ.
//adds Total column
var q1 = src.Select(x=>new { x.TextColumn
,x.NumberColumn0
,x.NumberColumn1
,Total=x.NumberColumn0+x.NumberColumn1});
//adds Totals row
var q2 = q1.Concat(new[]{new{ TextColumn="Total"
,NumberColumn0 = src.Sum(_=>_.NumberColumn0)
,NumberColumn1 = src.Sum(_=>_.NumberColumn1)
,Total=q1.Sum(_=>_.Total)}});
There are a number of ways of doing this, depending on how you are getting this back you wouldn't even need to use LINQ.
You could do the following if you are using standard classes for data transport.
public class MyDataClass
{
public string TextColumn {get; set;}
public int NumberColumn0 {get; set;}
public int NumberColumn1 {get; set;}
public int Total
{
get { return NumberColumn0 + NumberColumn1; }
}
}
This way you have no need for LINQ. If you REALLY want to use LINQ you could use
var results = from x in MyDataList
select new { TextColumn = x.TextColumn, NumberColumn0 = x.NumberColumn0,
NumberColumn1 = x.NumberColumn1,
Total = x.NumberColumn1 + x.NumberColumn0};
If you have a variable number of columns or something similar, you might not have as much luck using LINQ, we would need to see more to get a true idea of what you are doing.
You could try like this one.
For each col As DataGridViewColumn in grid.Columns
'' You could try adding if here if there are exception columns
gRID.Item(col.Index, 0).Value = gRID.Rows.Cast(Of DataGridViewRow)().AsEnumerable().Sum(Function(c) Convert.ToDecimal(c.Cells(col.Index).Value)).ToString()
Next
I just enhance p.cambell's solution a bit

Categories