Convert nested T-SQL select query to linq - entity-framework

I have the following SQL query which I am trying to translate to a LINQ query:
SELECT
CustomerId,
SUM(Bills.BillAmount) AS BillAmountTotal,
SUM(COALESCE(Pay.Paid, 0)) AS [Payments]
FROM
Bills
LEFT OUTER JOIN
(SELECT
BillId,
SUM(PaymentAmount) AS [Paid]
FROM
BillPayments
GROUP BY
BillId) Pay ON Pay.BillId = Bills.Id
GROUP BY
CustomerId

I solved it using the following code :
var pay = from p in db.BillPayments
group p by new { p.BillId } into g
select new
{
payKey = g.Key,
TotalPayments = g.Sum(p => p.PaymentAmount)
};
var query = from c in db.Customers
join b in db.Bills on c.Id equals b.CustomerId
join p in pay on b.Id equals p.payKey.BillId into cs
from xx in cs.DefaultIfEmpty()
group new { c, b, xx } by new { c.Name, c.MobilePhone, c.Id, c.CustomerCode } into g
select new
{
Received = g.Key,
TotalPayment = g.Sum(p => p.xx.TotalPayments == null ? 0 : p.xx.TotalPayments),
TotlalBilling = g.Sum(p => p.b.BillAmount),
GrandTotal = g.Sum(p => p.b.BillAmount) - g.Sum(p => p.xx.TotalPayments == null ? 0 : p.xx.TotalPayments)
};

Related

Linq Limit/Group by Id when joining IQueryables

I have a list of ids, that I then have multiple IQueryables that at the end I am joining to return my results. However some of these queries could have multiple records for that particular ID and I only need 1 record per ID from my set of x IDs. For example there could be 1 Name record, 3 Addresses, 2 Emails, 4 Phones for a single ID, but I only need 1 of each, does not matter which if multiple exist.
var clientNames = (
from n in db.Names
join cnl in db.ClientNameLinks on n.name_id equals cnl.name_id
where request.Ids.Contains(cnl.client_id)
&& n.name_id > 0
&& n.nametype_id != null
select new
{
n.display_name,
cnl.client_id
});
var clientAddresses = (
from a in db.Addresses
join cal in db.ClientAddressLinks on a.address_id equals cal.address_id
where request.Ids.Contains(cal.client_id)
&& a.address_id > 0
select new {
a.display_address,
cal.client_id
});
var clientEmails = (
from e in db.Emails
join cel in db.ClientEmailLinks on e.email_id equals cel.email_id
where request.Ids.Contains(cel.client_id)
&& e.email_id > 0
select new {
e.email_address,
cel.client_id
});
var clientPhones = (
from p in db.Phones
join cpl in db.ClientPhoneLinks on p.phone_id equals cpl.phone_id
where request.Ids.Contains(cpl.client_id)
&& p.phone_id > 0
select new {
p.phone_num,
cpl.client_id
});
var rows = await (
from n in clientNames
join a in clientAddresses on n.client_id equals a.client_id into n_a
from na in n_a.DefaultIfEmpty() //LEFT JOIN
join e in clientEmails on n.client_id equals e.client_id into n_e
from ne in n_e.DefaultIfEmpty() //LEFT JOIN
join ph in clientPhones on n.client_id equals ph.client_id into n_p
from np in n_p.DefaultIfEmpty() //LEFT JOIN
select new PiiDiamondClient
{
ClientId = n.client_id,
DisplayName = n.display_name,
DisplayAddress = na.display_address,
EmailAddress = ne.email_address,
PhoneExtension = np.phone_ext,
PhoneNumber = np.phone_num
}).Distinct().ToListAsync();
return rows.OrderBy(x => x.ClientId).ToList();
Your last query can be rewritten in the following way. It will exactly get one record from JOIN which can multiply result set.
var query =
from n in clientNames
from a in clientAddresses.Where(a => n.client_id == a.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
from e in clientEmails.Where(e => n.client_id == e.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
from ph in clientPhones.Where(ph => n.client_id == ph.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
select new PiiDiamondClient
{
ClientId = n.client_id,
DisplayName = n.display_name,
DisplayAddress = a.display_address,
EmailAddress = e.email_address,
PhoneExtension = ph.phone_ext,
PhoneNumber = ph.phone_num
};

About Custom Fields of Moodle

I am using Moodle 3.11.
I have a custom course field named discount.
how can i fetch names of all the course that provides discount on a new page?
You could use this query for Moodle - replace d.value with the required field
$sql = 'SELECT c.id AS courseid, c.fullname AS coursename, d.value AS discount
FROM mdl_customfield_category cat
JOIN mdl_customfield_field f ON f.categoryid = cat.id AND f.shortname = :shortname
JOIN mdl_customfield_data d ON d.fieldid = f.id
JOIN mdl_course c ON c.id = d.instanceid
WHERE cat.area = :area'
$courses = $DB->get_records_sql($sql, array('area' => 'course' 'shortname' => 'discount'));
for each ($courses as $course) {
// Display $course->fullame and $course->discount
}
Or this for Totara
$sql = 'SELECT c.id AS courseid, c.fullname, d.data AS discount
FROM {course_info_field} f
JOIN {course_info_data} d ON d.fieldid = f.id AND d.data > ''
JOIN {course} c ON c.id = d.courseid
WHERE f.shortname = :shortname'
$courses = $DB->get_records_sql($sql, array('shortname' => 'discount'));
for each ($courses as $course) {
// Display $course->fullame and $course->discount
}

Grouping query fields from different tables (Entity Framework and ASP.NET Core)

I'm trying to translate this query to entity framework, but it's not working. Can anyone understand why?
SELECT
T3.COD1, T1.COD2,
COUNT(T1.X1) R1 ,
ROUND(SUM(T1.X2) / SUM(T2.X3),2) R2,
FROM
table1 A, table2 T2, table3 T3
WHERE
T1.O_KEY = T3.O_KEY
AND T1.M_KEY = T2.M_KEY
GROUP BY
T3.COD1, T1.COD2
ORDER BY
T3.COD1, T1.COD2
Where the (WHERE T1.O_KEY = T3.O_KEY and T1.M_KEY = T2.M_KE) instruction is equivalent to INNER JOIN:
SELECT
T3.COD1, T1.COD2,
COUNT(T1.X1) R1 ,
ROUND(SUM(T1.X2) / SUM(T2.X3),2) R2,
FROM
table1 A
INNER JOIN
table3 T3 ON T1.O_KEY = T3.O_KEY
INNER JOIN
table2 T2 ON T1.M_KEY = T2.M_KEY
GROUP BY
T3.COD1, T1.COD2
ORDER BY
T3.COD1, T1.COD2
The conversion:
//{Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1[App.Models.T1Model]}
var T1 = _repositoryT1.GetAll(); // return context.T1
var T2 = _repositoryT2.GetAll(); // return context.T2
var T3 = _repositoryT3.GetAll(); // return context.T3
//Note: The GetAll method does not return results to memory. Operates on Dbset/LINK.T1, T2 and T3 are database tables. The context class is an integral part of Entity Framework. An instance of DbContext represents a session with the database which can be used to query and save instances of entities to a database
var query =
(from xT1 in T1
join xT3 in T3 on new { k1 = (long)xT1.O_KEY } equals new { k1 = (long)xT3.O_KEY }
join xT2 in T2 on new { k1 = (long)xT1.M_KEY } equals new { k1 = (long)T2.M_KEY}
group new { xT3, xT1 } by new { xT3.COD1 , xT1.COD2, xT1, xT3, xT2 } into G
select new ResultDTO
{
COD1 = G.Key.COD1,
COD2 = G.Key.COD2,
R1 = T1.Count(),
R2 = T1.Sum(x => (long)x.X2) / T2.Sum(x => (long)x.X2),
});
I found the answer.
var query =
(from xT1 in T1
join xT3 in T3 on new { k1 = (long)xT1.O_KEY } equals new { k1 = (long)xT3.O_KEY }
join xT2 in T2 on new { k1 = (long)xT1.M_KEY } equals new { k1 = (long)T2.M_KEY}
select new { xT1, xT2, xT3 } into x
group x by new { x.xT3.COD1 , x.xT1.COD2} into G
select new ResultDTO
{
COD1 = G.Key.COD1,
COD2 = G.Key.COD2,
R1 = G.Count(),
R2 = Convert.ToDecimal(G.Sum(x => (decimal)x.X2) / T2.Sum(x => (decimal)x.X2)),
});

How do you use aggregate functions in LINQ?

I can do this really easily in SQL but I can't translate it into LINQ.
The SQL:
SELECT EmployeeId
,YEAR(DistributionDate) AS DistributionYear
,SUM(SalesLocal * FX.ExchangeRate) AS TotalDistributions
FROM Distributions AS DD INNER JOIN FiscalPeriod AS FP ON DD.DistributionDate BETWEEN FP.StartDate AND FP.EndDate
INNER JOIN FXRates AS FX ON FP.FiscalPeriodId = FX.FiscalPeriodId AND FX.FromCurrencyId = DD.CurrencyId AND FX.ToCurrencyId = 'USD'
GROUP BY EmployeeId, YEAR(DistributionDate)
ORDER BY EmployeeId, DistributionYear
This is my broken attempt at translation:
var pointList = (from dd in db.Distributions
join e in db.Employee on dd.EmployeeId equals e.EmployeeId
join fp in db.FiscalPeriod.Where(p => dd.DistributionDate >= p.StartDate && dd.DistributionDate <= p.EndDate)
join fx in db.Fxrates on (fp.FiscalPeriodId equals fx.FiscalPeriodId) && (fx.FromCurrencyId equals dd.CurrencyId) && (fx.ToCurrencyId equals "USD")
group by dd.EmployeeId && dd.DistributionDate.Year
select new Point<int, decimal?>
{
X = dd.DistributionDate.Year,
Y = dd.Sum(dd.SalesLocal * fx.ExchangeRate)
}).ToList();
Visual Studio complains first that dd is not declared and then really complains once it reaches the join to Fxrates.
I think this may be close to what you want:
var pointList = (from dd in db.Distributions
join e in db.Employee on dd.EmployeeId equals e.EmployeeId
from fp in db.FiscalPeriod.Where(p => dd.DistributionDate >= p.StartDate && dd.DistributionDate <= p.EndDate)
from fx in db.Fxrates.Where(fx2 => fp.FiscalPeriodId == fx2.FiscalPeriodId && fx2.FromCurrencyId == dd.CurrencyId && fx2.ToCurrencyId == "USD")
group new { dd, fx } by new { dd.EmployeeId, dd.DistributionDate.Year } into distGroup
select new
{
X = distGroup.Key.Year,
Y = distGroup.Sum(d => d.dd.SalesLocal * d.fx.ExchangeRate)
}).ToList();

Entity Framework 4 INNER JOIN help

Can anyone tell me how to write the following query in Entity Framework 4.0? "Blogs" and "Categories" are my entities. The query basically returns me the list of categories and the number of blogs that are in that category.
SELECT b.CategoryId, c.Value, Count(b.Id) AS [Count] FROM dbo.Blogs b
INNER JOIN dbo.Categories c ON b.CategoryId = c.Id
GROUP BY b.CategoryId, c.Value
Thanks in advance.
The following should work (LinqToEntities):
var categories = from c in oc.Categories
select new
{
CategoryId = c.Id,
c.Value,
Count = c.Blogs.Count()
}
This will give you a list of category ids and values and for each category id you get the number of blogs in that category.
EDIT: To give an answer to the question in your comment: this isn't possible in LinqToEntities but you can do it in Entity SQL.
var results = new ObjectQuery<DbDataRecord>(
#"SELECT y, COUNT(y)
FROM MyEntities.Blogs AS b
GROUP BY YEAR(b.CreatedDate) AS y", entities).ToList();
var nrBlogsPerYear = from r in results
select new { Year = r[0], NrBlogs = r[1] };
In the Entity SQL query, you should replace MyEntities with the name of your context.
EDIT: As I just found out through a comment by Craig, grouping by year is possible in L2E so you can write your query like this:
var nrBlogsPerYear = from b in oc.Blogs
group b by b.CreatedDate.Year into g
select new { Year = g.Key, NrBlogs = g.Count() };
If you have navigation properties in your entities, you can do that :
var cats = from c in db.Categories
let count = c.Blogs.Count()
where count > 0
select new
{
CategoryId = c.Id,
Value = c.Value,
Count = count
};
If you prefer to use an explicit join, you can do it like that :
var cats = from c in db.Categories
join b in db.Blogs on c.Id equals b.CategoryId into g
select new
{
CategoryId = c.Id,
Value = c.Value,
Count = g.Count()
};
var query = from p in B.Products
join c in B.Categories on p.CategoryID equals c.CategoryID
orderby c.CategoryName,p.ProductName
select new
{
c.CategoryName ,
p.ProductName,
p.ReorderLevel
};
GridView1.DataSource = query.ToList();
GridView1.DataBind();