What am I doing wrong in this SQL to LINQ conversion? - sql

I'm using LINQPad in my effort to convert SQL to LINQ and learn LINQ in the process. I'm running into some problems, though, and was hoping someone could look at my resulting LINQ code to see if I'm on the right track?
Here's my SQL code:
Select Count(Convert(varchar(250),
Comment.CommentId)) as Num,
DiscussionBoard.ItemName,
Status.Status,
Status.Sequence
From Comment inner join status on Comment.StatusID = Status.StatusID
inner join DiscussionBoard on Comment.DiscussionBoardID
= DiscussionBoard.DiscussionBoardID
WHERE discussionboard.DiscussionBoardID
= '3ab7c139-317c-4450-9823-45a40ea6d0ff'
Group By status.Status,
Status.Sequence,
DiscussionBoard.ItemName
ORDER BY Status ASC
Here is the LINQ code that I've come up with so far. Bear with me, I'm just now learning LINQ and I haven't quite got my head around it. Any pointers would be greatly appreciated:
from Comment in Comments
where DiscussionBoard.DiscussionBoardID == '3ab7c139-317c-4450-9823-45a40ea6d0ff'
join Status in Statuses on Comment.StatusID equals Statuses.StatusID
join DiscussionBoard in DiscussionBoards on Comment.DiscussionBoardID equals DiscucussionBoard.DiscussionBoardID
group CountGroup by new {
Status.Status,
Status.Sequence,
DiscussionBoard.DiscussionBoardID
}
select new
{
Count = CountGroup.Count(),
DiscussionBoard.ItemName,
Status.Status,
Status.Sequence
}

group x by y
This fragment ends a query.
I think you meant:
group x by y into z
This fragment continues the query with z in scope. and removes all prior range variables from scope. z is an IGrouping<y, x>, which is to say, the key type is y's type and the group element type is x's type.
Here's my stab at your query:
from comment in Comments
where comment.DiscussionBoard.DiscussionBoardID == '3ab7c139-317c-4450-9823-45a40ea6d0ff'
let status = comment.Status
let board = comment.DiscussionBoard
group comment by new {
status.Status,
status.Sequence,
board.ItemName
} into g
select new
{
Count = g.Count(),
ItemName = g.Key.ItemName,
Status = g.Key.Status,
Sequence = g.Key.Sequence
}
Another way to open this query would be:
from board in DiscussionBoards
where board.DiscussionBoardID == '3ab7c139-317c-4450-9823-45a40ea6d0ff'
from comment in board.Comments

Related

Query on other query linq

Just assume a simple example:I have two table and use this query:
select *
from People
where PersonGuid = (select PersonGuid from Sellers where Guid = '')
How can I write this query with linq?
I tried this:
var person = from p in loginContext.Person where p.PersonGuid =
(loginContext.Seller.Where(s => s.Guid == sellerGuid).FirstOrDefaultAsync());
but that's wrong.
What is the right way to write it?
If you were to rewrite your SQL query as
select *
from People AS PE
JOIN
Sellers AS Sellers
ON PE.PersonGuid = SE.PersonGuid
WHERE
SE.Guid = ''
then the LINQ becomes a little more obvious:
var person = (from p in loginContext.Person
join s in loginContext.Seller on p.PersonGuid equals s.PersonGuid
where s.guid == ""
select p)
.FirstOrDefault();
Although your SQL would suggest FirstOrDefault is not required.
Warning - not tested.
You could probably try using LINQ JOIN clause.
It should be something like:
from p in loginContext.Person
join s in loginContext.Seller on new { p.PersonGuid = s.Guid }
where s.Guid = sellerGuid
You can know more about LINQ JOIN here -> Microsoft C# Reference
Hope it helps!
I will assume that select PersonGuid from Sellers where Guid = '' will provide a single value no matter what, otherwise your SQL statement would fail.
Accepted answer works, but if you want a solution that keeps the spirit of the SQL query, you can rely on Any function. Something along the following:
var person = await loginContext.Person
.Where(p => loginContext.Sellers
.Any(s => s.PersonGuid == p.PersonGuid && s.Guid = ""))
.FirstOrDefaultAsync();
I expect this to be transformed into an SELECT ... WHERE EXISTS which is similar to your initial query.

Slick function for SQL exists

I want to construct an SQL along this but try not to use sqlu.
select el.oid, el.name, el.res_cat from el
left join bk on (el.cat = bk.cat and bk.oid=100)
where not exists (select 1 from dates bd where
el.oid=bd.lots_oid and bd.bk_oid = bk.oid) and el.e_oid=bk.e_oid
Are there Slick functions for SQL exists or not exists? Thanks
Update 1
I realized my mistakes when I revisited my Slick code again. I want to apologize for the false alarm I set. This is not an answer and hopefully someone can help me to rectify my mistakes. For the time being, I am using Slick's plain SQL to continue my work.
The Slick query I constructed didn't work. It was close to the SQL I wanted. What I did was,
val elQuery = elTable.joinLeft(bkTable)
.on((el, bk) => el.cat === bk.cat && bk.oid === 100)
val query = for {
a <- elQuery if bdTable.filterNot(bd => a._2.map(_.oid === bd.bkOid).isDefined && a._1.oid === bd.elOid).exists
} yield a
finalQuery.result.statements.foreach(x => Logger.debug(s"xx => $x"))
I notice filterNot does not generate a SQL not exists. This is the other portion that lost me.
I don't have enough reputation to make comment yet. But I assume that you want to get all rows that doesn't exit in dates table. I would rewrite your query like below:
SELECT
el.oid, el.name, el.res_cat.cat
FROM
el
LEFT JOIN bk ON bk.cat = el.cat
AND bk.e_oid = el.e_oid
AND bk.oid = 100
LEFT JOIN dates bd ON bd.lots_oid = el.oid
AND bd.bk_oid = bk.oid
WHERE
bd.lots_oid IS NULL
Explanation:
Instead of taking NOT EXISTS, you can achieve the same thing by LEFT JOIN dates and specify on WHERE condition that the primary key (PK) for dates IS NULL. I don't know the PK for dates table, so I just add the column I know. You should adjust it to the PK of dates table.
LEFT JOINing and WHERE PK IS NULL ensures you that the row doesn't exist on the left joined table.

Convert SQL with multiple join into LINQ

I would like to know how can i change the following sql statement into Linq or Lambda Extension in C#
SELECT m.mdes as AgeGroup,COUNT(DISTINCT(mbcd))as "No.of Member" FROM mageg m
LEFT JOIN (select distinct(mbcd) ,mage
FROMtevtl
JOIN mvipm
ON tevtl.mbcd = mvipm.mvip
WHERE datm >= '2014-04-01'
AND datm <= '2014-04-30'
)vip
ON m.tage >= vip.mage AND m.fage <= vip.mage
GROUP BY m.mdes
I manage to do the first half of the LINQ statement. Not sure If it is correct
here is the first half. I do not know how to connect with the left join.
(from mem in mvipms
from log in tevtls
from grp in magegs
where mem.mage >=grp.fage && mem.mage <=grp.tage && mem.mvip.Equals(log.mbcd)
&& log.datm >= DateTime.Parse("2014-04-01") && log.datm <= DateTime.Parse("2014-04-30")
select new {mem.mvip,grp.mdes}).Distinct()
Pls advice. I am using the MSSQL 2008 and VS2010.
Thanks a million.
I am no expert on LINQ, but since no-one else has answered, here goes!
Firstly you cannot (AFAIK) do a LINQ join on anything other than equals so the structure of your LEFT JOIN has to change. Partly for debugging purposes, but also for readability, I prefer to layout my LINQ in bite-size chunks, so what I would do in your case is something like this (assuming I have understood your data structure correctly):
var vip = (from t in tevtl
join v in mvipl
on t.mbcd equals v.mvip
where t.datm >= new DateTime(2014, 4, 1) && t.datm <= new DateTime(2014, 4, 30)
select new { t.mbcd, v.mage }).Distinct();
var mv = from m in magegl
from v in vip
where m.tage >= v.mage && m.fage <= v.mage
select new
{
m.mdes,
v.mbcd
};
var gmv = from m in mv
group m by new { m.mdes } into grp
select new
{
mdes = grp.Key.mdes,
CountMBCD = grp.Count()
};
var lj = from m in magegl
join v in gmv
on m.mdes equals v.mdes into lhs
from x in lhs.DefaultIfEmpty()
select new
{
AgeGroup = m.mdes,
CountMBCD = x != null ? x.CountMBCD : 0
};
By way of explanation. mv is the equivalent structure for your left join in that it has the relevant where clause, but obviously it does not contain any nulls - it is equivalent to an INNER JOIN. gmv is a group on mv, so is still effectively an INNER JOIN. Then to pick up the missing mdes (if any) I re-join on magel with the added syntax DefaultIfEmpty() - this converts the join into the equivalent of a LEFT OUTER JOIN.
Without any sample data, I haven't been able to test this, but I hope it gives you enough to point you in the right direction!

Entity Framework left outer join not working

I have an Entity Framework model with the following classes (I have simplified the classes for simpler viewing):
PuzzleItem
-PuzzleId (int, primary key)
-Title
PuzzleProgress
-ProgressId (int, primary key)
-PuzzleId (FK)
-UserId (FK)
In PuzzleItem I have a number of levels. The PuzzleProgress keeps track of which level the user is at by inserting a record of that level when the user completes a previous level. For a start a new user will have one entry in PuzzleProgress with PuzzleId = 1.
In my code, I am using the following statement to perform a left outer join, so that I will get a list of all puzzles, and indicating to me which puzzle has not been solved. I made reference to this post from StackOverflow.
Here is my code:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on pzs equals prg.PuzzleItem
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
where pzs_prg.UserId == userId
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};
After running the code, only the first level of this new user is returned (while the PuzzleItem table has 3 records).
I've tried playing around with the code but the one I pasted above is the nearest I can get to, can anybody point me in the correct direction? Thanks!
It's a bit hard to say exactly without seeing more of the code, but where pzs_prg.UserId == userId is probably negating the left outer join.
What I mean is, if you're intending PuzzleItems LEFT JOIN PuzzleProgress then you want all PuzzleItems even when there is no PuzzleProgress. But the where pzs_prg.UserId == userId means PuzzleProgress cannot be null, because it must have a UserId (of value userId). So, you effectively have an inner join.
Personally, I don't like the "correct" way of doing joins (left or inner) in linq, so this is how I would correct the linq statement:
var result = from pz in db.PuzzleItems
from pg in db.PuzzleProgresses
.Where(pg => pg.PuzzleId == pz.PuzzleId)
.Where(pg => pg.UserId == userId)
.DefaultIfEmpty()
select new
{
PuzzleId = pz.PuzzleId,
PuzzleName = pz.Title,
IsUnlocked = (pg != null)
};
This reads a lot more like SQL joins, which I learned a long time ago so it fits my thinking.
If you want to re-factor the join type syntax, look at this 'LINQ Joining in c# with multiple conditions'
I think the where clause is filtering out the records. You need to include the where clause in your left join. Like so:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on new { pzs.PuzzleId, UserId = userId } equals new { prg.PuzzleId, prg.UserId }
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};

Linq to SQL latest date

I have a linq to sql query
var items = from p in ctx.bam_Zending_AllInstances
join q in ctx.bam_Zending_CompletedRelationships
on p.ActivityID equals q.ActivityID
join r in ctx.bam_Prestatie_AllInstances
on q.ReferenceData equals r.ActivityID
where q.ReferenceType == "Activity" &&
p.Zendingnummer == zenderNr
orderby r.PrestatieCode
select new Data.BAMPrestatieInstance
{
Aanvaard = r.PrestatieAanvaard,
Contactnummer = r.ContactNr,
Foutmelding = r.Foutmelding,
Identificatie = r.Identificatie,
Ontvangen = r.PrestatieZendingOntvangen,
Uitvoerdatum = r.Uitvoerdatum,
ZendingsNr = p.Zendingnummer,
PrestatieCode = r.PrestatieCode
};
This returns a list of classes of multiple "r.PrestatieCode"s. "Uitvoerdatum" is a date however, and I need to only have the latest date for each r.PrestatieCode. How should I go about with this? Because I know how to do this in sql, but I can't seem to find the way to do it in linq.
Need this a lot atm actually,
any help is usefull,
thanks a bunch!
Gr
There is probably a more optimal way, but this will do what you want. With items defined as above:
var whatYouWant = items
.GroupBy(r => r.PrestatieCode)
.Select(
g => g.Single(
r => r.Uitvoerdatum == g.Max(s => s.Uitvoerdatum)
)
);
What this does: First it groups your result set by Data.BAMPrestatieInstance.PrestatieCode. Then, from each group with the same Data.BAMPrestatieInstance.PrestatieCode, it extracts the maximum Data.BAMPrestatieInstance.Uitvoerdatum and then finds the unique item with Data.BAMPrestatieInstance.Uitvoerdatum equal to that maximum date. If there is not a unique item with Data.BAMPrestatieInstance.Uitvoerdatum equal to that maximum date you can alter accordingly (use Where instead of Single, for example).
Let me know if I misunderstood your requirements.
If you know how to do it in SQL, you could write it like:
yourDataContext.ExecuteQuery<Data.BAMPrestatieInstance>(
"<sql query>");