Need to convert this query to linq or lambda - linq

This query pulls back two columns a userid column and a column of their total sales for the current month.
I need to add a column that ranks them (it would be the exact same as the row #)
And I need to convert this from SQL to LINQ or Lambda.
select pu.userID, SUM(o.OrderTotal) as OrderTotal from ProPit_User pu
inner join SeperateDB.dbo.orders o on pu.salesrepid = o.salesrepid and o.DateCompleted > '2014-05-01' and o.DateCompleted < '2014-05-23'
group by pu.userID
order by SUM(o.OrderTotal) desc
Currently returns:
userID OrderTotal
340 68992.74
318 49575.05
228 42470.88
278 38196.87
291 36220.52
351 34962.32
422 31764.67
178 31433.41
430 30836.77
212 30375.99

To capture the ranking index using SQL, you can simply modify your current sql as such:
SELECT pu.userid,
SUM(o.OrderTotal) AS OrderTotal,
ROW_NUMBER() OVER (ORDER BY SUM(o.ordertotal) DESC) AS [Rank]
FROM ProPit_User pu
INNER JOIN SeperateDB.dbo.Orders o ON pu.salesrepid = o.salesrepid
AND o.DateCompleted > '2014-05-01' AND o.DateCompleted < '2014-05-23'
GROUP BY pu.userid
ORDER BY SUM(o.OrderTotal) DESC
which will yield
userID OrderTotal Rank
340 68992.74 1
318 49575.05 2
228 42470.88 3
etc
Not sure in which context you want to 'convert to LINQ'. If you mean a linq query against these two tables within an EF or Linq-to-Sql context, then these statements will yield the same results as above:
var minDate = new DateTime(2014,5,1);
var maxDate = new DateTime(2014,5,23);
-- the linq to sql query:
-- join on salesrepid, group by userid, sum the ordertotals
var dbQuery = ProPit_Users.GroupJoin(
Orders,
pu => pu.salesrepid,
o => o.salesrepid,
(pu, orders) => new
{
pu.UserId,
OrderTotal = orders.Where(o => o.datecompleted > minDate && o.datecompleted. < maxDate )
.Sum(o => o.ordertotal)
}
)
.OrderByDescending(row => row.OrderTotal)
-- materialize the db query
.ToList();
-- add ranking to the results of the query
var userRankings = dbQuery.Select((row, idx) => new { Rank = ++idx, row.UserId, row.OrderTotal });
which will yield a list of objects:
Rank UserID OrderTotal
1 340 68992.74
2 318 49575.05
3 228 42470.88
etc

Related

Using Linq, how would one join two tables without a foreign key relationship between the two tables?

So let's say we have these two tables:
CARS
ID CAR_MODEL
11 Mustang
22 Camaro
33 F-150
PARTS
ID CAR_ID PART_NAME
1 11 Steering Wheel
2 22 Steering Wheel
3 22 Headlights
NOTE: there is no foreign key relationship in the database between the PARTS.CAR_ID and CARS.ID columns.
Without the foreign key, what sort of linq query would be used to obtain these results:
CARS
ID CAR_MODEL PART_NAME_LIST
11 Mustang Steering Wheel
22 Camaro Steering Wheel, Headlights
33 F-150 (null)
Environment: SQL Server 2014, linq-to-sql
edit: here is what I have so far, but this results in 4 rows in the results (2 for Camaro), instead of 3, and it does not include any parts.
from C in db.CARS
join P in db.PARTS on C.ID equals P.CAR_ID
select new{
ID = C.ID,
CAR_MODEL = C.CAR_MODEL,
PART_NAME_LIST = ???
}
Here is a fluent syntax group join:
var sub = (from C in db.CARS
join P in db.PARTS on C.ID equals P.CAR_ID into Pj
select new {
C.ID,
C.CAR_MODEL,
PART_NAMES = from p in Pj select p.PART_NAME
})
.AsEnumerable();
var ans = from icp in sub
select new {
icp.ID,
icp.CAR_MODEL,
PART_NAME_LIST = String.Join(", ", icp.PART_NAMES)
I would probably switch to lambda syntax in the select in my code:
var ans = (from C in db.CARS
join P in db.PARTS on C.ID equals P.CAR_ID into Pj
select new {
C.ID,
C.CAR_MODEL,
PART_NAMES = Pj.Select(cpj => cpj.PART_NAME)
})
.AsEnumerable()
.Select(icp => new {
icp.ID,
icp.CAR_MODEL,
PART_NAME_LIST = String.Join(", ", icp.PART_NAMES)
});
For concatenating strings you have to use string.Join. The easiest way to do that is in a subquery:
(
from c in db.CARS
select new
{
ID = c.ID,
CAR_MODEL = c.CAR_MODEL,
PART_NAME_LIST = db.PARTS
.Where(p => c.ID equals p.CAR_ID)
.Select(p => p.PART_NAME)
}
).AsEnumerable()
.Select(c => new
{
ID = c.ID,
CAR_MODEL = c.CAR_MODEL,
PART_NAME_LIST = string.Join(", ", cPART_NAME_LIST)
})
Entity Framework doesn't support string.Join, so you have to split the query in a part that EF can translate into SQL and a part that's executed in memory, separated by AsEnumerable().

How to use LINQ Group By with Count

I want to convert the following SQL Query into Entity Framework + LINQ query. There are three tables Brands, Products and ProductReviews. Products table has BrandId as FK and ProductReviews has ProductId as FK.
SELECT Top 5 b.Id, b.ShortName, COUNT(r.Id) AS TotalReviews
FROM ProductsReviews r
INNER JOIN Products p ON r.ProductId = p.Id
INNER JOIN Brands b ON p.BrandId = b.Id
GROUP BY b.Id, b.ShortName
Order By TotalReviews desc
Basically, I want to display top 5 brands based on the reviews posted for the products of those brands. I want output like below:
Id ShortName TotalReviews
-----------------------------------------
76 Adidas 61
120 Yamaha 29
109 Tommy Hilfiger 26
61 Mothercare 25
31 Haier 22
pseudocode
var results =
( from r in ProductsReviews
join p in Products on r.ProductId equals p.Id
join b in Brands on p.BrandId equals b.Id
group c by new {
b.Id,
b.ShortName } into grp
select new {
Id = grp.key.Id,
ShortName = grp.key.ShortName,
TotalReviews = grp.Count()}
)
.OrderBy(x=>x.TotalReviews).Take(5);
It really depends on how your model is setup with EF.
If you have added relations in your model, the query could be as simple as
var result = context.ProductReviews.OrderByDescending(x => x.TotalReviews).Select(x => new { x.BrandId, X.Brand.ShortName, x.TotalReviews});
Because you are not selecting anything from Product, I am not including it in the query. If you have relationships in your EF, we would be able to use navigation properties such as x.Brand.Someproperty, and EF will handle creating the query based upon the model that you have setup.
the result variable will contain the query and once you access the values, the query will be executed. Lastly to only select the top 5 you would simply use the Take function like so
var result = context.ProductReviews.OrderByDescending(x => x.TotalReviews).Select(x => new { x.BrandId, X.Brand.ShortName, x.TotalReviews}).Take(5);
Regards
Louis

LINQ distinct on 2 fields

I have a LINQ query that returns data and I want to filter based on 2 fields (fkProfileID and fkOrgID) that are often the same. I only want to show 1 record when fkProfileID and fkOrgID match, so it is similar to doing an SQL distinct, but on 2 fields rather than one.
My data will look like this (other fields have been removed) :
fkProfileID fkOrgID
1 1001
1 1001
1 1001
2 1001
2 1001
1 1005
1 1005
So here I want to return only the following:
fkProfileID fkOrgID
1 1001
2 1001
1 1005
Here is my current LINQ (both fields above are in tblUserRights), how do I need to change it to do this?
List<ProfileJSON> lstProfiles = (from r in _database.tblUserRights
join p in _database.LuProfiles on r.fkProfileID equals p.luProfileID
join o in _database.tblOrganisations on r.fkOrgID equals o.pkOrgID
where r.fkUniqueID == intPKUserID
orderby o.OrgDesc, p.ProfileName
select new ProfileJSON
{
SiteID = o.pkOrgID,
SiteName = o.OrgDesc,
ProfileID = p.luProfileID,
ProfileName = p.ProfileName
}).ToList();
You can use .Distinct() after your query
List<ProfileJSON> lstProfiles = (from r in _database.tblUserRights
join p in _database.LuProfiles on r.fkProfileID equals p.luProfileID
join o in _database.tblOrganisations on r.fkOrgID equals o.pkOrgID
where r.fkUniqueID == intPKUserID
orderby o.OrgDesc, p.ProfileName
select new ProfileJSON
{
SiteID = o.pkOrgID,
SiteName = o.OrgDesc,
ProfileID = p.luProfileID,
ProfileName = p.ProfileName
}).Distinct().ToList(); //Distinct here

LINQ count query returns a 1 instead of a 0

I have the following view:-
CREATE VIEW tbl_adjudicator_result_view
AS
SELECT a.adjudicator_id, sar.section_adjudicator_role_id, s.section_id, sdr.section_dance_role_id, d.dance_id, c.contact_id,
ro.round_id, r.result_id, c.title, c.first_name, c.last_name, d.name, r.value, ro.type
FROM tbl_adjudicator a
INNER JOIN tbl_section_adjudicator_role sar on sar.section_adjudicator_role2adjudicator = a.adjudicator_id
INNER JOIN tbl_section s on sar.section_adjudicator_role2section = s.section_id
INNER JOIN tbl_section_dance_role sdr on sdr.section_dance_role2section = s.section_id
INNER JOIN tbl_dance d on sdr.section_dance_role2dance = d.dance_id
INNER JOIN tbl_contact c on a.adjudicator2contact = c.contact_id
INNER JOIN tbl_round ro on ro.round2section = s.section_id
LEFT OUTER JOIN tbl_result r on r.result2adjudicator = a.adjudicator_id AND r.result2dance = d.dance_id
When I run the following query directly against the db I get 0 in the count column where there is no result
select adjudicator_id, first_name, COUNT(result_id)
from tbl_adjudicator_result_view arv
where arv.round_id = 16
group by adjudicator_id, first_name
However when I use LINQ query I always get 1 in the Count Column
var query = from arv in db.AdjudicatorResultViews
where arv.round_id == id
group arv by new { arv.adjudicator_id, arv.first_name} into grp
select new AdjudicatorResultViewGroupedByDance
{
AdjudicatorId = grp.Key.adjudicator_id,
FirstName = grp.Key.first_name,
Count = grp.Select(p => p.result_id).Distinct().Count()
};
What do I need to change in the View / Linq query.
You're not doing the same thing in the LINQ query as in the SQL. COUNT(result_id) does not count distinct values of result_id - it counts non-null values.
Try this instead:
Count = grp.Select(p => p.result_id).Where(x => x != null).Count()
The point is: you're grouping your data in the LINQ query - and you'll always get at least one group.
That group's Count may be 0 - but the count of groups will be 1.

How to get the last element by date of each "type" in LINQ or TSQL

Imagine to have a table defined as
CREATE TABLE [dbo].[Price](
[ID] [int] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Price] [int] NOT NULL
)
where ID is the identifier of an action having a certain Price. This price can be updated if necessary by adding a new line with the same ID, different Price, and a more recent date.
So with a set of a data like
ID StartDate Price
1 01/01/2009 10
1 01/01/2010 20
2 01/01/2009 10
2 01/01/2010 20
How to obtain a set like the following?
1 01/01/2010 20
2 01/01/2010 20
In SQL, there are several ways to say it. Here's one that uses a subquery:
SELECT *
FROM Price p
WHERE NOT EXISTS (
SELECT *
FROM Price
WHERE ID = p.ID
AND StartDate > p.StartDate
)
This translates fairly trivially to LINQ:
var q = from p in ctx.Price
where !(from pp in ctx.Price
where pp.ID == p.ID
&& pp.StartDate > p.StartDate
select pp
).Any()
select p;
Or should I say, I think it does. I'm not in front VS right now, so I can't verify that this is correct, or that LINQ will be able to convert it to SQL.
Minor quibble: Don't use the name ID to store a non-unique value (the type, in this case). It's confusing.
Assuming ID & StartDate will be unique:
SELECT p.ID, p.StartDate, p.Price
FROM Price p
JOIN
(
SELECT ID, MAX(StartDate) AS LatestDate
FROM Price
GROUP BY ID
) p2 ON p.ID = p2.ID AND p.StartDate = p2.LatestDate
Since you tagged your question with LINQ to SQL, here is an LINQ query to express what you want:
from price in db.Prices
group price by price.Id into group
let maxDateInGroup = group.Max(g => g.StartDate)
let maxDatePrice = group.First(g => g.StartDate == maxDateInGroup)
select
{
Id = group.Key,
StartDate = maxDatePrice.StartDate,
Price = maxDatePrice.Price
};

Resources