How to convert this SQL query into LINQ in c# - c#

UPDATE m
SET m.Score = s.AScore + '-' + s.BScore
FROM #Matches m
INNER JOIN Scores s ON m.MatchId = s.MatchId
AND s.InfoTypeId = (
CASE
WHEN m.SportId = 1 AND (m.StatusId >= 13 AND m.StatusId <= 17) THEN 10
WHEN m.SportId = 1 AND m.StatusId = 20 THEN 24
WHEN m.SportId = 1 AND m.StatusId = 21 THEN 23
WHEN m.SportId = 1 AND m.StatusId = 18 THEN 8
ELSE 5
END
)
I'm having two lists in C# one is Matches and 2nd is Scores, and I want to get the result from those list like the result this query will return. Means I want to update "Score" property of "Matches" list like it is being updated in SQL query.
Any Help Please.
Matches.ForEach(m => m.Score = (Scores.Where(ms => ms.MatchId == m.MatchId
&& ms.ScoreInfoTypeId == ((m.SportId == 1 && m.StatusId >= 13 && m.StatusId <= 17) ? 10
: (m.SportId == 1 && m.StatusId == 20) ? 24
: (m.SportId == 1 && m.StatusId == 21) ? 23
: (m.SportId == 1 && m.StatusId == 18) ? 8
: 5)).Select(ms => ms.AScore + "-" + ms.BScore).FirstOrDefault()));
I have tried, but I think its too expensive. It is taking too much time. Is there any optimized way please.

Try this example in LinqPad. You can use query syntax to join the 2 lists and iterate over the result to set match scores. I used a dictionary to simplify that switch case.
void Main()
{
var matches = new[]{
new Match{MatchId=1,SportId=1,StatusId=13,Score=""},
new Match{MatchId=2,SportId=1,StatusId=18,Score=""},
new Match{MatchId=3,SportId=2,StatusId=24,Score=""},
};
var scores = new[]{
new{MatchId=1,AScore="10",BScore="0",InfoTypeId=10},
new{MatchId=2,AScore="20",BScore="0",InfoTypeId=8},
new{MatchId=3,AScore="30",BScore="0",InfoTypeId=5},
};
var dict = new Dictionary<int,int>{[13]=10,[14]=10,[15]=10,[16]=10,[17]=10,[20]=24,[21]=23,[18]=8};
var data = (from m in matches
join s in scores on m.MatchId equals s.MatchId
where s.InfoTypeId == ((m.SportId == 1 && dict.ContainsKey(m.StatusId))? dict[m.StatusId] : 5)
select new {m,s}
).ToList();
data.ForEach(o =>
{
o.m.Score = o.s.AScore + "-" + o.s.BScore;
});
matches.Dump();
}
class Match{public int MatchId; public int SportId; public int StatusId; public string Score;}

Related

Sum and subtract using Linq C#

I need to return only one result, I need to sum all the states (1, 3) and (2, 4, 5) then subtract each total Example:
cashtransaction_amount, cashtransactionstatus_id
50.00 1 (+)
39.99 2 (-)
330.70 3 (+)
10.00 5 (-)
50.50 4 (-)
30.00 2 (-)
Sum(1,3): 50.00 + 330.70 = 380.70
Sum(2, 5, 4): 39.99 + 10.00 + 50.50 + 30.00 = 130.49
Final Result Sum(1,3) - Sum(2, 5, 4): 380.70 - 130.49 = 250.21
I tryed:
(from DataRow Transaction_DataRow in Dt_transaction.AsEnumerable()
where Transaction_DataRow.Field<Int32>("cashpaymenttype_id") == 1 // Cash Money
group Transaction_DataRow by Transaction_DataRow.Field<Int32>("cashtransactionstatus_id") into tmp
select new
{
cashtransaction_amount = tmp.Sum(x => (x.Field<Int32>("cashtransactionstatus_id") == 1 || x.Field<Int32>("cashtransactionstatus_id") == 3) ? x.Field<Double>("cashtransaction_amount") : -x.Field<Double>("cashtransaction_amount"))
}).Aggregate(Transaction_DataTable, (dt, result) => { dt.Rows.Add(result.cashtransaction_amount); return dt; });
Have you tried
cashtransaction_amount = tmp.Aggregate((a,b) =>
{
var id = b.Field<Int32>("cashtransactionstatus_id");
var amt = b.Field<Double>("cashtransaction_amount");
return id == 1 || id == 3 ? a + amt : a - amt;
});
Another way would be:
total = tmp.Select(b =>
{
var id = b.Field<Int32>("cashtransactionstatus_id");
return (id == 1 || id == 3 ? 1 : -1) *
b.Field<Double>("cashtransaction_amount");
}).Sum();
Or:
total = tmp.Sum(b => (new[]{1,3}.Contains(b.Field<Int32>("cashtransactionstatus_id"))
? 1 : -1) * b.Field<Double>("cashtransaction_amount"));

Possible to use anonymous type members during declaration?

Ok, so I'm trying to turn this:
sSelect = "SELECT CONVERT(VARCHAR, CommitDate, 101) AS XLabel, " +
"SUM(CASE WHEN NOT CompletionDate IS NULL THEN 1 ELSE 0 END) AS ClosedEvents, " +
"COUNT(CommitDate) AS Total, " +
"COUNT(CommitDate) - SUM(CASE WHEN NOT CompletionDate IS NULL THEN 1 ELSE 0 END) AS OpenEvents, " +
"SUM(CASE WHEN CompletionDate IS NULL AND commitdate < Convert(varchar(12),getdate(),101) THEN 1 ELSE 0 END) AS BehindSchedule";
into a Linq query. So far I've got:
var vEventsEntriesEnum = dtEventsEntries.AsEnumerable();
var vOpenEntriesData = from r in vEventsEntriesEnum
select new
{
XLabel = r["CommitDate"],
ClosedEvents = vEventsEntriesEnum.Sum(closed => (closed["CompletionDate"] != null) ? 1 : 0),
Total = vEventsEntriesEnum.Count(total => (total["CommitDate"] != null) ? true : false),
OpenEvents = Total - ClosedEvents,
};
which is where I realized I might have a problem. Total and ClosedEvents do not exist in that context.
While I can just go ahead and repeat the queries for ClosedEvents and Total and cast then subtract them or whatever, I was hoping there'd be a better way.
If there are any other ways of doing what I'm doing with the rest of the code, feel free to let me know.
Any ideas?
You have to materialize it somewhere. In query syntax you can use the let clause:
var vOpenEntriesData = from r in vEventsEntriesEnum
let ClosedEvents = vEventsEntriesEnum.Sum(closed => (closed["CompletionDate"] != null) ? 1 : 0)
select new
{
XLabel = r["CommitDate"],
ClosedEvents,
Total = vEventsEntriesEnum.Count(total => (total["CommitDate"] != null) ? true : false),
OpenEvents = Total - ClosedEvents,
};
This is similar to a variable in a loop.

create a query for date and average values

I need to select date and average values from a datacontext's table and I need to group it by year and by month.
In SQL it will look like this
select Year(data) as years, Month(data) as months, Avg(value) as prices from Prices
group by Year(data),MONTH(data)
order by years, months
I've created a LINQ query
var query0 = from c in dc.Prices
where Convert.ToDateTime(c.data).CompareTo(left) >= 0
&& Convert.ToDateTime(c.data).CompareTo(right) <= 0
&& c.idsticker.Equals(x)
group c by new { ((DateTime)c.data).Year, ((DateTime)c.data).Month }
into groupMonthAvg
select groupMonthAvg;
But I don't know how to get average values in result
Use the Average function.
var query0 = from c in dc.Prices
where Convert.ToDateTime(c.data).CompareTo(left) >= 0
&& Convert.ToDateTime(c.data).CompareTo(right) <= 0
&& c.idsticker.Equals(x)
group c by new { ((DateTime)c.data).Year, ((DateTime)c.data).Month }
into groupMonthAvg
select new
{
years = groupMonthAvg.Key.Year,
months = groupMonthAvg.Key.Month,
prices = groupMonthAvg.Average(x=>x.value)
};
Just use the Average function in your select:
var query0 = from c in dc.Prices
where Convert.ToDateTime(c.data).CompareTo(left) >= 0
&& Convert.ToDateTime(c.data).CompareTo(right) <= 0
&& c.idsticker.Equals(x)
group c by new { ((DateTime)c.data).Year, ((DateTime)c.data).Month }
into groupMonthAvg
select new {
groupMonthAvg.Key.Year,
groupMonthAvg.Key.Month,
YearAverage = groupMonthAvg.Average(x=>x.Year),
MonthAverage = groupMonthAvg.Average(x=>x.Month)
};
This should do it:
var query0 = from c in dc.Prices
where Convert.ToDateTime(c.data).CompareTo(left) >= 0
&& Convert.ToDateTime(c.data).CompareTo(right) <= 0
&& c.idsticker.Equals(x)
group c by new { ((DateTime)c.data).Year, ((DateTime)c.data).Month }
into groupMonthAvg
select new { Year = groupMonthAvg.Key.Year, Month = groupMonthAvg.Key.Month, Average => groupMonthAvg.Average(i => i.value) };

Is there something like `continue` to bypass or skip an iteration of query in LINQ?

Take this example:
int[] queryValues1 = new int[10] {0,1,2,3,4,5,6,7,8,9};
int[] queryValues2 = new int[100]; // this is 0 to 100
for (int i = 0; i < queryValues2.Length; i++)
{
queryValues2[i] = i;
}
var queryResult =
from qRes1 in queryValues1
from qRes2 in queryValues2
where qRes1 * qRes2 == 12
select new { qRes1, qRes2 };
foreach (var result in queryResult)
{
textBox1.Text += result.qRes1 + " * " + result.qRes2 + " = 12" + Environment.NewLine;
}
Obviously this code will result in:
1 * 12 = 12
2 * 6 = 12
3 * 4 = 12
4 * 3 = 12
6 * 2 = 12
But what I need is only the first 3 lines. That is I do not want if 2*6 = 12 the query checks if 6*2 is also 12. Is there a way to filter this in the LINQ query or I have to do it in the foreach loop afterward?
My question just is a sample to show what I mean. so I want to know the way of doing such thing no matter what is the type of object being linqed to!
In general the simple solution would be more where conditions since the where clauses are what by definition cause LINQ to skip iterations:
var queryResult =
from qRes1 in queryValues1
from qRes2 in queryValues1
where qRes1 * qRes2 == 12
&& qRes1 <= Math.Sqrt(12)
select new { qRes1, qRes2 };
You could use .Distinct() and create your own IEqualityComparer that compares objects based on what 'equals' means in your case.
So, for your original example:
class PairSetEqualityComparer : IEqualityComparer<Tuple<int, int>>
{
public bool Equals(Tuple<int, int> x, Tuple<int, int> y)
{
return (x.Item1 == y.Item1 && x.Item2 == y.Item2) ||
(x.Item1 == y.Item2 && x.Item2 == y.Item1);
}
public int GetHashCode(Tuple<int, int> obj)
{
return obj.Item1*obj.Item2;
}
}
And, you use it like this:
var queryResult =
(from qRes1 in queryValues1
from qRes2 in queryValues2
where qRes1 * qRes2 == 12
select new Tuple<int, int>(qRes1, qRes2)).Distinct(new PairSetEqualityComparer());
TakeWhile(condition):Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
foreach (var result in queryResult.TakeWhile(x => x.qRes1 <= Math.Sqrt(12)))

Select case in LINQ [duplicate]

This question already has answers here:
linq case statement
(6 answers)
Closed 8 years ago.
how can I translate this into LINQ?
select t.age as AgeRange, count(*) as Users
from (
select case
when age between 0 and 9 then ' 0-25'
when age between 10 and 14 then '26-40'
when age between 20 and 49 then '60-100'
else '50+' end as age
from user) t
group by t.age
Thank you!
Maybe this works:
from u in users
let range = (u.Age >= 0 && u.Age < 10 ? "0-25" :
u.Age >= 10 && u.Age < 15 ? "26-40" :
u.Age >= 15 && u.Age < 50 ? "60-100" :
"50+")
group u by range into g
select new { g.Key, Count=g.Count() };
check this may help you
var query = from grade in sc.StudentGrade
join student in sc.Person on grade.Person.PersonID
equals student.PersonID
select new
{
FirstName = student.FirstName,
LastName = student.LastName,
Grade = grade.Grade.Value >= 4 ? "A" :
grade.Grade.Value >= 3 ? "B" :
grade.Grade.Value >= 2 ? "C" :
grade.Grade.Value != null ? "D" : "-"
};
Use something like that:
class AgeHelper
{
private static Dictionary<IEnumerable<int>, string> dic = new Dictionary<IEnumerable<int>, string>
{
{ Enumerable.Range(0, 10), "0-25" },
{ Enumerable.Range(10, 5), "26-40" },
{ Enumerable.Range(15, 35), "60-100" }
};
public string this[int age]
{
get
{
return dic.FirstOrDefault(p => p.Key.Contains(age)).Value ?? "50+";
}
}
}
The rest of #Botz3000's answer:
from u in users
let range = new AgeHelper()[u.Age]
...
Something like this?
var users = (from u in Users
select new
{
User = u,
AgeRange =
u.Age >= 0 && u.Age <= 9 ? "0-25" :
u.Age <= 14 ? "26-50" :
u.Age <= 49 ? "60-100":
"50+"
}).GroupBy(e => e.AgeRange);
I don't know of any way how to create efficient SQL like this, using a LINQ statement. But you can use:
Use a stored procedure (or function), and call the stored procedure from LINQ.
Use Direct SQL
Sure you can use a lot of inline conditional statements (? :), but I don't think the result will be efficient.

Categories

Resources