2014-05-15 24 views
9

Ich bin ein Subsystem von NHibernate-Entity Framework Portierung und wollen den besten Weg, um Port die folgende Abfrage EF zu sehen.Subquery mit Entity Framework

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(); 

Der Kontostand Klasse:

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; } 
} 

Der Tisch ist:

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 
    ) 
) 

Ein Beispieldaten (numerische IDs für ein besseres Verständnis verwenden):

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 

Die AccountBalanceByDate Entität den Kontostand an einem bestimmten Tag halten. Wenn ein Tag keine Transaktion hat, hat dieser Tag keine AccountBalanceByDate und wir sollten nach den vorherigen Tagen suchen, um den Kontostand für dieses Konto zu sehen.

Wenn ich mit dem Datum abfragen 2014.02.01 sollte ich:

No results 

Wenn ich mit dem Datum abfragen 2014.02.02 sollte ich:

1 | 2014-02-01 | 101  | 1390.00000 
2 | 2014-02-01 | 102  | 1360.00000 
3 | 2014-02-01 | 103  | 1630.00000 

Wenn ich mit dem Datum 2014.02.03 Abfrage soll ich:

1 | 2014-02-01 | 101  | 1390.00000 
4 | 2014-02-02 | 102  | 1370.00000 
5 | 2014-02-02 | 103  | 1700.00000 

Wenn ich mit dem Datum abfragen 2014.02.04 sollte ich:

4 | 2014-02-02 | 102  | 1370.00000 
6 | 2014-02-03 | 101  | 1490.00000 
7 | 2014-02-03 | 103  | 1760.00000 

Wenn ich mit dem Datum abfragen 2014.02.05 Ich sollte erhalten:

7 | 2014-02-03 | 103  | 1760.00000 
8 | 2014-02-04 | 101  | 1530.00000 
9 | 2014-02-04 | 102  | 1540.00000 

Ich kann dies in Entity Framework mit Raw SQL, aber es ist nicht das Ideal.

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

Es ist bevorzugt, nur ein einziges Mal in der Datenbank zu gehen, wie in NHibernate und roher SQL, aber mit nur Linq ist es möglich?

UPDATE:

Feste Ergebnisse in der Frage.

SQL die Beispielabfrage auf GIST zeigt: https://gist.github.com/LawfulHacker/275ec363070f2513b887

Entity Framework Probe auf GIST: https://gist.github.com/LawfulHacker/9f7bd31a21363ee0b646

+0

Ihre eigene SQL-Abfrage, die Sie in Ihrer Frage gab nicht die Ergebnisse produzieren! Abfrage für 2014-02-01 gibt nichts zurück! FIX deine Anfrage zuerst. –

+0

@AgentShark, bitte beachten Sie, dass ich eine Unterabfrage habe, die auf die oberste Tabelle verweist, und zwar für jede Zeile. Dann erhält es für jede Zeile seine Konto-ID und erhält das maximale Datum. Es gibt mir alle Ergebnisse im maximalen Datum jedes Kontos zurück, das maximale Datum kann für jedes Konto unterschiedlich sein. Es ist der Haken, meine Stichprobe ist korrekt, ich habe es erneut überprüft, als Sie darauf hingewiesen haben, dass es falsch war. – LawfulHacker

+0

Ich bekomme, was Sie versuchen, aber ich habe Ihre genauen Daten in SQL Server 2008 eingegeben und Ihre Abfrage ausgeführt. "2014-02-01" gibt nichts zurück. '2014-02-02' gibt Ids 3,2,1 zurück. "2014-02-03" zurück Ids 5,4,1. '2014-02-04' gibt Ids 7,4,6 ​​zurück. –

Antwort

18

Die folgende Abfrage tun genau das, was ich mit nur einer Anfrage an die Datenbank benötigen:

var accountBalance = context 
    .AccountBalanceByDate 
    .Where(a => 
     a.Date == context.AccountBalanceByDate 
      .Where(b => b.AccountId == a.AccountId && b.Date < date).Max(b => b.Date)); 

Dank @AgentShark für die Hilfe.

Der Code ist auf GIST: https://gist.github.com/LawfulHacker/9f7bd31a21363ee0b646

3

schließlich eine Lösung.:)

Sie wollten es in LINQ, aber persönlich, könnte ich von der SQL für Wartbarkeit behalten.

+0

Ich aktualisiere das gist-Beispiel mit Ihrer Lösung, bitte beachten Sie, dass es immer noch falsch ist. In der Tat schätze ich Ihre Hilfe sehr. – LawfulHacker

+0

@SergioGarcia Ich muss dir widersprechen, mein Herr, Ids kehrte von diesem LINQ Match IDs aus dem SQL zurück. Ich habe das Datum einfach fest programmiert. Ich habe dies in VS2013 anhand Ihrer Testdaten getestet. –

+0

Bitte überprüfen Sie den Kernpunkt. Aber ich habe eine Lösung gefunden. – LawfulHacker