2016-08-01 32 views
1

I wie unten einige historische Daten aus Firebird Datenbank am Zug:Firebird Gruppe von Zeit

Product_ID  Date  Price 
1   2001-01-01 10 
1   2001-02-01 10 
1   2001-03-01 15 
1   2001-04-01 10 
1   2001-05-01 20 
1   2001-06-01 20 

Was ich bin versucht, die erste für das Auftreten jeder Preisänderung zu extrahieren. Beispiel erwarteten Datensatz:

Product_ID  Date  Price 
1   2001-01-01 10 
1   2001-03-01 15 
1   2001-04-01 10 
1   2001-05-01 20 

Ich weiß, dass auf MSSQL ich LAG dafür nutzen könnte. Ist das möglich mit Firebird?

+0

Sie können problemlos eine gespeicherte Prozedur verwenden. Gehe durch alle Datensätze ('FOR SELECT ..... INTO ... ORDER BY .....') und wenn Price change, rufe 'SUSPEND' an. –

+0

Oder Sie können dies versuchen (nicht getestet - haben keine Firebird-Instanz hier): 'SELECT Produkt_ID, MIN (Datum), Preis von xxx GROUP BY Produkt_ID, Datum, Preis' –

+0

Das würde nur erste Instanz des gleichen Preises abrufen, aber es würde nicht die 2. Preisänderung abrufen (10 am 04-01) – Ninjago

Antwort

0

in MoreLinq gibt es eine Lag-Extension-Methode, aber es ist nur in Linq to Objects unterstützt ...

Was können Sie tun, wenn Sie, dass für eine C# Linq Antwort suchen können Sie:

Im Grunde ordnen Sie Ihre Daten den richtigen Weg und fügen dann einen Zeilenindex hinzu, während der Preis (und die Produkt-ID) immer noch gleich ist. Dann gruppieren Sie sich danach und wählen Sie das min Datum.

int groupingIndex = 0; 
int previousPrice = 0; 

var response = data 
    .OrderBy(item => item.Product_ID) 
    .ThenBy(item => item.Date) 
    .Select(item => 
    { 
     if (item.Price != previousPrice) 
     { 
      previousPrice = item.Price; 
      groupingIndex++; 
     } 

     return new { Index = groupingIndex, Item = item }; 
    }) 
    .GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price }) 
    .Select(group => new Record 
    { 
     Product_ID = group.Key.Product_ID, 
     Price = group.Key.Price, 
     Date = group.Min(item => item.Item.Date) 
    }).ToList(); 

Und wenn Sie nichts dagegen haben den Betrieb in der C# und nicht die DB zu tun (und eine Beta-Version des MoreLinq verwenden) dann:

int index = 0; 

var result2 = data 
    .OrderBy(item => item.Product_ID) 
    .ThenBy(item => item.Date) 
    .Lag(1, (current, previous) => new { Index = (current.Price == previous?.Price ? index : ++index), Item = current }) 
    .GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price }) 
    .Select(group => new Record { Product_ID = group.Key.Product_ID, Price = group.Key.Price, Date = group.Min(item => item.Item.Date) }) 
    .ToList(); 
0

du versuchen können, aber sein bewusst habe ich es nicht getestet:

CREATE PROCEDURE SP_Test 
RETURNS (
    Product_ID INTEGER, 
    Date DATE, 
    Price INTEGER 
) 
AS 
DECLARE VARIABLE Last_Product_ID INTEGER; 
DECLARE VARIABLE Last_Date DATE; 
DECLARE VARIABLE Last_Price INTEGER; 
BEGIN 
    FOR SELECT Product_ID, Date, Price 
     FROM xxxx 
     ORDER BY Product_ID, Date 
     INTO Product_ID, Date, Price 
    DO BEGIN 
    IF ((:Last_Product_ID IS NULL) OR 
     (:Last_Date IS NULL) OR 
     (:Last_Price IS NULL) OR 
     (:Product_ID <> :Last_Product_ID) OR 
     (:Price <> :Last_Price)) THEN 
     SUSPEND; 
    Last_Product_ID = :Product_ID; 
    Last_Date = :Date; 
    Last_Price = :Price; 
    END;  
END; 
+0

oder man kann EXECTE BLOCK anstelle eines persistenten SP verwenden –

0

Dies ist ein wenig kompliziert, aber es funktioniert

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data; 

namespace ConsoleApplication6 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      DataTable dt = new DataTable(); 
      dt.Columns.Add("Product_ID", typeof(int)); 
      dt.Columns.Add("Date", typeof(DateTime)); 
      dt.Columns.Add("Price", typeof(int)); 

      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-01-01"), 10}); 
      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-02-01"), 10}); 
      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-03-01"), 15}); 
      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-04-01"), 10}); 
      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-05-01"), 20}); 
      dt.Rows.Add(new object[] {1, DateTime.Parse("2001-06-01"), 20}); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-01-01"), 10 }); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-02-01"), 10 }); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-03-01"), 15 }); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-04-01"), 10 }); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-05-01"), 20 }); 
      dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-06-01"), 20 }); 

      dt = dt.AsEnumerable().OrderBy(x => x.Field<DateTime>("Date")).CopyToDataTable(); 
      List<DataRow> results = dt.AsEnumerable() 
       .GroupBy(g => g.Field<int>("Product_ID")) 
       .Select(g1 => g1.Select((x, i) => new { row = x, dup = (i == 0) || ((i > 0) && (g1.Skip(i - 1).FirstOrDefault().Field<int>("Price") != g1.Skip(i).FirstOrDefault().Field<int>("Price"))) ? false : true }) 
       .Where(y => y.dup == false).Select(z => z.row)).SelectMany(m => m).ToList(); 

     } 
    } 
} 
+0

Danke. Wie berücksichtige ich auch die Product_ID? – Ninjago

+0

Es ist nicht schön, aber ich habe den Code aktualisiert. – jdweng