I am using Entity Framework to work with my DB and I need help forming a LINQ query that will help me to get columns LoginId and AccNumber. I need only 1 LoginId for every single AccNumber, doesn't matter which LoginId it is. As it stands now, there are about 800K LoginIds and only 5000 AccNumber. Therefore, at the end I need 1 AccNumber associated with 1 LoginId. So I should have 5000 rows and 2 columns.
Here are the tables that I need joined:
Item
ItemId, AccNumber, other irrelevant columns
Block_Item
ItemId, BlockId, other irrelevant columns
Bookversion_Block
BookversionId, BlockId, other irrelevant columns
Sudent
LoginId, BookversionId, other irrelevant columns
Ideally, I want to replicate this SQL query using LINQ (just trying to provide as much info as possible)
select
LoginId,
AccessionNumber
from Item I
outer apply (select top 1 * from Block_Item where I.ItemId = ItemId) BI
outer apply (select top 1 * from BookletVersion_Block where BlockId = BI.BlockId) BVB
outer apply (select top 1 LoginId from Student where BookletVersionId = BVB.BookletVersionId) ST
Here is what I tried, however, the results that I get back are not correct and I get back like 183,000 records and it takes like 10 minutes to execute this. This line "var uniques = q.ToList();" is the one taking a long time.
using (var context = new MyContext())
{
context.Database.CommandTimeout = 1200;
var listOfAccessionNumbers = GetListOfAllAccessionNumbers(context);
var q = (from items in context.Items
join blockItem in context.Block_Item
on items.ItemId equals blockItem.ItemId into bi
join bookletVersion in context.BookletVersion_Block
on bi.Select(x => x.BlockId).FirstOrDefault() equals bookletVersion.BlockId into BVB
join student in context.Students
on BVB.Select(x => x.BookletVersionId).FirstOrDefault() equals student.BookletVersionId into st
//'VH098334'
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(),
AccNum = items.AccessionNumber.ToString() });
**var uniques = q.ToList();**
IList<string> listOfLogins = new List<string>();
foreach (var accessionNumber in listOfAccessionNumbers)
{
var i = q.ToList().Find(x => x.AccNum.Contains(accessionNumber));
listOfLogins.Add(i.LoginId);
}
}
Try to separate your linq operations, to hopefully see which one is taking the longest and where its going wrong.
Also, I suggest using a dictionary with accNumber as key and LoginID as value so you can make sure each accNumber is associated with 1 and only 1 LoginID.
Dictionary<string, string> uniques = new Dictionary<string, string>();
var q = (from items in context.Items
join bi in context.Block_Item
on items.ItemId equals bi.ItemId
select items).ToList();
//var first = bi.Select(x => x.BlockId).FirstOrDefault();
var val = (from b in context.BookletVersion_Block.Select(bv => bv.BlockId)
join a in q on b equals q.FirstOrDefault().BlockId into BVB
join c in context.Students on BVB.FirstOrDefault().BookletVersionId equals c.BookletVersionId into st
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(), AccNum = a.AccessionNumber.ToString() });
//if (!uniques.ContainsKey(bi.AccNum))
//uniques.Add(val.AccNum, val.LoginId);
uniques = val.ToDictionary(c => c.AccNum, c => c.LoginId);
Haven't tested it, so let me know the time taken and whether you get correct values.
The dictionary should ensure you get 5000 values, but I am not sure why it is taking so long, but lets fix the number of records problem first.
Related
I am using below code to join two tables based on officeId field. Its retuning 0 records.
IQueryable<Usage> usages = this.context.Usage;
usages = usages.Where(usage => usage.OfficeId == officeId);
var agencyList = this.context.Agencies.ToList();
var usage = usages.ToList();
var query = usage.Join(agencyList,
r => r.OfficeId,
a => a.OfficeId,
(r, a) => new UsageAgencyApiModel () {
Id = r.Id,
Product = r.Product,
Chain = a.Chain,
Name = a.Name
}).ToList();
I have 1000+ records in agencies table and 26 records in usage table.
I am expecting 26 records as a result with chain and name colums attached to result from agency table.
Its not returning anything. I am new to .net please guide me if I am missing anything
EDIT
#Tim Schmelter's solution works fine if I get both table context while executing join. But I need to add filter on top of usage table before applying join
IQueryable<Usage> usages = this.context.Usage;
usages = usages.Where(usage => usage.OfficeId == officeId);
var query = from a in usages
// works with this.context.usages instead of usages
join u in this.context.Agencies on a.OfficeId equals u.OfficeId
select new
{
Id = a.Id,
Product = a.Product,
Chain = u.Chain,
Name = u.Name
};
return query.ToList();
Attaching screenshot here
same join query works fine with in memory data as you see below
Both ways works fine if I add in memory datasource or both datasource directly. But not working if I add filter on usages based on officeId before applying join query
One problem ist that you load all into memory first(ToList()).
With joins i prefer query syntax, it is less verbose:
var query = from a in this.context.Agencies
join u in this.context.Usage on a.OfficeId equals u.OfficeId
select new UsageAgencyApiModel()
{
Id = u.Id,
Product = u.Product,
Chain = a.Chain,
Name = a.Name
};
List<UsageAgencyApiModel> resultList = query.ToList();
Edit: You should be able to apply the Where after the Join. If you still don't get records there are no matching:
var query = from a in this.context.Agencies
join u in this.context.Usage on a.OfficeId equals u.OfficeId
where u.OfficeId == officeId
select new UsageAgencyApiModel{ ... };
The following code can help to get the output based on the ID value.
Of course, I wrote with Lambda.
var officeId = 1;
var query = context.Agencies // your starting point - table in the "from" statement
.Join(database.context.Usage, // the source table of the inner join
agency => agency.OfficeId, // Select the primary key (the first part of the "on" clause in an sql "join" statement)
usage => usage.OfficeId , // Select the foreign key (the second part of the "on" clause)
(agency, usage) => new {Agency = agency, Usage = usage }) // selection
.Where(x => x.Agency.OfficeId == id); // where statement
I have a database with about 20 table, all of them have some columns which are same, eg, name, cost, year manufactured etc. I need to query those tables. what is best, most efficient way. tables and data have to stay as they are.
Here is what I am doing right now.
var table1 = (from a in _entities.ta1
join b in _entities.subTa on a.tid equals b.Id
select new
{
id = a.id,
name = a.name,
type = a.type,
}).ToList();
var table2 = (from a in _entities.ta2
join b in _entities.subTa2 on a.tid equals b.Id
select new
{
id = a.id,
name = a.name,
type = a.type,
}).ToList();
var abc = table1;
abc.AddRange(table2)
var list = new List<MyClass>();
foreach(var item in abc)
{
var classItem = new MyClass();
classItem.id = item.id;
classItem.name = item.name;
classItem.type = item.type;
list.Add(classItem);
}
return list;
This needs to be done to many table, which is not very efficient coding.
How can I improve this code?
You could use that, assuming that data types for all columns do match.
List<MyClass> list =
( //table1
from a in _entities.ta1
join b in _entities.subTa
on a.tid equals b.Id
select new MyClass()
{
id = a.id,
name = a.name,
type = a.type
})
.Concat(( //table2 (use .Union instead of .Concat if you wish to eliminate duplicate rows)
from a in _entities.ta2
join b in _entities.subTa2
on a.tid equals b.Id
select new MyClass()
{
id = a.id,
name = a.name,
type = a.type
}
)).ToList();
return list;
The reason why this could be more efficient is that it
groups all queries into one database query, thus causes less database traffic,
lets all the computation happen on the database server, therefore eliminating duplicate computation in the OP code, and
optimizes memory usage as it does NOT create a list of anonymous objects and then adding them to a new List<MyClass>.
This is the answer i was looking or. this is not ideal but it got the job done
var tables= new Dictionary<string, string>();
tables.Add("table1", "subTable1");
tables.Add("table2", "subTable2");
foreach (var table in tables)
{
var tableName = table.Key;
var subName= table.Value;
var data = _entities.Database.SqlQuery<MyClass>($#"select
a.Id,a.Name,b.subName from {tableName} a left join {subName} b on
a.subId=b.Id").ToList();
}
Thank you everyone for your contribution
I have two tables. Table One contains a list of Areas, and Table Two contains a list of Samples, with each Sample row containing Area_ID as a Foreign Key.
I need to retrieve all the records in my Area table with only the most recent corresponding Sample Status. I have this query, but it just returns one Area with the most recent sample from the Sample table:
var result = (
from a in db.area
join c in db.sample
on a.location_id equals c.location_id
select new
{
name = a.location_name,
status = c.sample_status,
date = c.sample_date
}).OrderByDescending(c => c.date).FirstOrDefault();
A solution could be filtering your second DbSet:
var result = from a in db.area
join c in db.sample.Where(s=>s.location_id==a.location_id).OrderByDescending(c => c.sample_date).Take(1)
on a.location_id equals c.location_id
select new
{
name = a.location_name,
status = c.sample_status,
date = c.sample_date
};
Another solution could be applying a group join:
var result = from a in db.area
join c in db.sample
on a.location_id equals c.location_id into samples
let sample=samples.OrderByDescending(c => c.sample_date).FirstOrDefault()
select new
{
name = a.location_name,
status = sample.sample_status,
date = sample.sample_date
};
If you use navigation properties could be even easier. Supposing you have a one to many relationship between Area and Sample:
var result =from a in db.area
let sample= a.Samples.OrderByDescending(c => c.sample_date).FirstOrDefault()
select new
{
name = a.location_name,
status = sample.sample_status,
date = sample.sample_date
};
I'm trying to get back a list of TransferIds, for each transfer a list of ChargeIds, and for each of those a list of ReferralMatches
here is what I've got
(from c in Commissions
where c.TransferStatus == "Paid"
where c.AdminHasReleased == false
join r in ReferralMatches on c.ReferralMatchId equals r.ReferralMatchId
group c by new { c.TransferId } into grp
select new {
TransferId = grp.Key.TransferId,
Charges = from c in grp
group c by c.ChargeId into grp2
select new {
ChargeId = grp2.Key,
Referrals = grp2 }
})
This works and is very close. It pulls back something that looks like this:
This looks like Charges that belong to a TransferId but what I need is ReferralMatches that belong to Charges that belong to a transferId. I've tried another select to pull in 'r' but running into errors.
I think LINQ people will be able to gather what they need from this post but if more info is needed, kindly let me know. Thank you.
EDIT, adding table samples
The two expanded tables are what I have to work with. It probably isn't useful but keep in mind that the ReferralMatch table also has ChargeId. One chargeId can cover multiple ReferralMatches but once the funds are available a bank transfer occurs...when that happens, records are created in the Commissions table.
So what I'm looking for is a list of TransferIds, foreach Id a list of chargeIds, and foreach chargeId a list of ReferralMatches...the innermost list of ReferralMatches would be full records from that table.
EDIT, more attempts
here's my latest attempt
from c in Commissions
where c.TransferStatus == "paid"
group c by c.TransferId into transferGroup
select new {
TransferId = transferGroup.Key,
Charges = from c in transferGroup
join r in ReferralMatches on c.ReferralMatchId equals r.ReferralMatchId
group c by c.ChargeId into chargeGroup
select new {
ChargeId = chargeGroup.Key,
Referrals = from r in chargeGroup
select new {
Referral = r
}
}
}
which pulls up something like this:
but unless I'm reading this incorrectly, the innermost item is still commission table which doesn't make sense. I need that to be ReferralMatches that have a ChargeId of [whatever]
You might need to separate out the expression into two and pull through the Referrals in the first expression, and then group on a second expression as follows:
var commissionAndReferrals =
from c in Commissions
where c.TransferStatus == "Paid"
where c.AdminHasReleased == false
join r in ReferralMatches on c.ReferralMatchId equals r.ReferralMatchId
select new { Commisson = c, Referral = r };
var result =
from cAndR in commissionAndReferrals
group cAndR by cAndR.Commisson.TransferId into transferGroup
select new
{
TransferId = transferGroup.Key,
Charges = from c in transferGroup
group c by c.Commisson.ChargeId into chargeGroup
select new
{
ChargeId = chargeGroup.Key,
Referrals = chargeGroup.Select(x => x.Referral)
}
};
Question - how to remove certain records from LINQ, like i have some records i wanted them to be removed from my LINQ.
Scenario - i have table A with 10 records and table B with 2 records i want to remove records that are belong to B to be removed from A [using linq]
-below i have all the records in q and i want to remove the records that are there in p.
var p = from c in q
join dr in dc.TableData on c.Id equals dr.CaseId
select new View()
{
ActiveCaseId = c.ActiveCaseId,
Id = c.Id
};
q = q.Except(p);
You can't do it with the Except as you show, because p and q are different types. But it would also be a bit clumsy.
You can do it in one query:
var p = from c in q
where !dc.TableData.Any(dr => dr.CaseId == c.Id)
select new View()
{
ActiveCaseId = c.ActiveCaseId,
Id = c.Id
};