Converting SQL CASE WHEN statement plus group statement into LINQ - sql-server

How can I write in fluent linq syntax the "case when" sql statement?
select QueueItem, COUNT(*) as [Count],
SUM(CASE WHEN Queued = 0 THEN 1 ELSE 0 END) AS [Sent],
SUM(CASE WHEN Queued = 1 THEN 1 ELSE 0 END) AS Queued,
SUM(CASE WHEN Success = 1 THEN 1 ELSE 0 END) AS Exported,
SUM(CASE WHEN Success = 0 THEN 1 ELSE 0 END) AS Failed
from ExportQueue x
group by QueueItem
Is there some program that can convert SQL to LINQ? LinqPad maybe?

Ok, something like this. I'll need a bit of info to be sure though
Is Queued a bit? That makes a difference in linq where it doesn't in SQL.
I also don't know your context names but you should get the idea.
var query = Context.ExportQueues.Select(x => new {
QueueItem = x.QueueItem,
Sent = !x.Queued ? 1 : 0,
Queued = x.Queued ? 1 : 0,
Exported = x.Success ? 1 : 0,
Failed = !x.Success ? 1 : 0 })
.GroupBy(x => x.QueueItem)
.Select(g => new {
QueueItem = g.Key,
Sent = g.Sum(x => x.Sent),
Queued = g.Sum(x => x.Queued),
Exported = g.Sum(x => x.Exported),
Failed = g.Sum(x => x.Failed)
}).ToList();
EDIT You could also combine these by doing the case on the fly in the query. I always tend to write it out as above first as I work through it though as more complex aggregates can be a bit hard to debug if there's errors:
var query = Context.ExportQueues
.GroupBy(x => x.QueueItem)
.Select(g => new {
QueueItem = g.Key,
Sent = g.Sum(x => !x.Queued ? 1 : 0),
Queued = g.Sum(x => x.Queued ? 1 : 0),
Exported = g.Sum(x => x.Success ? 1 : 0),
Failed = g.Sum(x => !x.Success ? 1 : 0 )
}).ToList();

As an alternative to Gatts solution you can do something like
var query = Context.ExportQueues.
.GroupBy(x => x.QueueItem)
.Select(g => new {
QueueItem = g.Key,
Sent = g.Count(x=>!x.Queued),
Queued = g.Count(x => x.Queued),
Exported = g.Count(x => x.Success),
Failed = g.Count(x => !x.Failed)
}).ToList();

This is actually quite long-winded to write using LINQ. What you will need to do is to group first and then use lambda expressions to handle the aggregation. So something like:
from eq in ExportQueue
group eq by new {
eq.QueueItem
} into temp
select new {
temp.Key.QueueItem,
Agg1 = temp.Sum(n => n.Queued == 0 ? 1 : 0),
Agg2 = temp.Sum(n => n.Queued == 1 ? 1 : 0)
}
And so on, LinqPad will be really useful in trying to get this to work.

Related

Get multiple values from a function in yii2

I have a function in my Model which is passed a single value and in which I have a query which is giving me two results.
public static function updImgtbl($id)
{
$total_images = 0;
$images_uploaded = 0;
$sqlquery = "SELECT SUM(CASE WHEN ims.`installation_id` = $id THEN 1 ELSE 0 END) AS total_images,
SUM(CASE WHEN ims.`image_upload_flag` = 1 AND ims.`installation_id` = $id THEN 1 ELSE 0 END) AS images_uploaded
FROM `installation_images_site` ims
INNER JOIN `installations` ins ON ins.`id` = ims.`installation_id`
WHERE (ims.`image_upload_flag` = 1 AND ims.`installation_id` = $id)
OR (ims.`installation_id` = $id)
GROUP BY ims.`installation_id`";
$pt = Yii::$app->db->createCommand($sqlquery)->queryAll();
foreach ($pt as $val)
{
$total_images= $val['total_images'];
$images_uploaded = $val['images_uploaded'];
}
return ["total_images" => $total_images, "images_uploaded"=>$images_uploaded];
}
This function is called from an API
$response = Installations::updImgtbl($install_id);
print_r($response);
exit();
The response is below
Array
(
[total_images] => 4
[images_uploaded] => 1
)
But I want to get these values in a variable in my API. How can I achieve it?
You can simply:
list($total_images, $images_uploaded) = array_values($response);
Then, you have total_images value in $total_images variable and images_uploaded in $images_uploaded variable.
I mean:
list($total_images, $images_uploaded) = Installations::updImgtbl($install_id);
echo $total_images; // 4
echo $images_uploaded; // 1
You can read more about list in official doc.
Another way is using extract() function. For example:
$response = Installations::updImgtbl($install_id);
extract($response);
echo $total_images; // 4
echo $images_uploaded; // 1

Converting SQL Server query containing GROUP BY into NHibernate LINQ

I'm basically trying to retrieve a paged list of unique GUIDs, sorted by (row) creation date.
I've been able to draft a SQL Server query that seems to work for me based on this answer, but now I have to translate that into LINQ.
SELECT TOP 15 payment.ClientRef,
MAX(payment.CreatedDateUtc)
FROM PaymentTransactionState payment
INNER JOIN OrderState orderstate ON payment.ClientRef = orderstate.ClientRef
WHERE orderstate.UserId = 2 AND
payment.PaymentState IN (
'Rejected',
'Authorized')
GROUP BY payment.ClientRef
ORDER BY MAX(payment.CreatedDateUtc) DESC,
payment.ClientRef
Problem is, I can't apply GroupBy on an IQueryOver, I'm probably missing the appropiate syntax:
session
.QueryOver<Payment>()
.JoinAlias(orderState => orderState.OrderStateEntity, () => orderStateRow)
.Where(() => orderStateRow.UserId == customer.UserId)
.WhereRestrictionOn(payment => payment.PaymentState).IsIn(paymentStates)
.GroupBy(pts => pts.ClientRef)
.OrderBy(payment => payment.CreatedDateUtc).Desc
.Skip(pageIndex*pageSize)
.Take(pageSize)
.List();
I could probably do the group by in query syntax, but I'm not so sure about the Skip & Take bit.
I would try like this:
var query = db.PaymentTransactionState
.Where( pts => pts.OrderState.UserId == 2 &&
new string[] {"Rejected", "Authorized"}.Contains(pts.PaymentState) )
.GroupBy( pts => pts.ClientRef )
.OrderByDescending( pts => pts.Max( p => p.CreatedDateUtc))
.ThenBy( p => p.Key )
.Take(15);
So here's what worked for me: basically I had to use SelectList instead of GroupBy; SelectGroup, SelectMax & TransformUsing were easy to tackle once I found that;
PaymentRow paymentAlias = null;
OrderStateRow orderStateRow = null;
var transactionStateRows = session
.QueryOver<PaymentRow >()
.JoinAlias(orderState => orderState.OrderStateEntity, () => orderStateRow)
.Where(() => orderStateRow.UserId == customer.UserId)
.WhereRestrictionOn(payment => payment.PaymentState).IsIn(paymentStates)
.SelectList(list => list
.SelectGroup(payment => payment.ClientRef).WithAlias(() => paymentAlias.ClientRef)
.SelectMax(payment => payment.CreatedDateUtc).WithAlias(() => paymentAlias.CreatedDateUtc))
.TransformUsing(Transformers.AliasToBean<PaymentRow >())
.OrderBy(payment => payment.CreatedDateUtc).Desc
.Skip(pageIndex*pageSize)
.Take(pageSize)
.List();
I'll leave this here in case someone might find my travails useful in the future. Thank you for your replies.

How to Convert sql server query to linq in entity framework

I am converting the sql server query to linq
SELECT * FROM DataFlow where FlowDeleted = 0 and
DataFlow.FlowVersionNumber =
(
SELECT MAX(FlowVersionNumber) from DataFlow i
where FlowDeleted = 0 AND
i.FlowCounter = DataFlow.FlowCounter
group by FlowCounter
)
ORDER by 1
My Linq query Code is:
public List<DataFlow> getdataflow()
var dflow = db.DataFlows.Where (
d => d.FlowDeleted == false &&
d.FlowVersionNumber =
(
db.DataFlows.Where(i => i.FlowDeleted == false && i.FlowCounter == d.FlowCounter).GroupBy(g => g.FlowCounter)
.Select(s => s.Max(m => m.FlowVersionNumber))
)
)
.Select(s =>
new DataFlow
{
FlowCounter = s.FlowCounter,
FlowDescription = s.FlowDescription,
FlowName = s.FlowName,
}).OrderBy(o => o.FlowCounter);
return dflow.ToList();
}
But it giving error like: Operator '&&' cannot be applied to operands of type 'bool' ... 'string'"
Please help me
You are using assignment operator instead of comparing... try ..d.FlowVersionNumber ==...
db.DataFlows.Where(d =>
d.FlowDeleted == false &&
d.FlowVersionNumber == db.DataFlows.Where(i =>
i.FlowDeleted == false &&
i.FlowCounter == d.FlowCounter).GroupBy(g => g.FlowCounter)
.First(s => s.Max(m => m.FlowVersionNumber))
)
)
I changed Select to First because you need to compare scalars. However, be sure that you WILL find an item, or you could get an exception.

Query Slow in Linq, Fast in LinqPad, SQL Management Studio and SQL Profiler

I have this linq query i'm using and it's taking 50 seconds to run when i am running it my asp.net application, however the same query executes in 500ms in LinqPad and Sql Management Studio.
I even took the query from the SQL Profiler and ran it again in SQL Management Studio and it takes around 500ms. What overhead Linq could be doing, that an extra 49s??
Below is the code for reference, thanks for your help.
var rCampaign =
(from a in db.AdCreative
join h in db.AdHit on a.ID equals h.AdID into gh
join l in db.AdGroup_Location on a.AdGroupID equals l.AdGroupID into gj
from subloc in gj.DefaultIfEmpty()
from subhits in gh.DefaultIfEmpty()
where a.AdGroup.AdHost.Select(q => q.ID).Contains(rPlatform.ID) &&
a.AdGroup.AdPublisher.Select(q => q.ID).Contains(rPublisher.ID) &&
a.AdDimensionID == AdSize &&
a.AdGroup.Campaign.Starts <= rNow &&
a.AdGroup.Campaign.Ends >= rNow &&
subhits.HitType == 1 &&
(subloc == null || subloc.LocationID == rLocationID)
select new {
ID = a.ID,
Name = a.Name,
Spent = (subhits.AdDimension != null) ? ((double)subhits.AdDimension.Credit / 1000) : 0,
CampaignID = a.AdGroup.Campaign.ID,
CampaignName = a.AdGroup.Campaign.Name,
CampaignBudget = a.AdGroup.Campaign.DailyBudget
}).GroupBy(adgroup => adgroup.ID)
.Select(adgroup => new {
ID = adgroup.Key,
Name = adgroup.FirstOrDefault().Name,
Spent = adgroup.Sum(q => q.Spent),
CampaignID = adgroup.FirstOrDefault().CampaignID,
CampaignName = adgroup.FirstOrDefault().CampaignName,
CampaignBudget = adgroup.FirstOrDefault().CampaignBudget,
})
.GroupBy(q => q.CampaignID)
.Select(campaigngroup => new {
CampaignID = campaigngroup.Key,
DailyBudget = campaigngroup.FirstOrDefault().CampaignBudget,
Consumed = campaigngroup.Sum(q => q.Spent),
RemainningCredit = campaigngroup.FirstOrDefault().CampaignBudget - campaigngroup.Sum(q => q.Spent),
Ads = campaigngroup.Select(ag => new {
ID = ag.ID,
Name = ag.Name,
Spent = ag.Spent
}).OrderBy(q => q.Spent)
})
.Where(q => q.Consumed <= q.DailyBudget).OrderByDescending(q => q.RemainningCredit).First();
There are a few ways you can simplify that query:
select into lets you keep it all in query syntax.
The join ... into/from/DefaultIfMany constructs implementing left joins can be replaced with join ... into construcs representing group joins.
Some of the groups near the end cannot be empty, so FirstOrDefault is unnecessary.
Some of the where conditions can be moved up to the top before the query gets complicated.
Here's the stab I took at it. The revisions were significant, so it might need a little debugging:
var rCampaign = (
from a in db.AdCreative
where a.AdDimensionID == AdSize &&
a.AdGroup.Campaign.Starts <= rNow &&
a.AdGroup.Campaign.Ends >= rNow &&
a.AdGroup.AdHost.Select(q => q.ID).Contains(rPlatform.ID) &&
a.AdGroup.AdPublisher.Select(q => q.ID).Contains(rPublisher.ID)
join hit in db.AdHit.Where(h => h.HitType == 1 && h.LocationID == rLocationID)
on a.ID equals hit.AdID
into hits
join loc in db.AdGroup_Location
on a.AdGroupID equals loc.AdGroupID
into locs
where !locs.Any() || locs.Any(l => l.LocationID == rLocationID)
select new {
a.ID,
a.Name,
Spent = hits.Sum(h => h.AdDimension.Credit / 1000) ?? 0,
CampaignID = a.AdGroup.Campaign.ID,
CampaignName = a.AdGroup.Campaign.Name,
CampaignBudget = a.AdGroup.Campaign.DailyBudget,
} into adgroup
group adgroup by adgroup.CampaignID into campaigngroup
select new
{
CampaignID = campaigngroup.Key,
DailyBudget = campaigngroup.First().CampaignBudget,
Consumed = campaigngroup.Sum(q => q.Spent),
RemainingCredit = campaigngroup.First().CampaignBudget - campaigngroup.Sum(q => q.Spent),
Ads = campaigngroup.Select(ag => new {
ag.ID,
ag.Name,
ag.Spent,
}).OrderBy(q => q.Spent)
} into q
where q.Consumed <= q.DailyBudget
orderby q.RemainingCredit desc)
.First()
I refactored using Query syntax (Not sure if it improved readability). Removed one group by. Made some minor adjustments (replaced FirstOrDefault with Key property, changed Contains to Any). Hopefully it has some effect of speed.
var rCampaign = (from cg in
(from a in db.AdCreative
from subhits in db.AdHit.Where(h => a.ID == h.AdID)
.DefaultIfEmpty()
from subloc in db.AdGroup_Location.Where(l => a.AdGroupID == l.AdGroupID)
.DefaultIfEmpty()
where a.AdGroup.AdHost.Any(q => q.ID == rPlatform.ID) &&
a.AdGroup.AdPublisher.Any(q => q.ID == rPublisher.ID) &&
a.AdDimensionID == AdSize &&
a.AdGroup.Campaign.Starts <= rNow &&
a.AdGroup.Campaign.Ends >= rNow &&
subhits.HitType == 1 &&
(subloc == null || subloc.LocationID == rLocationID)
group new { a, subhits } by new { ID = a.ID, a.Name, CampaignID = a.AdGroup.Campaign.ID, CampaignName = a.AdGroup.Campaign.Name, CampaignBudget = a.AdGroup.Campaign.DailyBudget } into g
select new
{
ID = g.Key.ID,
Name = g.Key.Name,
Spent = g.Sum(x => (x.subhits.AdDimension != null) ? ((double)subhits.AdDimension.Credit / 1000) : 0),
CampaignID = g.Key.CampaignID,
CampaignName = g.Key.CampaignName,
CampaignBudget = g.Key.CampaignBudget
})
group cg by new { cg.CampaignID, cg.CampaignBudget } into cg
let tempConsumed = cg.Sum(q => q.Spent)
let tempRemainningCredit = cg.Key.CampaignBudget - tempConsumed
where tempConsumed <= cg.Key.CampaignBudget
orderby tempRemainningCredit desc
select new
{
CampaignID = cg.Key.CampaignID,
DailyBudget = cg.Key.CampaignBudget,
Consumed = tempConsumed,
RemainningCredit = tempRemainningCredit,
Ads = from ag in cg
orderby ag.Spent
select new
{
ID = ag.ID,
Name = ag.Name,
Spent = ag.Spent
}
}).First();

LINQ to Entities combine two IQueryable<AnonymousType>

I have 2 queries which work fine:
var q = (from c in _context.Wxlogs
where (SqlFunctions.DatePart("Month", c.LogDate2) == m3) && (SqlFunctions.DatePart("Year", c.LogDate2) == y1)
group c by c.LogDate2
into g
orderby g.Key
let maxTemp = g.Max(c => c.Temp)
let minTemp = g.Min(c => c.Temp)
let maxHum = g.Max(c => c.Humidity)
let minHum = g.Min(c => c.Humidity)
select new
{
LogDate = g.Key,
MaxTemp = maxTemp,
MaxTempTime = g.FirstOrDefault(c => c.Temp == maxTemp).LogTime,
MinTemp = minTemp,
MinTempTime = g.FirstOrDefault(c => c.Temp == minTemp).LogTime,
MaxHum = maxHum,
MaxHumTime = g.FirstOrDefault(c => c.Humidity == maxHum).LogTime,
MinHum = minHum,
MinHumTime = g.FirstOrDefault(c => c.Humidity == minHum).LogTime,
});
var r = (from c in _context.Wxlogs
where
(SqlFunctions.DatePart("Month", c.LogDate2) == m3) &&
(SqlFunctions.DatePart("Year", c.LogDate2) == y1)
group c by c.LogDate2
into g
orderby g.Key
let maxDew = g.Max(c => c.Dew_Point)
let minDew = g.Min(c => c.Dew_Point)
//let maxWind = g.Max(c=> c.Wind_Gust)
let maxRainRate = g.Max(c => c.Rain_rate_now)
let maxPres = g.Max(c => c.Barometer)
let minPres = g.Min(c => c.Barometer)
select new
{
LogDate = g.Key,
MaxRainRateTime = g.FirstOrDefault(c => c.Rain_rate_now == maxRainRate).LogTime,
MaxPres = maxPres,
MaxPresTime = g.FirstOrDefault(c => c.Barometer == maxPres).LogTime,
MinPres = minPres,
MinPresTime = g.FirstOrDefault(c => c.Barometer == minPres).LogTime,
MinDew = minDew,
MinDewTime = g.FirstOrDefault(c => c.Dew_Point == minDew).LogTime,
MaxDew = maxDew,
MaxDewTime = g.FirstOrDefault(c => c.Dew_Point == maxDew).LogTime,
MaxRainRate = maxRainRate,
});
however when I try to combine them using union, in order to output the results to a WPF datgrid:
var result = r.Union(q);
the following error is thrown on the union:
Error 1 Instance argument: cannot convert from 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Linq.ParallelQuery<AnonymousType#2>'
I can't seem to find a way to make this work and any help would be appreciated.
A "union" operation combines two sequences of the same type into a single set (i.e. eliminating all the duplicates). Since you clearly have sequences of two different types, you don't want a union operation. It looks like you want a "concat" operation, which just chains two sequences together. You need something like:
var result = r.Concat<object>(q);
However, since you're using L2E, your query will try to get executed on the server. Since your server won't allow you to combine your two queries (due to mismatched types), you need to execute them separately and then concat the sequences on the client:
var result = r.AsEnumerable().Concat<object>(q.AsEnumerable());
The use of AsEnumerable() runs the queries on the server and brings the results to the client.
Since it turns out that you want to combine the sequences next to each other (i.e. using the same rows in your grid but another group of columns), you actually want a join operation:
var result = from rrow in r.AsEnumerable()
join qrow in q.AsEnumerable() on rrow.LogDate equals qrow.LogDate
select new { rrow.LogDate,
rrow.MaxTemp, rrow.MaxTempTime,
rrow.MinTemp, rrow.MinTempTime,
rrow.MaxHum, rrow.MaxHumTime,
rrow.MinHum, rrow.MinHumTime,
qrow.MaxRainRate, qrow.MaxRainRateTime,
qrow.MaxPres, qrow.MaxPresTime,
qrow.MinPres, qrow.MinPresTime,
qrow.MaxDew, qrow.MaxDewTime,
qrow.MinDew, qrow.MinDewTime };

Resources