Как сделать агрегированный запрос в linq - PullRequest
3 голосов
/ 06 апреля 2011

Я новичок в Linux и мне нужна помощь. У меня есть таблица транзакций с полем суммы, и я хочу разделить поле суммы на Дебет и Кредит. Что такое linq эквивалент SQL-запроса ниже: Выберите сумму (Сумма <0? Сумма: 0) как Дебет, сумму (Сумма> 0? 0: Сумма) как Кредит из транзакций, где Счет = strAccount.

Ответы [ 3 ]

10 голосов
/ 06 апреля 2011

К сожалению, нет простого способа выражения обоих агрегатов в одном запросе LINQ, подобном тому, когда он извлекает один результат. Два отдельных запроса будут:

// Doesn't actually perform a query yet
var accountTransactions = db.Transactions.Where(t => t.AccountId == accountId);

var credit = accountTransactions.Sum(t => t.Amount > 0 ? t.Amount : 0);
var debit = accountTransactions.Sum(t => t.Amount < 0 ? t.Amount : 0);

Или альтернативно:

var credit = accountTransactions.Sum(t => Math.Max(t.Amount, 0));
var debit = accountTransactions.Sum(t => Math.Min(t.Amount, 0));

Я не могу гарантировать, что они будут иметь переводы (скажем) в LINQ to SQL, но я надеюсь, что они будут.

Если вы находите кредит / дебет для всех учетных записей, вы могли бы сделать это в одном запросе:

var query = from account in db.Accounts
            join transaction in db.Transactions
              on account.AccountID equals transaction.TransactionID
              into trans
            select new { Account = account,
                         Credit = trans.Sum(t => Math.Max(t.Amount, 0)),
                         Debit = trans.Sum(t => Math.Min(t.Amount, 0)) };

Теперь, конечно, вы можете использовать:

var myTransactions = query.Where(result => result.Account.AccountId == accountID)
                          .FirstOrDefault();

Тогда будет одним оператором SQL, возвращающим либо один результат, либо ноль, если он не может найти идентификатор этой учетной записи. Опять же, вам нужно увидеть, как это на самом деле переведено в SQL.

1 голос
/ 06 апреля 2011

Вы могли бы сделать заявление вроде:

var query = from t in db.Transactions
            where t.Account == strAccount
            group t by t.Account into grouping
            select new
            {
                Debit = grouping.Sum(x => x.Amount < 0 ? x.Amount),
                Credit = grouping.Sum(x => x.Amount > 0 ? x.Amount),
            };

Это переводит в SQL как:

SELECT SUM(
    (CASE 
        WHEN [t0].[Amount] < @p1 THEN [t0].[Amount]
        ELSE @p2
     END)) AS [Debit], SUM(
    (CASE 
        WHEN [t0].[Amount] > @p3 THEN [t0].[Amount]
        ELSE @p4
     END)) AS [Credit]
FROM [Accounts] AS [t0]
WHERE [t0].[Account] = @p0
GROUP BY [t0].[Account]

Не совсем так же, как оригинал - вам нужно запустить анализатор запросовчтобы понять, было ли влияние GROUP BY значительным для вашей базы данных.

0 голосов
/ 06 апреля 2011
db.Transactions
    .Select(t => new { CR=Math.Min(0,t.Amount), DB=Math.Max(0,t.Amount) })
    .Aggregate(new { CR=0, DB=0 }, (s, t) => return new { CR=s.CR+t.CR, DB=s.DB+t.DB });

Преимущество состоит в том, что вы перебираете таблицу транзакций только один раз.Однако при этом создается много временных объектов, по одному для каждой транзакции.

Если вам необходимо сэкономить память, сделайте два отдельных прохода по транзакциям:

var cr = db.Transactions.Sum(t => Math.Min(0,t.Amount));
var db = db.Transactions.Sum(t => Math.Max(0,t.Amount));
...