サブシステムをNHibernateからEntity Frameworkに移植していて、次のクエリを[〜#〜] ef [〜#〜]。
var date = DateTime.Now; // It can be any day
AccountBalanceByDate abbd = null;
var lastBalanceDateByAccountQuery = QueryOver.Of<AccountBalanceByDate>()
.Where(x => x.AccountId == abbd.AccountId && x.Date < date)
.Select(Projections.Max<AccountBalanceByDate>(x => x.Date));
var lastBalances = session.QueryOver<AccountBalanceByDate>(() => abbd)
.WithSubquery.WhereProperty(x => x.Date).Eq(lastBalanceDateByAccountQuery)
.List();
勘定残高クラスは次のとおりです。
public class AccountBalanceByDate
{
public virtual int Id { get; set; }
public virtual int AccountId { get; set; }
public virtual DateTime Date { get; set; }
public virtual decimal Balance { get; set; }
}
テーブルは次のとおりです。
CREATE TABLE [dbo].[AccountBalanceByDate]
(
[Id] int NOT NULL,
[AccountId] int NOT NULL,
[Date] [datetime] NOT NULL,
[Balance] [decimal](19, 5) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)
)
サンプルデータは次のとおりです(理解を深めるために数値IDを使用しています)。
Id | Date | Account | Balance
------------------------------------
1 | 2014-02-01 | 101 | 1390.00000
2 | 2014-02-01 | 102 | 1360.00000
3 | 2014-02-01 | 103 | 1630.00000
4 | 2014-02-02 | 102 | 1370.00000
5 | 2014-02-02 | 103 | 1700.00000
6 | 2014-02-03 | 101 | 1490.00000
7 | 2014-02-03 | 103 | 1760.00000
8 | 2014-02-04 | 101 | 1530.00000
9 | 2014-02-04 | 102 | 1540.00000
AccountBalanceByDateエンティティは、特定の日の口座残高を保持します。日にトランザクションがない場合、その日にはAccountBalanceByDateがないため、前の日を探してそのアカウントの残高を確認する必要があります。
日付2014-02-01でクエリすると、次のようになります。
No results
日付2014-02-02でクエリすると、次のようになります。
1 | 2014-02-01 | 101 | 1390.00000
2 | 2014-02-01 | 102 | 1360.00000
3 | 2014-02-01 | 103 | 1630.00000
日付2014-02-03でクエリすると、次のようになります。
1 | 2014-02-01 | 101 | 1390.00000
4 | 2014-02-02 | 102 | 1370.00000
5 | 2014-02-02 | 103 | 1700.00000
日付2014-02-04でクエリすると、次のようになります。
4 | 2014-02-02 | 102 | 1370.00000
6 | 2014-02-03 | 101 | 1490.00000
7 | 2014-02-03 | 103 | 1760.00000
日付2014-02-05でクエリすると、次のようになります。
7 | 2014-02-03 | 103 | 1760.00000
8 | 2014-02-04 | 101 | 1530.00000
9 | 2014-02-04 | 102 | 1540.00000
Entity Frameworkで生のSQLを使用してこれを行うことができますが、それは理想的ではありません。
using (var context = new DbContext())
{
var lastBalances = context.AccountBalanceByDate.SqlQuery(
@"SELECT
*
FROM
[AccountBalanceByDate] AB
WHERE
DATE = (
SELECT
MAX(Date)
FROM
[AccountBalanceByDate]
WHERE
AccountId = AB.AccountId AND DATE < @p0
)", date).ToList();
}
NHibernateやraw SQLのように1回だけデータベースにアクセスすることをお勧めしますが、linqだけを使用することも可能です?
UPDATE:
質問の結果を修正しました。
Gistのサンプルクエリを示すSQL: https://Gist.github.com/LawfulHacker/275ec363070f2513b887
Gistのエンティティフレームワークサンプル: https://Gist.github.com/LawfulHacker/9f7bd31a21363ee0b646
次のクエリは、データベースに対するクエリを1つだけ実行することで、必要な処理を正確に実行します。
var accountBalance = context
.AccountBalanceByDate
.Where(a =>
a.Date == context.AccountBalanceByDate
.Where(b => b.AccountId == a.AccountId && b.Date < date).Max(b => b.Date));
助けてくれて@AgentSharkに感謝します。
コードはGistにあります: https://Gist.github.com/LawfulHacker/9f7bd31a21363ee0b646
最後に、ソリューション。 :)
var date = DateTime.Now; // It can be any day
var lastBalances = (from a in context.AccountBalanceByDate
where a.Date < date
group a by new {a.AccountId} into g
select g.OrderByDescending(a => a.Date).FirstOrDefault() into r
select new
{
Id = r.Id,
AccountId = r.AccountId,
Date = r.Date,
Balance = r.Balance
}).ToList();
あなたはLINQでそれを望んでいましたが、個人的には、保守性のためにSQLを保持したかもしれません。