Я действительно застрял на этом. У меня обширный опыт работы с SQL, но я только начал новую работу, и они предпочитают использовать LINQ для простых запросов. Итак, в духе обучения я попытался переписать этот простой SQL-запрос:
SELECT
AVG([Weight] / [Count]) AS [Average],
COUNT(*) AS [Count]
FROM [dbo].[Average Weight]
WHERE
[ID] = 187
Для ясности, вот схема таблицы:
CREATE TABLE [dbo].[Average Weight]
(
[ID] INT NOT NULL,
[Weight] DECIMAL(8, 4) NOT NULL,
[Count] INT NOT NULL,
[Date] DATETIME NOT NULL,
PRIMARY KEY([ID], [Date])
)
Вот что я придумал:
var averageWeight = Data.Context.AverageWeight
.Where(i => i.ID == 187)
.GroupBy(w => w.ID)
.Select(i => new { Average = i.Average(a => a.Weight / a.Count), Count = i.Count() });
Data.Context.AverageWeight - это объект Linq To SQL, созданный SQLMetal. Если я попытаюсь выполнить averageWeight.First ()
, я получу OverflowException. Я использовал SQL Profiler, чтобы посмотреть, как выглядит параметризованный запрос, созданный LINQ. С измененным отступом, который выглядит следующим образом:
EXEC sp_executesql N'
SELECT TOP(1)
[t2].[value] AS [Average],
[t2].[value2] AS [Count]
FROM (
SELECT
AVG([t1].[value]) AS [value],
COUNT(*) AS [value2]
FROM (
SELECT
[t0].[Weight] / (CONVERT(DECIMAL(29, 4), [t0].[Count])) AS
[value],
[t0].[ID]
FROM [dbo].[Average Weight] AS [t0]
) AS [t1]
WHERE
([t1].[ID] = @p0)
GROUP BY
[t1].[ID]
) AS [t2]',
N'@p0 int',
@p0 = 187
Не говоря уже о чрезмерном вложении, я вижу только одну проблему: DECIMAL (29, 4). (Запрос выполняется и дает ожидаемый результат.) Насколько я понимаю, все, что выше 28, приведет к переполнению десятичного типа данных C #. [Count] - это INT, поэтому его нужно преобразовать, но [Weight] - это DECIMAL (8, 4). Я понятия не имею, почему LINQ использует такой большой тип данных.
Зачем LINQ CONVERT к типу данных, который вызывает переполнение? Есть ли способ изменить это поведение? Или я даже на правильном пути?
Кроме того, Data.Context.AverageWeight был сгенерирован SqlMetal, и я подтвердил, что Weight является десятичным, а атрибут столбца верен (Decimal (8,4)).
Заранее спасибо .
Обновление: Таким образом, похоже, что виновником может быть LINQ to SQL. Я изменил свой LINQ следующим образом:
var averageWeight = Data.Context.AverageWeight
.Where(i => i.ID == 187)
.GroupBy(w => w.ID)
.Select(i => new { Average = i.Average(a => a.Weight) / (decimal)i.Average(a => a.Count), Count = i.Count() });
Теперь сгенерированный SQL выглядит так:
SELECT TOP(1)
[t2].[value] AS [Average],
[t2].[value2] AS [Count]
FROM (
SELECT
AVG([t1].[value]) AS [value],
COUNT(*) AS [value2]
FROM (
SELECT
[t0].[Weight] / (CONVERT(DECIMAL(16, 4), [t0].[Count])) AS [value],
[t0].[ID]
FROM [dbo].[Average Weight] AS [t0]
) AS [t1]
WHERE
([t1].[ID] = 187)
GROUP BY
[t1].[ID]
) AS [t2]
Результат:
Average Count
0.000518750000000 16
Предыдущий подход дал:
Average Count
0.000518750000000000000 16
Переполнения больше нет, но запрос меньше эффективный. Я не знаю, почему LINQ to SQL преобразуется в такую высокую точность. Ни одна из других переменных не так точна. И, насколько я могу судить, в LINQ я ничего не могу сделать, чтобы принудительно указать тип данных.
Есть идеи?