LINQ to SQL группирует людей по возрасту

#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() }