I want create a poll. i have a table for questions(Fields: ID,Question) and another for answers(Fields: ID,QuestionID,Answer). There is a table for the results(fields: QuestionID,AnswerID,UserID).i want to show the Percent of responding to any item in datagridview.
for example when i enter question id, datagridview show:
Choose question ID:1
Option...........Percent
1 ------------------------- 30
2 -------------------------- 20
3---------------------------50
4-------------------------- 10
this is my code But the result does not show it:
int a = Convert.ToInt32(textBox1.Text);
var q = (from s in Session.DB.PoolUsers
where s.PoolQID == a
select s);
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
var qqq = (from c in qq
select c).Count();
MessageBox.Show(qqq.ToString());
And there is my classes:
public partial class PoolA //For answers
{
public int Id { get; set; }
public string Answer { get; set; }
public string QuestionID { get; set; }
}
public partial class PoolQ //Questions
{
public int Id { get; set; }
public string Question { get; set; }
public string AnswerID { get; set; }
public string Status { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
public partial class PoolUser //resaults
{
public int Id { get; set; }
public int PoolQID { get; set; }
public int PoolAID { get; set; }
public int UserID { get; set; }
}
This is not the optimal way I would do it, I would highly consider you, to change your entities.
Here is the solution:
int questionId = 1;
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
.NET Fiddle Link
I don't know your intentions but I think you should redesign the schema (not that it not doable as it is) for maximum efficiency. You call your PoolUser table your results table but it is your answers log at best. you need a table that has the results processed already such as would be done in a DWH kind of situation.
If you have a results summary table you could work out vertical sums groups all day without the extra performance of joining tables etc. for ad hoc data. I reckon you'd be running this a lot to go over the results, so it sounds like it is a better idea to store the summarized information for reporting, performance etc.
as for the current state of the problem:
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
agroups has the answerIds grouped up for the question number you typed in.
then you are just showing the row count of the grouped up answers which shows you 2:
var qqq = (from c in qq
select c).Count();
you are telling me that your messagebox shows the number "2"
this means that people only selected 2 options to answer whatever question id you put in. you don't know whether the answer is correct or not. the last query "qqq" you have, you have the count outside so it only returns the row count. you don't want the row count. you want the answerID count after the grouping.
from c in qq
select count(c)
this is the total answers for the questionID you typed in.
now you need the correct answers in order to calculate the percentage. this is the query missing. you have gone 75% of the way. just finish it off. hopefully this is sufficient detail and not too much text to read.
The problem was solved and I got the following code. I also draw a graph based on
private void button1_Click(object sender, EventArgs e)
{
var list = (from c in Session.DB.PoolUsers
select c).ToList();
int questionId = Convert.ToInt32(textBox1.Text);
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
dataGridView1.DataSource = answersPrecentage.ToArray();
//Draw Chart From DataGridview:
chart1.DataBindTable(answersPrecentage);
}
And works properly
Thanks all :)
Related
I am accustomed to using SQL left joins to get a list of all available options and determine what items are currently selected. My table structure would look like this
Table MainRecord -- recordId | mainRecordInfo
Table SelectedOptions -- recordId | optionId
Table AvailableOptions -- optionId | optionValue
and my query would look like this
SELECT optionValue, IIF(optionId IS NULL, 0, 1) AS selected
FROM AvailableOptions AS a
LEFT JOIN SelectedOptions AS s ON s.optionId = a.optionId AND a.recordId = 'TheCurrentRecord'
I am trying to replace this with Entity Framework, so I need help with both a model and a query -- they both need corrected.
public class MainRecord
{
public int recordId { get; set; }
public string mainRecordInfo { get; set; }
[ForeignKey("recordId")]
public List<SelectedOptions> selectedOptions { get; set; }
}
public class SelectedOptions
{
public int recordId { get; set; }
public int optionId { get; set; }
}
public class AvailableOptions
{
public int optionId { get; set; }
public string optionValue { get; set; }
}
Query
IQueryable<AvailableOptions> options = from o in context.AvailableOptions select o;
I can get a list of AvailableOptions, but how can I get a list and know which ones are selected?
If the number of selections and available options is small enough, you can do this in memory:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s });
selected will now be a list of objects with Available and Selected as properties and will only contain those that matched in optionId value.
If you only wish to get a pure list of AvailableOptions that match, simply chain a Select to the join:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s })
.Select(o => o.Available);
Not a complete answer, but it is really good to understand the navigational properties that you get from the model. Here is a query that most likely isn't exactly what you want but that demonstrate it
from ao in _db.AvailableOptions
where ao.recordId == "TheCurrentRecord" && ao.SelectedOptions.OptionId == 1
select new
MyPoxo {ao.SelectedOptions.Value ?? 0};
so instead of just having o you navigate through the joins that gets specified by the FKs. In this example I would assum AvailableOptions would have a linke to SelectedOptions.
I am using Linq to SQL in a WebApi to return a list of objects from a database to a frontend.
Let's say the model looks something like this:
public class Course
{
public int ID { get; set; }
public string NAME { get; set; }
}
public class Schedules
{
public int id { get; set; }
public int courseid
public datetime start { get; set; }
}
In addition my Linq-to-SQL inside of one Controller looks something like this:
...
...
var list = (from xx in xx.Courses
select new Course
{
ID = xx.ID,
NAME = xx.NAME,
Schedules = (from yy in yy.Schedules
where xx.ID == yy.courseid
select new Schedules
{
id = yy.id,
courseid = yy.courseid,
start = yy.start
}).toList()
}).toList()
...
...
Now I need to order "list" by the minValue of Schedules.start in an ascending order. That means, that the output should give me the element with the earliest start first. In addition Courses with no planned schedule should be given out at the end.
In the end the output should look like this:
[{"ID":5, "NAME":"NAME1", "Schedules":[{"id":10, "courseid":5, "start":"2017-12-15 00:00:00.000"}, {"id":8, "courseid":5, "start":"2017-12-20 00:00:00.000"}]}],[{"ID":1, "NAME":"NAME1", "Schedules":[{"id":9, "courseid":1, "start":"2017-12-16 00:00:00.000"}, {"id":2, "courseid":1, "start":"2017-12-17 00:00:00.000"}]}]
Thanks in advance.
I was in such a situation where I need to sort the parent depend on the child property's value.
var list = (from xx in xx.Courses
select new Course
{
ID = xx.ID,
NAME = xx.NAME,
Schedules = (from yy in yy.Schedules
where xx.ID == yy.courseid
select new Schedules
{
id = yy.id,
courseid = yy.courseid,
start = yy.start
}).toList()
}).OrderBy(mc => mc.Schedules.Min(dc => dc.start)).toList()
Let's say I have the following data in a database.
class Data
{
public int Category { get; set; }
public int ValueA { get; set; }
public int ValueB { get; set; }
}
How can I write a LINQ query to get the sum of ValueA and also the sum of ValueB for all rows with Category == 1?
I know I could load all the data and then use Sum on the loaded data but would prefer to total them in the database.
I know I can use group by but I'm not grouping by anything. I just want these two totals from the data.
If you are using EF, you can try this:
var result= context.Data.Where(d=>d.Category == 1)
.GroupBy(d=>d.Category)
.Select(g=>new {
SumA=g.Sum(d=>d.ValueA),
SumB=g.Sum(d=>d.ValueB)
}
);
You can group by a constant
var result = from d in context.Data
where d.Category == 1
group d by 1 into g
select
{
ASum = g.Sum(d => d.ValueA),
BSum = g.Sum(d => d.ValueB)
};
Or as octavioccl pointed out you can also group by Category since it will be a constant value because of the where clause. But using a constant is how you can achieve what you want in the general case.
2 tables: User and Alarm
Table:User
UserID(int),
FullName(varchar)
Table:Alarm
AssignedTo(int),
Resolved(bool)
Query:
SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved, COUNT(resolved) - SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname
Results:
Fullname Assigned Resolved Unresolved
User1 204 4 200
User2 39 9 30
User3 235 200 35
User4 1 0 1
User5 469 69 400
For the life of me I can't figure out how to make this into a Linq query. I am having trouble with the grouping function.
I've looked a countless examples and none have my combination of Left Outer join with grouping or they are so complicated that I can't figure out how to make it work with mine. Any help here would be Greatly appreciated!!!
Update:
I may not have been clear in what I'm looking for. I am looking for the alarms grouped by the AssignedTo Column which is a userid... Except, I want to replace that userid with the FullName that is located in the users table. Someone had posted and deleted something close except it gave me all users in the user table which is not what I'm looking for..
Update 2: See my answer below
Assuming that you have the following models:
This is the model for Alarm:
public class Alarm
{
public int id { get; set; }
public int AssignedTo { get; set; }
[ForeignKey("AssignedTo")]
public virtual User User { get; set; }
public bool Resolved { get; set; }
}
This is the model for User:
public class User
{
public int UserID { get; set; }
public string FullName { get; set; }
public virtual ICollection<Alarm> Alarms { get; set; }
public User()
{
Alarms = new HashSet<Alarm>();
}
}
This is the model that will hold the alarm statistics for each user:
public class UserStatistics
{
public string FullName { get; set; }
public int Assigned { get; set; }
public int Resolved { get; set; }
public int Unresolved { get; set; }
}
You can then do the following:
var query = context.Users.Select(
user =>
new UserStatistics
{
FullName = user.FullName,
Assigned = user.Alarms.Count,
Resolved = user.Alarms.Count(alarm => alarm.Resolved),
Unresolved = user.Alarms.Count(alarm => !alarm.Resolved)
});
var result = query.ToList();
By the way, you can also modify the query and remove Unresolved = user.Alarms.Count(alarm => !alarm.Resolved), and then make the Unresolved property a calculated property like this:
public class UserStatistics
{
public string FullName { get; set; }
public int Assigned { get; set; }
public int Resolved { get; set; }
public int Unresolved
{
get { return Assigned - Resolved; }
}
}
This will make the generated SQL query simpler.
I finally figured it out.
This:
var results = alarms.GroupBy(x => x.AssignedTo)
.Join(users, alm => alm.Key , usr => usr.UserID, (alm, usr) => new {
Fullname = usr.FullName,AssignedNum = alm.Count(),
Resolved = alm.Where(t=>t.resolved == true).Select(y => y.resolved).Count(),
Unresolved = alm.Where(t=>t.resolved == false).Select(y => y.resolved).Count() });
Reproduces This:
SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved,
COUNT(resolved) - SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname
The result is grouped by the AssignedTo (int) but AssignedTo is not selected. Instead FullName is selected from the joined user table.
Many thanks to everyone that tried to help! I learned a lot from your answers.
For bonus points, how would I write my lamdbda answer in a SQL like syntax?
Try this :
from u in context.User
join a in context.Alarm on u.UserID equals a.AssignedTo into g1
from g2 in g1.DefaultIfEmpty()
group g2 by u.Fullname into grouped
select new { Fullname = grouped.Key, Assigned = grouped.Count(t=>t.Resolved != null), Resolved = grouped.Sum
(t => int.Parse(t.Resolved)), Unresolved = (grouped.Count(t=>t.Resolved != null) - grouped.Sum
(t => int.Parse(t.Resolved)))}
I guess it is not necessarily to use "Grouping" for this query in Linq because the combination of "LEFT JOIN" + "GROUP BY" changed them over to "INNER JOIN".
var results =
from u in users
join a in alarms on u.UserID equals a.AssignedTo into ua
select new
{
Fullname = u.FullName,
Assigned = ua.Count(),
Resolved = ua.Count(a => a.Resolved),
Unresolved = ua.Count(a => !a.Resolved)
};
foreach (var r in results)
{
Console.WriteLine(r.Fullname + ", " + r.Assigned + ", " + r.Resolved + ", " + r.Unresolved);
}
I am using .NET 4 Data Visualization Charts and am using a Linq query to get the data for the chart. I am new to Linq so not sure on how to word this query. Here is what it does, I have the follwoing tables its pulling data from
Orders
ShipmentSchedule
The orders table has the Product that was ordered, the OrderId, the amount that was shipped and the remaining quantity for that order. One order can only have a single product in it. I am trying to get all the Ordershipments in the class OrderShipment, make a List<> for it and group it by each product and show the total quantity for each product that was ordered and what has been shipped. The query below sums up the quantity shipped for all orders which is correct but because it is making a list of OrderShipments, I want the remaining quantity for each order across all shipments and sum that up by the product. Right now its adding up the remaining quantity for all the shipments which is wrong since an Order has the same Remaining Quantity across all shipments. how can I get the remaining quantity for each product so thw query adds up the Remaining quantity by each order correctly?? Please provide an example with code how to accomplish this if you have a suggestion, your helps really appreciated, thanks
private class ChartDataPoint
{
public string ProductName { get; set; }
public double QtyRemaining { get; set; }
public double QtyShipped { get; set; }
}
private class OrderShipment
{
public string ProductName { get; set; }
public double QuantityRemaining { get; set; }
public double QuantityShipped { get; set; }
public int OrderId { get; set; }
}
List<OrderShipment> productsOrdered =
(from os in Statistics.OptimalShipments
from o in Statistics.OrdersPlaced
where ((os.Date >= Statistics.ShippingWindowStartDate) &&
(os.Date <= Statistics.ShippingWindowEndDate) &&
(os.OrderId == o.OrderId) &&
((o.ClientLocationId == ClientLocation.ClientLocationId)))
select new OrderShipment()
{
ProductName = o.Product.Name,
QuantityRemaining = o.RemainingQuantity,
QuantityShipped = os.QuantityShipped,
}).ToList();
var query = productsOrdered.GroupBy(p => p.ProductName);
List<ChartDataPoint> chartDataPoints = new List<ChartDataPoint>();
foreach (var productGroup in query)
{
chartDataPoints.Add(new ChartDataPoint()
{
ProductName = productGroup.Key,
// This is obv wrong this sums up the Remaining quantity across
// all shipments for a order when we should be only taking the
//Remaining quantity once for each order across all shipments.
QtyRemaining = productGroup.Sum(po => po.QuantityRemaining),
QtyShipped = productGroup.Sum(po => po.QuantityShipped)
});
}
As I understand it your productGroup should have a lot of OrderShipment objects grouped by their product name. You then want to sum the QuantityShipped and the QuantityRemaining should all be the same and you want to just take this value?
If so then this should do you:
QtyRemaining = productGroup.First();
If I have misunderstood you may want to simplify the explanation, perhaps with an example...