I'm looking for some advice on how best to get the first record when using a join with multiple tables, as demonstrated below.
I have three tables:
Leads <-- this should be unique in the results
LeadAddresses (joining
table)
Addresses
Normally I'd join them like this:
from t2
in db.Leads
.Where(o => t1.LeadId == o.Lead_ID)
from t4
in db.LeadAddresses
.Where(o => t2.Lead_ID == o.Lead_ID)
.DefaultIfEmpty()
from t5
in db.Addresses
.Where(o => t4.Address_ID == o.Address_ID)
.DefaultIfEmpty()
(if this is bad practice, let me know ;)
I'm looking to get a property from the Addresses table (the one with, say, the maximum ID) for each Lead record and project to a model:
select new LeadGridModel
{
...
});
Example:
Lead Company | City | ZIP
==============================
Company 1 | Boston | 00000
Company 2 | Houston | 00001
from l in db.Leads
from a in l.LeadAddresses.Select(la => la.Address).OrderByDescending(a => a.ID).Take(1).DefaultIfEmpty()
select new { l, a }
This might look tricky, but you understand it part by part:
Using OrderByDescending in combination with Take(1) we take the address with the maximum ID
Using DefaultIfEmpty we create a left-join.
Be aware that this pattern forces a loop-join due to limitation of SQL Server. For small result sets this is usually not a problem.
Related
Books Table
Id VendorId ASIN Price
-- -------- ---- ------
1 gold123 123 10
2 sil123 123 11
3 gold456 456 15
4 gold678 678 12
5 sil456 456 12
6 gold980 980 12
I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.
Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.
I tried following
var gold = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("gold"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
var asinsForGold = gold.Select(x => x.ASIN).ToList();
var correspondingSilver = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("sil")
&& asinsForGold.Contains(x.ASIN)).ToList();
var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();
var goldWithoutCorrespondingSilver = gold.Where(x =>
!correspondingSilverAsins.Contains(x.ASIN));
Can We apply self join or better way to get result only in one query instead of two query and several other list statement.
It's just another predicate, "where a corresponding silver vendor doesn't exist":
var goldWoSilver = _repository.Query<Books>()
.Where(x => x.VendorId.Contains("gold"))
.Where(x => !_repository.Query<Books>()
.Any(s => s.ASIN == x.ASIN
&& s.VendorId.Contains("sil"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.
See if it helps -
var goldWithoutCorrespondingSilver = from b1 in books
join b2 in books on b1.ASIN equals b2.ASIN
where b1.VendorId.Contains("gold")
group b2 by b1.VendorId into g
where !g.Any(x => x.VendorId.Contains("sil"))
select g.FirstOrDefault();
What I have done is -
Selected records with matching ASIN
Grouped them by VendorID
Selected ones which do not have sil
My requirement is to fetch the SiteItemOnHand records, group by Businessunitid,inventoryitemid and get the whole SiteItemOnHand record in each group which is having max(lastmodifiedtimestamp)
I am trying to write nhibernate equivalent query for the below sql query
Select x.* from
(
Select businessunitid,inventoryitemid,max(lastmodifiedtimestamp) as maxvalue from InventoryItemBUOnHand
group by businessunitid,inventoryitemid HAVING inventoryitemid in (939) and businessunitid=829
) as x
inner join
(
Select businessunitid,inventoryitemid,lastmodifiedtimestamp,inventoryitemonhandid from InventoryItemBUOnHand
where inventoryitemid in (939) and businessunitid=829
) as y
on x.businessunitid=y.businessunitid and x.inventoryitemid =y.inventoryitemid and x.maxvalue=y.lastmodifiedtimestamp
I have so many limitations. I am only allowed to use Nhibernate 3.0 version and also writing sql queries (Criteria) strictly prohibited.
I was able to write only half part of the query which is below. Any help would be appreciated.
var query= _session.QueryOver<SiteItemOnHand>()
.Where(x => x.Businessunitid == siteID)
.Where(x => x.End == endDate)
.Where(x => x.CountID != filterCount.ID)
.WhereRestrictionOn(x => x.ItemID).IsIn(itemIdList.ToArray())
.SelectList(list => list
.SelectMax(x => x.LastModifiedTimestamp)
.SelectGroup(x => x.Businessunitid)
.SelectGroup(x => x.ItemId));
The above query is generating the below SQL Query which only returns the three columns, but i need entire record because after retriving the record i need to update. I need to fetch all the SiteItemOnHand records which satisfies the above query.
SELECT max(this_.lastmodifiedtimestamp) as y0_, this_.businessunitid as y1_, this_.inventoryitemid as y2_
FROM InventoryItemBUOnHand this_
WHERE this_.businessunitid = 567 and this_.enddate = '1/31/2019 1:18:17 AM' and not (this_.itemcountid = 958)
and this_.inventoryitemid in (744,755)
GROUP BY this_.businessunitid, this_.inventoryitemid
Any help would be appreciated. Please let me know if you need more information.
If I understand you correctly you have data like this and you want the marked records. I don't know if iventoryitemonhandid is the id of the table, so I'm not relying on that.
Table InventoryItemBUOnHand (mapped as SiteItemOnHand)
| businessunitid|inventoryitemid|lastmodifiedtimestamp|inventoryitemonhandid|
| 829| 939| 2019-01-01 00:00:00| 100| <--
| 829| 940| 2019-01-02 00:00:00| 101|
| 829| 940| 2019-01-03 00:00:00| 102| <--
| 829| 950| 2019-01-04 00:00:00| 103|
| 829| 950| 2019-01-10 00:00:00| 104| <--
| 829| 950| 2019-01-06 00:00:00| 105|
If yes, then I'd use a SubQuery like in this question:
SO: NHibernate - Select full records from aggregates
Modified (not tested) to your requirements it would look something like this:
int siteID = 829;
List<int> itemIdList = new List<int>() { 939, 940, 950 };
SiteItemOnHand siAlias = null;
var subQuery = QueryOver.Of<SiteItemOnHand>()
.Where(x => x.Businessunitid == siAlias.Businessunitid)
.And(x => x.ItemID == siAlias.ItemID)
.Select(Projections.Max<SiteItemOnHand>(y => y.lastmodifiedtimestamp));
var siteItems = Session.QueryOver<SiteItemOnHand>(() => siAlias)
.Where(x => x.Businessunitid == siteID)
.AndRestrictionOn(x => x.ItemID).IsIn(itemIdList.ToArray())
.WithSubquery.Where(x => siAlias.lastmodifiedtimestamp == subQuery.As<DateTime>())
.List();
The goal here is to use a Subquery to filter the correct max dates per group and then use that to filter the actual records.
The resulting SQL would look like this:
SELECT <field_list> FROM InventoryItemBUOnHand
WHERE Businessunitid = 829
AND inventoryitemid in (939, 940, 950)
AND this_.lastmodifiedtimestamp =
(SELECT max(this_0_.lastmodifiedtimestamp) as y0_
FROM InventoryItemBUOnHand this_0_
WHERE this_0_.Businessunitid = this_.Businessunitid and this_0_.ItemID = this_.ItemID)
Caution: Comparing lastmodifiedtimestamp may cause unwanted results when two records have the same value for the same businessunitid and inventoryitemid. If that can happen, you can add an OrderBy and select the first record only. It becomes even simpler if inventoryitemonhandid is a unique index.
I have 2 tables, one is Posts another is Comments. These tables contain "RatedPoint" field.
I want to take 5 users who have the highest point.
For example, user ID =1 and its total point 50 in Post table
and it's total point is 25 in Comment table, so its total point is 75
so, i have to look whole members and after choose 5 highest point
It seems a bit complicated, i hope its clear..
I tried something like that
var abc= csEntity.Users.Where(u => csEntity.Posts.Any(p => u.Id == p.UserId)).
Take(userCount).OrderByDescending(u => u.Posts.Count).ToList();
or..
var xyz = csEntity.Posts.Where(p => csEntity.Comments.Any(c => c.UserId == p.UserId));
I dont want to use 2 different list if possible.. is it possible to do it in one query?
I could do it with 2 for loops, but i think its a bad idea..
Post TABLE
Comments TABLE
As you see, these two tables contain userID and each user has RatedPoint...
I think now its clear
EDIT: Maybe a user never write a comment or never write a post just write a comment.. then i think we musnt make equal posts.userId=comments.UserId
Here is a LINQ expression that does what you seem to be asking for:
var result = from p in posts
join c in comments on p.Id equals c.Id
select new { Id = p.Id, Total = p.Points + c.Points };
That provides the actual joined data. Then you can pick the top 5 like this:
result.OrderByDescending(item => item.Total).Take(5)
Note that the above does assume that both tables always have each user, even if they didn't post or comment. I.e. they would simply have a point count of 0. Your updated question clarifies that in your case, you have potentially disjoint tables, i.e. a user can be in one table but not the other.
In that case, the following should work for you:
var leftOuter = from p in posts
join c in comments on p.Id equals c.Id into groupJoin
let c = groupJoin.SingleOrDefault()
select new { Id = p.Id, Total = p.Points + (c == null ? 0 : c.Points) };
var rightAnti = from c in comments
join p in posts on c.Id equals p.Id into groupJoin
let p = groupJoin.SingleOrDefault()
where p == null
select new { Id = c.Id, Total = c.Points };
var result = leftOuter.Concat(rightAnti);
The first LINQ expression does a left outer join. The second LINQ expression does a left anti-join (but I call it "right" because it's effectively the right-join of the original data :) ). I'm using SingleToDefault() to ensure that each user is in each table once at most. The code will throw an exception if it turns out they are present more than once (which otherwise would result in that user being represented in the final result more than once).
I admit, I don't know whether the above is the most efficient approach. I think it should be pretty close, since the joins should be optimized (in objects or SQL) and that's the most expensive part of the whole operation. But I make no promises regarding performance. :)
I have database structure for Plans and PlanVersions in following relationship:
+------+ +-------------+
| Plan | --------------> | PlanVersion |
+------+ 1 (1..n) +-------------+
PlanVersion is version table tracking all version changes and it have ActiveFromData and ActiveToData columns show us when was this version active.
Plan also can have SubPlans which can change in time so PlanVersion also have ParrentPlanId column which tell us what was current subplan for version.
What i want is to get all changes of all SubPlans since some time and for specific Plan.
This query is what i came with:
DECLARE #since datetime;
set #since = '2014-08-27 12:00:00.000';
DECLARE #parrentPlan bigint;
set #parrentPlan = 1;
SELECT pv.*
FROM [dbo].[PlanVersion] pv
INNER JOIN
/* Query Over subselect*/
(
SELECT PlanId, MAX(ActiveFromDate) AS MaxActiveFromDate
FROM [dbo].[PlanVersion] pv
WHERE pv.ParentPlanId=#parrentPlan
GROUP BY PlanId
) groupedVersions
ON pv.ParentPlanId = groupedVersions.PlanId
AND pv.ActiveFromDate = groupedVersions.MaxActiveFromDate
WHERE (pv.ActiveFromDate>=#since OR pv.ActiveToDate>#since)
Now i want is translate that to Nhibernate QueryOver:
i have this code
var subquery = QueryOver.Of<PlanVersion>()
.Where(x => x.ParentPlan.Id == parrentPlanId)
.Select(
Projections.Group<PlanVersion>(e => e.ParrentPlan.Id),
Projections.Max<PlanVersion>(e => e.ActiveFromDate)
);
But i dont know how to write that inner join on Two columns from suquery in QueryOver.
Notes:
We use Nhibernate 3.3 with 4.0 in testing
This query will be part of polling so performance is really important for me
I would say, that this has solution. We have to use a bit more complex SQL in fact. This approach I've already deeply explained here:
Query on HasMany reference
So, below is just a draft based on your subquery draft. What we are doing, is creating two subselects in fact (check the intended SQL here)
PlanVersion planVersion = null;
// the most INNER SELECT
var maxSubquery = QueryOver.Of<PlanVersion>()
.SelectList(l => l
.SelectGroup(item => item.ParentPlan.Id)
.SelectMax(item => item.ActiveFromDate)
)
// WHERE Clause
.Where(item => item.ParentPlan.Id == planVersion.ParentPlan.Id)
// HAVING Clause
.Where(Restrictions.EqProperty(
Projections.Max<PlanVersion>(item => item.ActiveFromDate),
Projections.Property(() => planVersion.ActiveFromDate)
));
// the middle SELECT
var successSubquery = QueryOver.Of<PlanVersion>(() => planVersion )
// the Plan ID
.Select(pv => pv.ParentPlan.Id)
.WithSubquery
.WhereExists(maxSubquery)
;
having this subqueries, we can ask for plan itself:
// the most outer SELECT
var query = session.QueryOver<Plan>()
.WithSubquery
.WhereProperty(p => p.Id)
.In(successSubquery)
.List<Plan>();
There could be some minor typos, but the draft should give you clear answer how to...
I have three tables, which two of them are in many to many relationship.
Picture:
This is the data in middle mm table:
Edit:
Got until here, I get proper 4 rows back, but they are all the same result(I know I need 4 rows back, but there are different results)
return this._mediaBugEntityDB.LotteryOffers
.Find(lotteryOfferId).LotteryDrawDates
.Join(this._mediaBugEntityDB.Lotteries, ldd => ldd.LotteryId, lot => lot.Id, (ldd, lot) =>
new Lottery
{
Name = lot.Name,
CreatedBy = lot.CreatedBy,
ModifiedOn = lot.ModifiedOn
}).AsQueryable();
My question is, how can I retrieve all the Lotteries via many to many table WHERE I have LotteryOfferId given only?
What I want to achieve is to get data from lottery table by LotteryDrawDateId.
First I use LotteryOfferId to get DrawDates from middle table, and by middle table I get drawDateIds to use them in LotteryDrawDate table. From that table I need to retreive Lottey table by LotteryId in LotteryDrawDate table.
I gain this by normal SQL(LotteryOffersLotteryDrawDates is middle table in DB, not seen in model):
select
Name, Lotteries.CreatedBy, Lotteries.ModifiedOn, count(Lotteries.Id)
as TotalDrawDates from Lotteries join LotteryDrawDates on Lotteries.Id
= LotteryDrawDates.LotteryId join LotteryOffersLotteryDrawDates on LotteryDrawDates.Id =
LotteryOffersLotteryDrawDates.LotteryDrawDate_Id
where LotteryOffersLotteryDrawDates.LotteryOffer_Id = 19 group by
Name, Lotteries.CreatedBy, Lotteries.ModifiedOn
But Linq is different story :P
I would like to do this with lambda expressions.
Thanks
db.LotteryOffer.Where(lo => lo.Id == <lotteryOfferId>)
.SelectMany(lo => lo.LotteryDrawDates)
.Select( ldd => ldd.Lottery )
.GroupBy( l => new { l.Name, l.CreatedBy, l.ModifiedOn } )
.Select( g => new
{
g.Key.Name,
g.Key.CreatedBy,
g.Key.ModifiedOn,
TotalDrawDates = g.Count()
} );
You can do this:
var query = from lo in this._mediaBugEntityDB.LotteryOffers
where lo.lotteryOfferId == lotteryOfferId
from ld in lo.LotteryDrawDates
group ld by ld.Lottery into grp
select grp.Key;
I do this in query syntax, because (in my opinion) it is easier to see what happens. The main point is the grouping by Lottery, because you get a number of LotteryDrawDates any of which can have the same Lottery.
If you want to display the counts of LotteryDrawDates per Lottery it's better to take a different approach:
from lot in this._mediaBugEntityDB.Lotteries.Include(x => x.LotteryDrawDates)
where lot.LotteryDrawDates
.Any(ld => ld.LotteryDrawDates
.Any(lo => lo.lotteryOfferId == lotteryOfferId))
select lot
Now you get Lottery objects with their LotteryDrawDates collections loaded, so afterwards you can access lottery.LotteryDrawDates.Count() without lazy loading exceptions.