#c# #vb.net #linq #linq-to-sql
#c# #vb.net #linq #linq-to-sql
Вопрос:
Пример на C # и VB.NET все в порядке.
У меня есть таблица «Люди» со следующими столбцами:
-Полное имя (nvarchar not null) -DOB (datetime null)
Я хочу написать LINQ для SQL, чтобы сгруппировать людей по возрасту, например, следующий результат:
Возраст 19: 4 человека
Возраст 20: 5 человек
Возраст 21: 6 человек
и так далее…
Вот моя попытка:
Dim query = From ppl In db.People _
Select New With {.Age = DateTime.Now.Year - CDate(ppl.DOB).Year, .CountAge = ppl.Count}
Обратите внимание, что для некоторых людей в таблицах нет записи DOB, поэтому они не должны включаться. В столбце DOB есть запись, подобная этой 1982-10-24 10:12:45, потому что это столбец даты и времени.
Комментарии:
1. «Обратите внимание, что для некоторых людей в таблицах есть запись DOB …» Вы пропустили слово в этом предложении?
2. Это должно быть «для некоторых людей нет записи DOB». Да, я не вводил слово «нет». Спасибо.
3. Для начала есть пара проблем:
.Age = DateTime.Now.Year - CDate(ppl.DOB).Year
большую часть времени будет указан неправильный возраст (учитывайте, где в году указана дата рождения). Кроме того, у вас там вообще нет агрегирования.
Ответ №1:
Более точное решение:
db.People.Where(p => p.DOB != null).GroupBy(p => ((DbFunctions.DiffDays(p.DOB, DateTime.Today) / 365)))
.Select(g => new {Age=g.Key, Count = g.Count()})
группировать по интервалам:
var interval = 5; //years
db.People.Where(p => p.DOB != null).GroupBy(p => ((DbFunctions.DiffDays(p.DOB, DateTime.Today) / 365) / interval))
.Select(g => new {GroupIndex=g.Key, Count = g.Count()})
Ответ №2:
Если вы используете EF Core 3.1, DbFunctions был заменен на EF.Функции. В качестве другой альтернативы вы могли бы использовать это:
var query = await db.People
.GroupBy(x.DateOfBirth.HasValue ? (int)(EF.Functions.DateDiffDay(x.DateOfBirth.Value, DateTime.UtcNow) / 365.25) : (int?)null)
.Select(x => new
{
Age = x.Key,
Count = x.Count(),
})
.ToListAsync(cancellationToken);
Он учитывает «день рождения», а не время, но является более точным, чем использование только «года рождения»
Ответ №3:
Я бы предпочел:
var result = db.People
.Where(p => p.DOB != null) // exclude null DOB's from stats
.GroupBy(p => p.DOB.Value.Year)
.Select(g => new { Age = DateTime.Now.Year - g.Key, Count = g.Count() });
Здесь мы группируем по году рождения. Это должно перевести GROUP BY YEAR(dob)
на язык SQL, который может иметь немного лучшую производительность или оптимизацию по сравнению с GROUP BY YEAR(GETDATE()) - YEAR(dob)
. Практически невозможно сопоставить выражения строк с индексами, но некоторые конструкции, такие как YEAR()
alone, могут быть оптимизированы для частичного использования индекса datetime. Здесь я делаю много предположений, например, что datetime
структуры начинаются с года, SQL server заботится об YEAR(x)
оптимизации и т. Д. По-прежнему полезно иметь в виду такие ситуации при построении запроса LINQ.
Ответ №4:
From ppl In db.People
Select New With {.Age = DateTime.Now.Year - CDate(ppl.DOB).Year,
.CountAge = ppl.Count()}
Group By (DateTime.Now.Year - CDate(ppl.DOB).Year)
Я думаю, что этот запрос послужит вашей цели.
Ответ №5:
Предполагая, что DOB является обнуляемым DateTime (DateTime?), Поэтому отсутствие записи DOB будет означать null там:
from ppl in db.People
where ppl.DOB.HasValue
let age = DateTime.Today.Year - ppl.DOB.Value.Year
group ppl by age into ages
select new { Age=ages.Key, Persons = ages }
Это на C #, но должно хорошо переводиться на VB, поскольку синтаксис похож.
Комментарии:
1. Если бы два человека родились в январе и декабре, разве они не были бы того же возраста, что и ваш запрос?
2. Действительно, это просто плохое приближение, но следуя логике, которую использует OP. Его вопрос касался группировки, поэтому я хотел оставить его таким, как у него 🙂
Ответ №6:
Это должно сработать (и должно вычислять возраст немного точнее):
var query = from person in db.People
where person.DOB.HasValue
let age = (DateTime.Now - username.UpdateDateTime.Value).Days % 365
group person by age into ages
select new { Age =ages.Key, People = ages.Count() }