How to write this SQL average query in linq - sql-server

Here's the SQL I would like run:
DECLARE #StartDt DATETIME, #EndDt DATETIME
SET #StartDt='2009-01-01'
SET #EndDt='2010-06-01'
SELECT
AVG(DATEDIFF(day, CreatedDt, CompletedDt))
AVG(DATEDIFF(day, CreatedDt, ComplianceDt))
FROM MyTable
WHERE RequestDt BETWEEN #StartDt AND #EndDt
Can this be expressed in Linq (C#) and have it all run on the database?

DateTime startDt = new DateTime(2009, 1, 1);
DateTime endDt = new DateTme(2010, 6, 1);
var query = dc.MyTables
.Where(x => startDt <= x.RequestDate && x.RequestDt <= endDt)
.GroupBy(x => 1) //unsure about this group by
.Select(g => new
{
FirstAvg = g.Average(x =>
SqlMethods.DateDiffDay(x.CreatedDt, x.CompletedDt)),
SecondAvg = g.Average(x =>
SqlMethods.DateDiffDay(x.CreatedDt, x.ComplianceDt))
});
var row = query.Single();

Related

Select multiple rows in order based on list of Ids with LINQ

I have a list of ids. How can I select all rows whose id is in the list and preserve the order?
This is what I have:
var ids = new int[]{5, 1, 8, 2, 3};
var items = Db.Items.Where(x => ids.Contains(x.Id));
But it doesn't preserve the order.
I know I can sort items based on ids, but I want O(n) complexity.
This question is similar to: Select multiple records based on list of Id's with linq but I need to keep the rows in order
I'm not into queries and databases that much but you can do this quite quickly with just doing C# tricks
var ids = new int[]{5, 1, 8, 2, 3};
var dict = ids.Select((id, i) => new { Index = i, Id = id })
.ToDictionary(x => x.Id, x => x.Index);
var items = Db.Items
.Where(x => ids.Contains(x.Id))
.OrderBy(x => dict[x.Id]);
I can't tell how it will get translated into a database query
I haven't tested it, but here'e a version without OrderBy, but less space-efficient (it might actually be slower):
var ids = new int[]{5, 1, 8, 2, 3};
var temp = Db.Items
.Where(x => ids.Contains(x.Id))
.ToLookup(x => x.Id);
var tempList = new List<IGrouping<int, Item>>();
for(int i = 0; i < ids.Length; i++)
{
tempList.Add(temp[ids[i]]);
}
var items = tempList.SelectMany(x => x);
There is also another way - simply do a reverse join:
var ids = new int[]{5, 1, 8, 2, 3};
var items = from id in ids
join item in Db.Items
on id equals item.Id
select item;
This will result in a query sorted by ids
What about projecting into an intermediate type to preserve the original index, building a union, and sorting over the indexes?
class ItemWithIndex
{
public int Index { get; set; }
public Item Item { get; set; }
}
class Item
{
public int Id { get; set; }
}
int[] ids = { 5, 1, 8, 2, 3 };
IQueryable<ItemWithIndex> query = null;
for(int index = 0; index < ids.Length; index++)
{
int currentIndex = index;
int currentId = ids[index];
IQueryable<ItemWithIndex> next = db.Items
.Where(i => i.Id == currentId)
.Select(i => new ItemWithIndex { Index = currentIndex, Item = i });
query = query == null ? next : query.Concat(next);
}
ItemWithIndex[] items = query
.OrderBy(i => i.Index)
.ToArray();
Here is the generated query:
SELECT
[UnionAll4].[Id] AS [C1],
[UnionAll4].[C1] AS [C2],
[UnionAll4].[Id1] AS [C3]
FROM (SELECT
[Extent1].[Id] AS [Id],
#p__linq__1 AS [C1],
[Extent1].[Id] AS [Id1]
FROM [dbo].[Items] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0
UNION ALL
SELECT
[Extent2].[Id] AS [Id],
#p__linq__3 AS [C1],
[Extent2].[Id] AS [Id1]
FROM [dbo].[Items] AS [Extent2]
WHERE [Extent2].[Id] = #p__linq__2
UNION ALL
SELECT
[Extent3].[Id] AS [Id],
#p__linq__5 AS [C1],
[Extent3].[Id] AS [Id1]
FROM [dbo].[Items] AS [Extent3]
WHERE [Extent3].[Id] = #p__linq__4
UNION ALL
SELECT
[Extent4].[Id] AS [Id],
#p__linq__7 AS [C1],
[Extent4].[Id] AS [Id1]
FROM [dbo].[Items] AS [Extent4]
WHERE [Extent4].[Id] = #p__linq__6
UNION ALL
SELECT
[Extent5].[Id] AS [Id],
#p__linq__9 AS [C1],
[Extent5].[Id] AS [Id1]
FROM [dbo].[Items] AS [Extent5]
WHERE [Extent5].[Id] = #p__linq__8) AS [UnionAll4]
I'd say that,
1st, you can get the items as you did before:
var ids = new int[]{5, 1, 8, 2, 3};
var items = Db.Items.Where(x => ids.Contains(x.Id));
and then you could do something like:
var orderedItems = new int[ids.Length()] // sorry, I'm codign in SO edit, not sure on the syntax
foreach(id in items)
{
var position = Array.IndexOf(items, id)
orderedITems[position] = id;
}
That should do what you asked (also could be simplified in a single line).
I hope it helps,
Juan
This is a possible solution:
var ids = new int[] {5, 1, 8, 2, 3};
var items = new List<Item>();
for (int i = 0; i < ids.Length; i++)
{
items.Add(Db.Items.Find(ids[i]));
}
However, it performs N queries, so there should be a better way.

Incorrect output from stored procedure

I am using below stored procedure to generate a crystal report.My report mainly filters data based on two possible values, Arcade or Franchise.
I filter data as Arcade = 1 , Franchise = 2 and Both = 0.The OutletType parameter get these int values.
When I filter for one particular value It gives me both arcade and franchise values.
ALTER PROCEDURE [dbo].[PrintReceiptCancellationWorkflow]
#EntryType int,
#OutletType int,
#RequestedUser varchar(50),
#FromDate DateTime2,
#ToDate DateTime2,
#OutletCode varchar(10),
#CancelledUser varchar(20),
#Status int
AS
BEGIN
SELECT
Outlets.OutletDesc AS 'Branch',
Receipt.CancelUser AS 'RequestedBy',
Receipt.RecDate AS 'ReqDateTime',
--ReceiptCancellationStatus.ApprovedStatus AS 'Status',
ReceiptCancellationStatus.StatusDesc As Status,
WorkflowRequestQueue.CposReference AS 'WCRNo',
Receipt.ReceiptNo AS 'ReceiptNo',
Receipt.PaymentMode AS 'PaymentMode',
Receipt.AppliedAmount AS 'Amount',
WorkflowRequestQueue.AppRejUser AS 'CompletedBy',
WorkflowRequestQueue.AppRejDateTime AS 'CompletedDateTime',
EntryTypes.EntryType AS 'PaymentCategory',
WorkflowRequestQueue.AppRejComment AS 'Comment',
OutletCategories.CatDesc As 'OutletType'
FROM Receipt
INNER JOIN
Outlets ON Receipt.OutletCode = Outlets.OutletCode
LEFT OUTER JOIN
EntryTypes ON Receipt.EntryTypeId = EntryTypes.Id
LEFT OUTER JOIN
WorkflowRequestQueue ON Receipt.CposReference = WorkflowRequestQueue.CposReference
LEFT OUTER JOIN
OutletCategories ON Outlets.OutletType = OutletCategories.CatCode
LEFT OUTER JOIN
ReceiptCancellationStatus ON WorkflowRequestQueue.ApprovedStatus = ReceiptCancellationStatus.Id
WHERE (Outlets.OutletType = #OutletType OR Outlets.OutletType = 0) OR
(Receipt.EntryTypeId = #EntryType OR Receipt.EntryTypeId = 0) OR
Receipt.CancelUser = #RequestedUser OR
(( CONVERT(DATE,WorkflowRequestQueue.AppRejDateTime) >= #FromDate) AND ( CONVERT(DATE,WorkflowRequestQueue.AppRejDateTime) <= #ToDate)) OR
Outlets.OutletCode = #OutletCode OR
WorkflowRequestQueue.ApprovedPerson = #CancelledUser OR
(WorkflowRequestQueue.ApprovedStatus = #Status OR WorkflowRequestQueue.ApprovedStatus = 0)
END
Outlet type drop down values,
$scope.VendorDropdown = [
{ "text": "Select", "value": "0" },
{ "text": "Arcade", "value": "1" },
{ "text": "Franchise", "value": "2" },
];
Report generation code,
object paymentCategory = Convert.ToInt32(Request.Form["PaymentCategory"]);
object vendor = Convert.ToInt32(Request.Form["Vendor"]);
object requestedUser = Convert.ToString(Request.Form["RequestedUser"]);
object cancelledUser = Convert.ToString(Request.Form["CancelledUser"]);
object Fromdate = Convert.ToDateTime(Request.Form["dateFrom"]);
object Todate = Convert.ToDateTime(Request.Form["dateTo"]);
object status = Convert.ToInt32(Request.Form["Status"]);
object outlet = Convert.ToString(Request.Form["Outlet"]);
if (isExcel)
{
myReport.Load(Server.MapPath("~/CPOSReport/MIS/CancellationReports/ReceiptCancellationWorkFlow.rpt"));
}
else {
myReport.Load(Server.MapPath("~/CPOSReport/MIS/CancellationReports/ReceiptCancellationWorkFlow.rpt"));
}
myReport.SetParameterValue("#EntryType", paymentCategory);
myReport.SetParameterValue("#OutletType",vendor );
myReport.SetParameterValue("#RequestedUser", requestedUser);
myReport.SetParameterValue("#CancelledUser", cancelledUser);
myReport.SetParameterValue("#FromDate", Fromdate);
myReport.SetParameterValue("#ToDate", Todate);
myReport.SetParameterValue("#Status", status);
myReport.SetParameterValue("#OutletCode", outlet);
All of your where conditions are OR'ed. This means that if one of them are satisfied, the row will be included.
You should probably try this WHERE in stead with the different conditions AND'ed together:
WHERE (Outlets.OutletType = #OutletType OR Outlets.OutletType = 0) AND
(Receipt.EntryTypeId = #EntryType OR Receipt.EntryTypeId = 0) AND
Receipt.CancelUser = #RequestedUser AND
(
( CONVERT(DATE,WorkflowRequestQueue.AppRejDateTime) >= #FromDate) AND
( CONVERT(DATE,WorkflowRequestQueue.AppRejDateTime) <= #ToDate)
) AND
Outlets.OutletCode = #OutletCode AND
WorkflowRequestQueue.ApprovedPerson = #CancelledUser AND
(WorkflowRequestQueue.ApprovedStatus = #Status OR WorkflowRequestQueue.ApprovedStatus = 0)

Need to convert sql statement into Entity Framework equivalent

Given the following tables:
InstrumentLogs
InstrumentLogId (PK, int, not null)
InstrumentId (FK, int, not null)
LogDate (datetime, not null)
Action (string, not null)
Instruments
InstrumentId (PK, int, not null)
CountyId (FK, int, not null)
Counties
CountyId (PK, int, not null)
StateFips (FK, int, not null)
Name (string, not null)
States
StateFips (PK, int, not null)
Name (string, not null)
I need to figure out how to write this SQL query using Entity Framework:
select s.StateName, count(*) as total
from instruments as i
join counties as c on i.CountyID = c.CountyID
join states as s on s.StateFIPS = c.StateFIPS
where i.InstrumentID in
(select i1.InstrumentId from InstrumentLogs as i1 where i1.action = 'review' and
i1.logdate in (select max(logdate) from instrumentlogs as i2 where i1.instrumentid
=i2.InstrumentID group by i2.instrumentid))
group by s.StateName
I tried something along the lines of:
_context.Instruments.Include(i => i.County.State)
.Where(i => _context.Logs.Where(l => l.Action == 'review'
&& _context.Logs.Where(l2 => l2.InstrumentId == l.InstrumentId).Max(l2 => l2.LogDate) == l.LogDate).GroupBy(i => i.County.State.Name)
.Select(g => new { State = g.Key.Name, Total = g.Count() });
However, EF doesn't like this. I wind up with the error stating that only primitive types or enumeration types are supported.
Thanks for your help.
I finally got it to work like this
var _query = _dataContext.IndexLogs.GroupBy(l => l.InstrumentId, l => l)
.Select(grp => new { Id = grp.Key, Date = grp.Max(g => g.ActionDate) });
//Prequery to find log records that match given type and action type
var _indexes = _dataContext.IndexLogs.Where(l => l.Action == type
&& _query.Contains(new { Id = l.InstrumentId, Date = l.ActionDate})).Select(i => i.InstrumentId);
var _states = _dataContext.AdvancedIndexes
.Include(i => i.County.State)
.Where(a => a.Id > 0 && _indexes.Contains(a.Id))
.GroupBy(i => new { i.County.State.Id, i.County.State.Name })
.Select(g => new { fips = g.Key.Id, name = g.Key.Name, count = g.Count() });

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();

How to do this SQL in LINQ

Morning i would like to know how to do this bit of (MS) SQL in LINQ...
SELECT CONVERT(CHAR(10),orderDate,110) AS OrderDate,
SUM(1) AS TotalOrders
FROM Orders
WHERE OrderDate>getdate()-30
GROUP BY CONVERT(CHAR(10),orderDate,110)
ORDER BY OrderDate DESC
Many thanks in advance.
UPDATE
I ended using and edited version of the solutions provided below, thought i would share it...
using (DataDataContext dc = new DataDataContext())
{
var query = from o in dc.Orders
where o.orderDate > DateTime.Now.AddDays(-30)
let dt = o.orderDate
group o by new DateTime(dt.Year, dt.Month, dt.Day) into g
select new OrderCounts
{
OrderDate = String.Format("{0:d}", g.Key.Date),
TotalOrders = g.Count()
};
query.GroupBy(o => o.OrderDate);
query.OrderBy(o => o.OrderDate);
return query.ToList();
}
var result =(from s in Orders
where s.OrderDate > DateTime.Now.AddDays(-30)
group s by new { date = s.OrderDate.ToString() } into g
// or use ((DateTime)s.OrderDate).ToShortDateString() instead of s.OrderDate.ToString()
select new
{
OrderDate = g.Key.date,
TotalOrders = g.Count()
}).OrderByDescending(x=>x.OrderDate);;
The query will be
var query = from o in res
where o.OrderDate > DateTime.Now.AddDays(-30)
orderby o.OrderDate descending
group o by o.OrderDate into g
select new
{
OrderDate = g.Key.Date.ToString("MM/dd/yyyy")
,
TotalOrders = g.Sum(i=> 1)
};
OR by Lambda expression
var query= res
.Where(i => i.OrderDate > DateTime.Now.AddDays(-30))
.OrderByDescending(o => o.OrderDate)
.GroupBy(g => g.OrderDate)
.Select(s => new { OrderDate = s.Key.Date.ToString("MM/dd/yyyy"), TotalOrders = s.Sum(i => 1) });

Resources