Wie erhalte ich effizient die aktuellste entsprechende Zeile?

53

Ich habe ein Abfragemuster, das sehr häufig vorkommt, aber ich weiß nicht, wie ich eine effiziente Abfrage dafür schreiben soll. Ich möchte die Zeilen einer Tabelle nachschlagen, die "dem letzten Datum nicht nach" den Zeilen einer anderen Tabelle entsprechen.

Ich habe zum Beispiel einen Tisch, inventoryder das Inventar darstellt, das ich an einem bestimmten Tag habe.

date       | good | quantity
------------------------------
2013-08-09 | egg  | 5
2013-08-09 | pear | 7
2013-08-02 | egg  | 1
2013-08-02 | pear | 2

und eine Tabelle, "Preis" sagen, die den Preis eines Gutes an einem bestimmten Tag hält

date       | good | price
--------------------------
2013-08-07 | egg  | 120
2013-08-06 | pear | 200
2013-08-01 | egg  | 110
2013-07-30 | pear | 220

Wie kann ich effizient den "neuesten" Preis für jede Zeile der Inventartabelle ermitteln, d. H

date       | pricing date | good | quantity | price
----------------------------------------------------
2013-08-09 | 2013-08-07   | egg  | 5        | 120
2013-08-09 | 2013-08-06   | pear | 7        | 200
2013-08-02 | 2013-08-01   | egg  | 1        | 110
2013-08-02 | 2013-07-30   | pear | 2        | 220

Ich kenne einen Weg, dies zu tun:

select inventory.date, max(price.date) as pricing_date, good
from inventory, price
where inventory.date >= price.date
and inventory.good = price.good
group by inventory.date, good

Fügen Sie diese Abfrage dann erneut zum Inventar hinzu. Bei großen Tabellen ist sogar die erste Abfrage (ohne erneut eine Verbindung zum Inventar herzustellen) sehr langsam. Dasselbe Problem lässt sich jedoch schnell lösen, wenn ich einfach mit meiner Programmiersprache eine max(price.date) ... where price.date <= date_of_interest ... order by price.date desc limit 1Abfrage für jede Abfrage date_of_interestaus der Inventartabelle stelle, sodass ich weiß, dass es keine rechnerischen Hindernisse gibt. Ich würde es jedoch vorziehen, das gesamte Problem mit einer einzigen SQL-Abfrage zu lösen, da ich damit das Ergebnis der Abfrage in SQL weiterverarbeiten könnte.

Gibt es eine Standardmethode, um dies effizient zu tun? Es fühlt sich so an, als müsste es oft auftauchen und es sollte eine Möglichkeit geben, eine schnelle Abfrage dafür zu schreiben.

Ich verwende Postgres, aber eine SQL-generische Antwort wäre wünschenswert.

Tom Ellis
quelle
3
Zur Migration auf DBA.SE gewählt, da es sich um eine Effizienzfrage handelt. Wir könnten die Abfrage auf verschiedene Arten schreiben, aber das wird sie nicht viel schneller machen.
ypercubeᵀᴹ
5
Benötigen Sie eigentlich alle Waren für alle Tage aus einer Anfrage? Scheint eine unwahrscheinliche Anforderung? Üblicherweise würde man Preise für ein bestimmtes Datum oder die Preise für ein bestimmtes Gut (zu einem bestimmten Datum) abrufen. Diese alternativen Abfragen könnten viel leichter von (geeigneten) Indizes profitieren. Wir müssen auch wissen: Kardinalitäten (wie viele Zeilen in jeder Tabelle?), Die vollständige Tabellendefinition inkl. Datentypen, Einschränkungen, Indizes, ... (Verwendung \d tblin psql), Ihre Version von Postgres und min. / max. Anzahl der Preise pro Ware.
Erwin Brandstetter
@ErwinBrandstetter Bitten Sie mich, eine Antwort zu akzeptieren? Ich bin nicht wirklich qualifiziert zu wissen, welches das Beste ist, aber da deines die meisten positiven Stimmen hat, bin ich glücklich, es zu akzeptieren.
Tom Ellis
Akzeptiere nur, wenn es deine Frage beantwortet oder für dich funktioniert. Sie können sogar einen Kommentar hinterlassen, wie Sie vorgegangen sind, wenn dies in verwandten Fällen hilfreich sein könnte. Wenn Sie der Meinung sind, dass Ihre Frage unbeantwortet bleibt, lassen Sie es uns wissen.
Erwin Brandstetter
1
Ich muss mich dann entschuldigen, denn obwohl ich scheinbar hervorragende Antworten erhalten habe, arbeite ich nicht mehr an dem Problem, das die Frage provoziert hat, und bin daher nicht in der Lage, zu beurteilen, welche die beste Antwort ist oder ob überhaupt eine von ihnen sind wirklich für meinen Anwendungsfall geeignet (wie es war). Wenn es eine DBA.Stackexchange-Etikette gibt, die ich in diesem Fall befolgen sollte, lassen Sie es mich bitte wissen.
Tom Ellis

Antworten:

42

Es kommt sehr auf die Umstände und die genauen Anforderungen an. Betrachten Sie meinen Kommentar zur Frage .

Einfache lösung

Mit DISTINCT ONin Postgres:

SELECT DISTINCT ON (i.good, i.the_date)
       i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM   inventory  i
LEFT   JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER  BY i.good, i.the_date, p.the_date DESC;

Geordnetes Ergebnis.

Oder mit NOT EXISTSStandard-SQL (funktioniert mit jedem mir bekannten RDBMS):

SELECT i.the_date, p.the_date AS pricing_date, i.good, i.quantity, p.price
FROM   inventory  i
LEFT   JOIN price p ON p.good = i.good AND p.the_date <= i.the_date
WHERE  NOT EXISTS (
   SELECT 1 FROM price p1
   WHERE  p1.good = p.good
   AND p1.the_date <= i.the_date
   AND p1.the_date >  p.the_date
   );

Gleiches Ergebnis, aber mit beliebiger Sortierreihenfolge - außer Sie fügen hinzu ORDER BY.
Abhängig von der Datenverteilung, den genauen Anforderungen und den Indizes kann einer davon schneller sein.
Im Allgemeinen DISTINCT ONist der Sieger und Sie erhalten ein sortiertes Ergebnis darüber. In bestimmten Fällen sind andere Abfragetechniken jedoch (viel) schneller. Siehe unten.

Lösungen mit Unterabfragen zur Berechnung von Max / Min-Werten sind im Allgemeinen langsamer. Varianten mit CTEs sind im Allgemeinen noch langsamer.

Einfache Ansichten (wie in einer anderen Antwort vorgeschlagen) tragen in Postgres überhaupt nicht zur Leistung bei.

SQL-Geige.


Richtige Lösung

Zeichenfolgen und Kollatierung

Zuallererst leiden Sie unter einem suboptimalen Tabellenlayout. Es mag trivial erscheinen, aber die Normalisierung Ihres Schemas kann sehr weit gehen.

Sortierung nach Zeichentypen ( text, varchar, ...) werden muss , erfolgt nach dem locale - der COLLATION im Besonderen. Höchstwahrscheinlich verwendet Ihre Datenbank einige lokale Regeln (wie in meinem Fall:) de_AT.UTF-8. Finden Sie es heraus mit:

SHOW lc_collate;

Dadurch werden das Sortieren und die Indexsuche verlangsamt . Je länger Ihre Saiten (Warennamen) sind, desto schlechter. Wenn Sie die Kollatierungsregeln in Ihrer Ausgabe (oder die Sortierreihenfolge überhaupt) nicht beachten, kann dies schneller sein, wenn Sie Folgendes hinzufügen COLLATE "C":

SELECT DISTINCT ON (i.good COLLATE "C", i.the_date)
       i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM   inventory  i
LEFT   JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER  BY i.good COLLATE "C", i.the_date, p.the_date DESC;

Beachten Sie, wie ich die Kollatierung an zwei Stellen hinzugefügt habe.
In meinem Test doppelt so schnell mit jeweils 20.000 Zeilen und sehr einfachen Namen ('good123').

Index

Wenn Ihre Abfrage einen Index verwenden soll, müssen Spalten mit Zeichendaten eine übereinstimmende Sortierung verwenden ( goodim Beispiel):

CREATE INDEX inventory_good_date_desc_collate_c_idx
ON price(good COLLATE "C", the_date DESC);

Lesen Sie unbedingt die letzten beiden Kapitel dieser Antwort zu SO:

Sie können sogar mehrere Indizes mit unterschiedlichen Sortierungen in denselben Spalten haben - wenn Sie in anderen Abfragen auch Waren benötigen, die nach einer anderen (oder der Standard-) Sortierung sortiert sind.

Normalisieren

Redundante Zeichenfolgen (name of good) belasten auch Ihre Tabellen und Indizes, wodurch alles noch langsamer wird. Mit einem korrekten Tabellenlayout könnten Sie die meisten Probleme zunächst vermeiden. Könnte so aussehen:

CREATE TABLE good (
  good_id serial PRIMARY KEY
, good    text   NOT NULL
);

CREATE TABLE inventory (
  good_id  int  REFERENCES good (good_id)
, the_date date NOT NULL
, quantity int  NOT NULL
, PRIMARY KEY(good_id, the_date)
);

CREATE TABLE price (
  good_id  int     REFERENCES good (good_id)
, the_date date    NOT NULL
, price    numeric NOT NULL
, PRIMARY KEY(good_id, the_date));

Die Primärschlüssel liefern automatisch (fast) alle benötigten Indizes.
Je nach fehlenden Details, einen mehrspaltigen Index auf pricemit absteigender Reihenfolge auf der zweiten Spalte kann die Leistung verbessern:

CREATE INDEX price_good_date_desc_idx ON price(good, the_date DESC);

Auch hier muss die Sortierung mit Ihrer Suchanfrage übereinstimmen (siehe oben).

In Postgres 9.2 oder höher kann das "Abdecken von Indizes" für Index-Only-Scans noch weiter helfen - insbesondere, wenn Ihre Tabellen zusätzliche Spalten enthalten und die Tabelle somit wesentlich größer als der abdeckende Index ist.

Diese resultierenden Abfragen sind viel schneller:

EXISTIERT NICHT

SELECT i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM   inventory  i
JOIN   good       g USING (good_id)
LEFT   JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
AND    NOT EXISTS (
   SELECT 1 FROM price p1
   WHERE  p1.good_id = p.good_id
   AND    p1.the_date <= i.the_date
   AND    p1.the_date >  p.the_date
   );

DISTINCT ON

SELECT DISTINCT ON (i.the_date)
       i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM   inventory  i
JOIN   good       g USING (good_id)
LEFT   JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
ORDER  BY i.the_date, p.the_date DESC;

SQL-Geige.


Schnellere Lösungen

Wenn das immer noch nicht schnell genug ist, kann es schnellere Lösungen geben.

Rekursiver CTE / JOIN LATERAL/ korrelierte Unterabfrage

Speziell für Datenverteilungen mit vielen Preisen pro Ware :

Materialisierte Ansicht

Wenn Sie dies häufig und schnell ausführen müssen, sollten Sie eine materialisierte Ansicht erstellen. Ich denke, es ist davon auszugehen, dass sich Preise und Lagerbestände für vergangene Daten selten ändern. Berechnen Sie das Ergebnis einmal und speichern Sie einen Schnappschuss als materialisierte Ansicht.

Postgres 9.3+ bietet automatisierte Unterstützung für materialisierte Ansichten. Sie können eine Basisversion problemlos in älteren Versionen implementieren.

Erwin Brandstetter
quelle
3
Der von price_good_date_desc_idxIhnen empfohlene Index hat die Leistung für eine ähnliche Abfrage von mir erheblich verbessert. Mein Abfrageplan ging von einem Preis von 42374.01..42374.86bis zu 0.00..37.12!
Cimmanon
@cimmanon: Schön! Was ist Ihre zentrale Abfragefunktion? EXISTIERT NICHT? AUSZEICHNEN? GRUPPIERE NACH?
Erwin Brandstetter
Verwenden von DISTINCT ON
Cimmanon
6

Zu Ihrer Information, ich habe mssql 2008 verwendet, sodass Postgres nicht über den Index "include" verfügt. Die Verwendung der unten gezeigten grundlegenden Indizierung ändert sich jedoch von Hash-Joins zu Merge-Joins in Postgres: http://explain.depesz.com/s/eF6 (kein Index) http://explain.depesz.com/s/j9x ( mit Index auf Join-Kriterien)

Ich schlage vor, Ihre Anfrage in zwei Teile zu unterteilen. Erstens eine Ansicht (die nicht dazu gedacht ist, die Leistung zu verbessern) , die in einer Vielzahl anderer Kontexte verwendet werden kann und die die Beziehung zwischen Inventurdaten und Preisdaten darstellt.

create view mostrecent_pricing_dates_per_good as
select i.good,i.date i_date,max(p.date)p_date
  from inventory i
  join price p on i.good = p.good and i.date >= p.date
 group by i.good,i.date;

Dann kann Ihre Anfrage für andere Arten einfacher und einfacher zu bearbeiten sein (z. B. durch Verwendung von Links-Joins, um Inventar ohne aktuelle Preisdaten zu finden):

select i.good
       ,i.date inventory_date
       ,i.quantity
       ,p.date pricing_date
       ,p.price       
  from inventory i
  join price p on i.good = p.good
  join mostrecent_pricing_dates_per_good x 
    on i.good = x.good 
   and p.date = x.p_date
   and i.date = x.i_date

Dies ergibt den folgenden Ausführungsplan: http://sqlfiddle.com/#!3/24f23/1 keine Indizierung

... Alle Scans mit einer vollständigen Sortierung. Beachten Sie, dass die Performancekosten von Hash-Matches einen Großteil der Gesamtkosten ausmachen ... und wir wissen, dass die Tabellenscans und -sortierungen langsam sind (verglichen mit dem Ziel: Index-Suchvorgänge).

Fügen Sie nun grundlegende Indizes hinzu, um die Kriterien zu unterstützen, die in Ihrem Join verwendet wurden (ich behaupte nicht, dass dies optimale Indizes sind, aber sie veranschaulichen den Punkt): http://sqlfiddle.com/#!3/5ec75/1 mit grundlegender Indizierung

Dies zeigt eine Verbesserung. Die verschachtelten Schleifenoperationen (innere Verknüpfungen) verursachen keine relevanten Gesamtkosten mehr für die Abfrage. Der Rest der Kosten verteilt sich nun auf Index-Suchvorgänge (ein Scan nach Inventar, da wir jede Inventarzeile ziehen). Aber wir können es noch besser machen, weil die Abfrage Menge und Preis zieht. Um diese Daten zu erhalten, müssen nach Auswertung der Join-Kriterien Lookups durchgeführt werden.

Die letzte Iteration verwendet "include" für die Indizes, damit der Plan einfach darüber gleiten und die zusätzlich angeforderten Daten direkt aus dem Index selbst abrufen kann. Die Suchanfragen sind also weg: http://sqlfiddle.com/#!3/5f143/1 Bildbeschreibung hier eingeben

Jetzt haben wir einen Abfrageplan, in dem die Gesamtkosten der Abfrage gleichmäßig auf sehr schnelle Indexsuchvorgänge verteilt sind. Dies wird fast so gut sein, wie es nur geht. Sicherlich können andere Experten dies weiter verbessern, aber die Lösung behebt einige wichtige Bedenken:

  1. Es werden verständliche Datenstrukturen in Ihrer Datenbank erstellt, die einfacher zu erstellen und in anderen Bereichen einer Anwendung wiederzuverwenden sind.
  2. Alle kostspieligsten Abfrageoperatoren wurden mithilfe einer grundlegenden Indizierung aus dem Abfrageplan herausgerechnet.
Cocogorilla
quelle
3
Dies ist in Ordnung (für SQL-Server), aber die Optimierung für verschiedene DBMS weist zwar Ähnlichkeiten, aber auch gravierende Unterschiede auf.
ypercubeᵀᴹ
@ypercube das ist wahr. Ich habe einige Qualifikationen über Postgres hinzugefügt. Mein Ziel war es, dass der größte Teil des hier dargestellten Denkprozesses unabhängig von DBMS-spezifischen Funktionen angewendet werden kann.
Cocogorilla
Die Antwort ist sehr ausführlich, daher werde ich einige Zeit brauchen, um sie auszuprobieren. Ich werde dich wissen lassen, wie es mir geht.
Tom Ellis
5

Wenn Sie zufällig PostgreSQL 9.3 (heute veröffentlicht) haben, können Sie LATERAL JOIN verwenden.

Ich habe keine Möglichkeit, dies zu testen, und habe es noch nie zuvor verwendet, aber nach dem, was ich aus der Dokumentation entnehmen kann, würde die Syntax etwa so lauten:

SELECT  Inventory.Date,
        Inventory.Good,
        Inventory.Quantity,
        Price.Date,
        Price.Price
FROM    Inventory
        LATERAL
        (   SELECT  Date, Price
            FROM    Price
            WHERE   Price.Good = Inventory.Good
            AND     Price.Date <= Inventory.Date
            ORDER BY Price.Date DESC
            LIMIT 1
        ) p;

Dies ist im Grunde genommen gleichbedeutend mit APPLY von SQL-Server , und es gibt ein funktionierendes Beispiel für SQL-Fiddle zu Demonstrationszwecken .

GarethD
quelle
5

Wie Erwin und andere angemerkt haben, hängt eine effiziente Abfrage von vielen Variablen ab, und PostgreSQL ist sehr bemüht, die Abfrageausführung basierend auf diesen Variablen zu optimieren. Im Allgemeinen möchten Sie zuerst aus Gründen der Übersichtlichkeit schreiben und dann nach dem Erkennen von Engpässen aus Gründen der Leistung ändern.

Zusätzlich hat PostgreSQL eine Reihe von Tricks, mit denen Sie die Effizienz steigern können (Teilindizes für einen). Abhängig von Ihrer Lese- / Schreiblast können Sie dies möglicherweise sehr weit optimieren, indem Sie sich mit sorgfältiger Indizierung befassen.

Das erste, was Sie versuchen sollten, ist, eine Ansicht zu erstellen und sich dieser anzuschließen:

CREATE VIEW most_recent_rows AS
SELECT good, max(date) as max_date
FROM inventory
GROUP BY good;

Dies sollte gut funktionieren, wenn Sie Folgendes tun:

SELECT price 
  FROM inventory i
  JOIN goods g ON i.goods = g.description
  JOIN most_recent_rows r ON i.goods = r.goods
 WHERE g.id = 123;

Dann können Sie sich dem anschließen. Die Abfrage verbindet sich dann mit der Ansicht der zugrunde liegenden Tabelle. Vorausgesetzt, Sie haben einen eindeutigen Index für (Datum, in dieser Reihenfolge gültig ), sollten Sie bereit sein (da dies eine einfache Cache-Suche ist). Dies funktioniert mit ein paar nachgeschlagenen Zeilen sehr gut, ist jedoch sehr ineffizient, wenn Sie versuchen, Millionen von Warenpreisen zu verdauen.

Das zweite, was Sie tun können, ist, der Inventartabelle die Spalte most_recent bool und hinzuzufügen

create unique index on inventory (good) where most_recent;

Sie möchten dann Trigger verwenden, um most_recent auf false zu setzen, wenn eine neue Zeile für eine Ware eingefügt wurde. Dies erhöht die Komplexität und erhöht die Wahrscheinlichkeit von Fehlern, ist jedoch hilfreich.

Auch hier hängt vieles davon ab, ob geeignete Indizes vorhanden sind. Bei den letzten Datumsabfragen sollten Sie wahrscheinlich einen Index für das Datum und möglicherweise einen Index für mehrere Spalten haben, der mit dem Datum beginnt und Ihre Beitrittskriterien enthält.

Update Per Erwins Kommentar unten, es sieht so aus, als hätte ich das falsch verstanden. Ich lese die Frage noch einmal und bin mir nicht sicher, was gefragt wird. Ich möchte im Update erwähnen, welches potenzielle Problem ich sehe und warum dies unklar bleibt.

Das angebotene Datenbankdesign hat keinen wirklichen IME-Nutzen mit ERP- und Buchhaltungssystemen. Es würde in einem hypothetischen perfekten Preismodell funktionieren, in dem alles, was an einem bestimmten Tag eines bestimmten Produkts verkauft wird, den gleichen Preis hat. Dies ist jedoch nicht immer der Fall. Dies gilt nicht einmal für Dinge wie Währungsumtausch (obwohl einige Modelle so tun, als ob dies der Fall wäre). Wenn dies ein erfundenes Beispiel ist, ist es unklar. Wenn es sich um ein echtes Beispiel handelt, gibt es größere Probleme beim Entwurf auf Datenebene. Ich gehe hier davon aus, dass dies ein echtes Beispiel ist.

Sie können nicht davon ausgehen, dass dieses Datum allein den Preis für eine bestimmte Ware angibt. Preise in jedem Geschäft können pro Gegenpartei und manchmal sogar pro Transaktion ausgehandelt werden. Aus diesem Grund sollten Sie den Preis wirklich in der Tabelle speichern, die das Inventar tatsächlich verarbeitet (die Inventartabelle). In einem solchen Fall gibt Ihre Datums- / Waren- / Preistabelle lediglich einen Grundpreis an, der sich aufgrund von Verhandlungen ändern kann. In einem solchen Fall handelt es sich bei diesem Problem nicht mehr um ein Berichtsproblem, sondern um ein Transaktionsproblem, bei dem jeweils eine Zeile aus jeder Tabelle bearbeitet wird. Sie können dann beispielsweise den Standardpreis für ein bestimmtes Produkt an einem bestimmten Tag wie folgt nachschlagen:

 SELECT price 
   FROM prices p
   JOIN goods g ON p.good = g.good
  WHERE g.id = 123 AND p."date" >= '2013-03-01'
  ORDER BY p."date" ASC LIMIT 1;

Mit einem Preisindex (gut, Datum) wird dies eine gute Leistung bringen.

Wenn dies ein erfundenes Beispiel ist, hilft vielleicht etwas, an dem Sie näher arbeiten.

Chris Travers
quelle
Der most_recentAnsatz sollte für den aktuellsten Preis durchaus funktionieren . Es scheint jedoch, dass das OP den aktuellsten Preis in Bezug auf jedes Inventardatum benötigt.
Erwin Brandstetter
Guter Punkt. Beim erneuten Lesen stelle ich einige echte praktische Mängel bei den vorgeschlagenen Daten fest, aber ich kann nicht sagen, ob es sich nur um ein erfundenes Beispiel handelt. Als erfundenes Beispiel kann ich nicht sagen, was fehlt. Vielleicht wäre auch ein Update angebracht, um darauf hinzuweisen.
Chris Travers
@ChrisTravers: Es ist ein erfundenes Beispiel, aber es steht mir nicht frei, das tatsächliche Schema, mit dem ich arbeite, zu veröffentlichen. Vielleicht könnten Sie ein wenig darüber sagen, welche praktischen Mängel Sie entdeckt haben.
Tom Ellis
Ich glaube nicht, dass es genau sein muss, aber ich mache mir Sorgen, dass das Problem in der Allegorie verloren geht. Etwas näheres wäre hilfreich. Das Problem ist, dass bei der Preisgestaltung der Preis an einem bestimmten Tag wahrscheinlich ein Standardwert ist und Sie ihn daher nicht für die Berichterstellung nur als Standardwert für die Transaktionserfassung verwenden würden, sodass Ihre interessanten Abfragen in der Regel nur wenige Zeilen hintereinander enthalten Zeit.
Chris Travers
3

Eine andere Möglichkeit wäre, die Fensterfunktion lead()zu verwenden, um den Datumsbereich für jede Zeile im Tabellenpreis abzurufen und dann betweenbeim Zusammenführen des Inventars zu verwenden. Ich habe dies tatsächlich im wirklichen Leben verwendet, aber hauptsächlich, weil dies meine erste Idee war, wie man das löst.

with cte as (
  select
    good,
    price,
    date,
    coalesce(lead(date) over(partition by good order by date) - 1
            ,Now()::date) as ndate
  from
    price
)

select * from inventory i join cte on
  (i.good = cte.good and i.date between cte.date and cte.ndate)

SqlFiddle

Tomas Greif
quelle
1

Verwenden Sie eine Verknüpfung von Inventar zu Preis mit Verknüpfungsbedingungen, die die Aufzeichnungen aus der Preistabelle auf diejenigen beschränken, die sich am oder vor dem Inventardatum befinden, und extrahieren Sie dann das maximale Datum, wobei das Datum das höchste Datum aus dieser Teilmenge ist

Also für Ihren Inventarpreis:

 Select i.date, p.Date pricingDate,
    i.good, quantity, price        
 from inventory I join price p 
    on p.good = i.good
        And p.Date = 
           (Select Max(Date from price
            where good = i.good
               and date <= i.Date)

Wenn sich der Preis für eine bestimmte Ware am selben Tag mehrmals geändert hat und Sie wirklich nur Datums- und Uhrzeitangaben in diesen Spalten haben, müssen Sie möglicherweise die Joins stärker einschränken, um nur einen der Preisänderungsdatensätze auszuwählen.


quelle
Scheint die Sache leider nicht zu beschleunigen.