I'm trying implement the follow query in LINQ, but I don't find solution:
SQL:
SELECT COUNT(*) AS AmountMonths
FROM (SELECT SUBSTRING(CONVERT(NVARCHAR(12), pay_date, 112), 1, 6) AS Month
FROM #tmp
GROUP BY SUBSTRING(CONVERT(NVARCHAR(12), pay_date, 112), 1, 6)) AS AmountMonths
What I need is get the amounts of months in which the clients made payments, with the condition that there may be months in which no payments have been made.
In C# I tried the following:
int amountMonths = payDetail.GroupBy(x => Convert.ToDateTime(x.PayDate)).Count();
and
int amountMonths = payDetail.GroupBy(x => Convert.ToDateTime(x.PayDate).Month).Count();
But I am not getting the expected result.
(Assuming you're using EF Core)
You're almost there. You could do:
var amountMonths = context.AmountMonths.GroupBy(c => new { c.PayDate.Year, c.PayDate.Month }).Count();
This will translate to something like:
SELECT COUNT(*)
FROM (
SELECT DATEPART(year, [a].[PayDate]) AS [a]
FROM [AmountMonths] AS [a]
GROUP BY DATEPART(year, [a].[PayDate]), DATEPART(month, [a].[Pay_Date])
) AS [t]
which I'd find preferable over creating a string and chopping it up. EOMONTH isn't a standard mapped function, alas, otherwise it can be used to convert a date to month level granularity
Related
In a SQL Server database, I have a table with log entries - timestamp and description.
I need to get logs occurrence in specified intervals (30minutes).
E.g.:
01.01.2015 00:00 - 100
01.01.2015 00:30 - 200
01.01.2015 01:00 - 2
and so on...
What I have:
var logs = session.Query<Log>;
//... other operations on logs queryable
var intervalInMinutes = 30;
var logsFrequency = logs
.GroupBy(l => new
{
Year = l.LogTimestamp.Year,
Month = l.LogTimestamp.Month,
Day = l.LogTimestamp.Day,
Hour = l.LogTimestamp.Hour,
Minute = (l.LogTimestamp.Minute / intervalInMinutes) * intervalInMinutes,
})
.Select(g => new LogsOccurence()
{
StartingDatetime = new DateTime(g.Key.Year, g.Key.Month, g.Key.Day, g.Key.Hour, g.Key.Minute, 0),
Frequency = g.Count()
})
.ToList();
Following query fails:
An exception of type 'NHibernate.Exceptions.GenericADOException' occurred in NHibernate.dll but was not handled in user code
Additional information: could not execute query
Inner exception: Column 'dbo.Log.LogTimestamp' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Generated select statement is:
select
datepart(year, Log0_.LogTimestamp) as col_0_0_
, datepart(month, Log0_.LogTimestamp) as col_1_0_
, datepart(day, Log0_.LogTimestamp) as col_2_0_
, datepart(hour, Log0_.LogTimestamp) as col_3_0_
, datepart(minute, Log0_.LogTimestamp) as col_4_0_
, cast(count(*) as INT) as col_5_0_
from
dbo.[Log] Log0_
group by
datepart(year, Log0_.LogTimestamp)
, datepart(month, Log0_.LogTimestamp)
, datepart(day, Log0_.LogTimestamp)
, datepart(hour, Log0_.LogTimestamp)
, datepart(minute, Log0_.LogTimestamp)/#p0*#p1
Name:p1 - Value:30 Name:p2 - Value:30
Operator precedence / order is wrong in groupby clause
datepart(minute, Log0_.LogTimestamp) / #p0 * #p1
Select part for minutes is different than group part for minutes - select fails
What is wrong with my Linq, and how to write correct one?
I am using NHibernate build 4.0.4.GA.
Operator precedence / order is wrong in groupby clause
The operator precedence looks correct to me. Multiplication and division have the same level of precedence, in both C# and T-SQL. Thus...
datepart(minute, Log0_.LogTimestamp) / #p0 * #p1
... is equivalent to...
(datepart(minute, Log0_.LogTimestamp) / #p0) * #p1
Select part for minutes is different than group part for minutes - select fails.
It is also different in the LINQ query. Since the GroupBy has...
Minute = (l.LogTimestamp.Minute / intervalInMinutes) * intervalInMinutes,
..., the Select should also have...
g.Key.Minute / intervalInMinutes * intervalInMinutes
That might possibly fix it.
I have a table that shows a list of sync's from our mobile users back to our database. This means that each user could have thousands of sync records.
I have written a query that uses the ROW_NUMBER() function to pull the most recent sync for every user and only active users, as I don't want to see sync'd data from terminated employees. (i.e. User A sync'd yesterday at noon, User A sync'd today at noon but I only want to see the sync from today).
SELECT * FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY [SerialNumber] ORDER BY SyncDate DESC )as RN
FROM [TSCH].[dbo].[SYNCREPORT]
) as T
Where RN = 1 and WorkerStatus = 'ACTIVE' and SerialNumber = ######;
What would the best approach for writing this using LINQ in c# for my .net web application? Thanks for the help!
Could be something like this
var result=yourtable.OrderBy(x=>x.SyncDate).GroupBy(x=>x.SerialNumber)
.Where(x=>x.WorkerStatus=="Active" && x.SerialNumber=="####")
.Select(g => new {g, count= g.Count()})
.SelectMany(t => t.g.Select(b => b)
.Zip(Enumerable.Range(1,t.count), (c,i) => new {c.value1, c,value2, rn = i}));
I have to make in c# a query with linq to sql. I can handle it in sql but in linq to
sql is the result not what I wanted to get.
So there is a table with:
a day, in datetime with date and time
and a kind of id
I have to count the ids for each date, the time isn't important. So the result
should be something like:
day: 2013-11-12 amountIDs: 4
People said to me, I can make a select new query and in this query I can set the day
and could count the ids, or I make a group by day. I read similar question, but it doesn't work in my case.
Could somebody help me?
I tried it with the statement below, but the days have to be grouped, so now the output is foreach datetime, like this
day: 12.12.2013 12:00:00 amountIDs: 1
day: 12.12.2013 12:10:10 amountIDs: 1
In sql I made this statement:
SELECT CONVERT(VARCHAR(20), data.dayandtime, 106) AS day, count(data.amountIds) as ids
FROM data
WHERE ( data.dayandtime >= DATEADD(day, -28, getdate()) AND (data.type = 100) AND (data.isSomething = 0) )
GROUP BY CONVERT(VARCHAR(20), data.dayandtime, 106), data.isSomthing and it works.
I saw similar cases, where people made a : from-select-new xyz statement, than I made a view of it and tried to group just the view. Like this
var query = data.GroupBy(g => g.day.Value).ToList();
var qry = from data in dbContext
group data by data.day into dataGrpd
select new
{
day= dataGrpd.Key,
amountIDs= dataGrpd.Select(x => x.Id).Distinct().Count()
};
Check This
I have an SQL query which I want to call from LINQ to SQL in asp.net application.
SELECT TOP 5 *
FROM (SELECT SongId,
DateInserted,
ROW_NUMBER()
OVER(
PARTITION BY SongId
ORDER BY DateInserted DESC) rn
FROM DownloadHistory) t
WHERE t.rn = 1
ORDER BY DateInserted DESC
I don't know whether its possible or not through linq to sql, if not then please provide any other way around.
I think you'd have to change the SQL partition to a Linq group-by. (Effectively all the partition does is group by song, and select the newest row for each group.) So something like this:
IEnumerable<DownloadHistory> top5Results = DownloadHistory
// group by SongId
.GroupBy(row => row.SongId)
// for each group, select the newest row
.Select(grp =>
grp.OrderByDescending(historyItem => historyItem.DateInserted)
.FirstOrDefault()
)
// get the newest 5 from the results of the newest-1-per-song partition
.OrderByDescending(historyItem => historyItem.DateInserted)
.Take(5);
Although McGarnagle answer solves the problem, but when i see the execution plan for the two queries, it was really amazing to see that linq to sql was really too slow as compare to native sql queries. See the generated query for the above linq to sql:
--It took 99% of the two execution
SELECT TOP (5) [t3].[SongId], [t3].[DateInserted]
FROM (
SELECT [t0].[SongId]
FROM [dbo].[DownloadHistory] AS [t0]
GROUP BY [t0].[SongId]
) AS [t1]
OUTER APPLY (
SELECT TOP (1) [t2].[SongId], [t2].[DateInserted]
FROM [dbo].[DownloadHistory] AS [t2]
WHERE [t1].[SongId] = [t2].[SongId]
ORDER BY [t2].[DateInserted] DESC
) AS [t3]
ORDER BY [t3].[DateInserted] DESC
--It took 1% of the two execution
SELECT TOP 5 t.SongId,t.DateInserted
FROM (SELECT SongId,
DateInserted,
ROW_NUMBER()
OVER(
PARTITION BY SongId
ORDER BY DateInserted DESC) rn
FROM DownloadHistory) t
WHERE t.rn = 1
ORDER BY DateInserted DESC
I've done a bit of research on this, and the best I've found so far is to use an Asenumerable on the whole dataset, so that the filtering occurs in linq to objects rather than on the DB. I'm using the latest EF.
My working (but very slow) code is:
var trendData =
from d in ExpenseItemsViewableDirect.AsEnumerable()
group d by new {Period = d.Er_Approved_Date.Year.ToString() + "-" + d.Er_Approved_Date.Month.ToString("00") } into g
select new
{
Period = g.Key.Period,
Total = g.Sum(x => x.Item_Amount),
AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2)
};
This gives me months in format YYYY-MM, along with the total amount and average amount. However it takes several minutes every time.
My other workaround is to do an update query in SQL so I have a YYYYMM field to group natively by. Changing the DB isn't an easy fix however so any suggestions would be appreciated.
The thread I found the above code idea (http://stackoverflow.com/questions/1059737/group-by-weeks-in-linq-to-entities) mentions 'waiting until .NET 4.0'. Is there anything recently introduced that helps in this situation?
The reason for poor performance is that the whole table is fetched into memory (AsEnumerable()). You can group then by Year and Month like this
var trendData =
(from d in ExpenseItemsViewableDirect
group d by new {
Year = d.Er_Approved_Date.Year,
Month = d.Er_Approved_Date.Month
} into g
select new
{
Year = g.Key.Year,
Month = g.Key.Month,
Total = g.Sum(x => x.Item_Amount),
AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2)
}
).AsEnumerable()
.Select(g=>new {
Period = g.Year + "-" + g.Month,
Total = g.Total,
AveragePerTrans = g.AveragePerTrans
});
edit
The original query, from my response, was trying to do a concatenation between an int and a string, which is not translatable by EF into SQL statements. I could use SqlFunctions class, but the query it gets kind ugly. So I added AsEnumerable() after the grouping is made, which means that EF will execute the group query on server, will get the year, month, etc, but the custom projection is made over objects (what follows after AsEnumerable()).
When it comes to group by month i prefer to do this task in this way:
var sqlMinDate = (DateTime) SqlDateTime.MinValue;
var trendData = ExpenseItemsViewableDirect
.GroupBy(x => SqlFunctions.DateAdd("month", SqlFunctions.DateDiff("month", sqlMinDate, x.Er_Approved_Date), sqlMinDate))
.Select(x => new
{
Period = g.Key // DateTime type
})
As it keeps datetime type in the grouping result.
Similarly to what cryss wrote, I am doing the following for EF. Note we have to use EntityFunctions to be able to call all DB providers supported by EF. SqlFunctions only works for SQLServer.
var sqlMinDate = (DateTime) SqlDateTime.MinValue;
(from x in ExpenseItemsViewableDirect
let month = EntityFunctions.AddMonths(sqlMinDate, EntityFunctions.DiffMonths(sqlMinDate, x.Er_Approved_Date))
group d by month
into g
select new
{
Period = g.Key,
Total = g.Sum(x => x.Item_Amount),
AveragePerTrans = Math.Round(g.Average(x => x.Item_Amount),2)
}).Dump();
A taste of generated SQL (from a similar schema):
-- Region Parameters
DECLARE #p__linq__0 DateTime2 = '1753-01-01 00:00:00.0000000'
DECLARE #p__linq__1 DateTime2 = '1753-01-01 00:00:00.0000000'
-- EndRegion
SELECT
1 AS [C1],
[GroupBy1].[K1] AS [C2],
[GroupBy1].[A1] AS [C3]
FROM ( SELECT
[Project1].[C1] AS [K1],
FROM ( SELECT
DATEADD (month, DATEDIFF (month, #p__linq__1, [Extent1].[CreationDate]), #p__linq__0) AS [C1]
FROM [YourTable] AS [Extent1]
) AS [Project1]
GROUP BY [Project1].[C1]
) AS [GroupBy1]