Aktualisieren offener Bestellungen aus einer Preisliste innerhalb eines bestimmten Zeitraums

7

Installieren

Ich habe ein Beispiel für rextester und dbfiddle erstellt .

Szenario

Preisliste : Es ist die Preisliste für Produkte. Ein Produkt kann mehr als einen aktiven Preis haben, sogar einen zukünftigen Preis.

+---------+-------+------------+--------+--------+
| product | price | date_price |  base  | active |
+---------+-------+------------+--------+--------+
|   0125  |    90 | 01.01.2017 |  1200  |    0   |
|   0125  |   100 | 25.01.2017 |  1000  |    1   |
|   0125  |   110 | 27.02.2017 |   500  |    1   |
+---------+-------+------------+--------+--------+
|   1200  |   140 | 01.01.2017 |  2000  |    0   |
|   1200  |   150 | 01.02.2017 |  1500  |    1   |
|   1200  |   160 | 27.02.2017 |  1000  |    1   |
+---------+-------+------------+--------+--------+

Bestellungen Ausstehende Bestellungen haben einen Preis und ein Bestelldatum

+---------+------------+-------+--------+
| product | order_date | price |  base  |
+---------+------------+-------+--------+
|   0125  | 19.02.2017 |  100  |  1000  |
|   0125  | 20.02.2017 |  100  |  1000  |
|   0125  | 21.02.2017 |  100  |  1000  |
|   0125  | 22.02.2017 |  100  |  1000  |
|   0125  | 23.02.2017 |  100  |  1000  |
|   0125  | 28.02.2017 |  110  |   500  |
+---------+------------+-------+--------+
|   1200  | 19.02.2017 |  150  |  1500  |
|   1200  | 20.02.2017 |  150  |  1500  |
|   1200  | 21.02.2017 |  150  |  1500  |
|   1200  | 22.02.2017 |  150  |  1500  |
|   1200  | 23.02.2017 |  150  |  1500  |
|   1200  | 28.02.2017 |  160  |  1000  |
+---------+------------+-------+--------+

Jedes Mal, wenn wir der Liste einen neuen Preis hinzufügen, müssen wir die betroffenen Zeilen ausstehender Bestellungen aktualisieren.

Zum Beispiel, wenn wir hinzufügen:

+---------+-------+------------+--------+--------+
| product | price | date_price |  base  | active |
+---------+-------+------------+--------+--------+
|   0125  |   105 | 21.02.2017 |  1300  |    1   |
|   1200  |   155 | 21.02.2017 |  1400  |    1   |
+---------+-------+------------+--------+--------+

Die neue Preisliste muss lauten:

+---------+------------+-------+--------+
| product | order_date | price |  base  |
+---------+------------+-------+--------+
|   0125  | 19.02.2017 |  100  |  1000  |
|   0125  | 20.02.2017 |  100  |  1000  |
|   0125  | 21.02.2017 |  105  |  1300  | *
|   0125  | 22.02.2017 |  105  |  1300  | * Affected rows
|   0125  | 23.02.2017 |  105  |  1300  | *
|   0125  | 28.02.2017 |  110  |   500  | 
+---------+------------+-------+--------+
|   1200  | 19.02.2017 |  150  |  1500  |
|   1200  | 20.02.2017 |  150  |  1500  |
|   1200  | 21.02.2017 |  150  |  1500  | *
|   1200  | 22.02.2017 |  150  |  1500  | * Affectd rows between 21.02.2017 and 27.02.2017
|   1200  | 23.02.2017 |  150  |  1500  | *
|   1200  | 28.02.2017 |  160  |  1000  |
+---------+------------+-------+--------+

Ich möchte betroffene Datensätze mit einer einzigen Abfrage aktualisieren.

Da es einen weiteren Preis gibt, der am 27.02.2017 beginnt, sind Bestellungen vom 28.02.2017 vom eingefügten Preis nicht betroffen.

Tatsächlicher Prozess

Inzwischen verwende ich eine Unterabfrage, die nach dem ersten Datum sucht, das in der Preislistentabelle übereinstimmt, aber jetzt muss ich auch das baseFeld aktualisieren . (Und zwei oder drei weitere Felder) und ich möchte vermeiden, zwei oder mehr Unterabfragen zu verwenden.

update @orders
set    price = (select   top 1 pl.price
                from     @price_list pl
                where    pl.product = o.product
                and      pl.date_price <= o.order_date
                and      active = 1
                order by pl.date_price desc),
       base  = (select   top 1 pl.base
                from     @price_list pl
                where    pl.product = o.product
                and      pl.date_price <= o.order_date
                and      active = 1
                order by pl.date_price desc)
from   @orders o
where  o.product in ('0125', '1200');  --<<< select distinct product from inserted

Bitte zögern Sie nicht, meinen Text zu korrigieren. Ich weiß, dass meine englische Grammatik nicht gut genug ist.

McNets
quelle
Macht Ihr "tatsächlicher Prozess" das, was Sie wollen, aber Sie wollen einfach nicht die mehreren Unterabfragen? Deshalb habe ich CROSS APPLY (gelöschte Antwort jetzt) ​​als mögliche Lösung angeboten. Mit CROSS APPLY können Sie dem Unterabfrageteil als Tabelle beitreten und alle benötigten Spalten abrufen.
Scott Hodgin
Inzwischen aktualisiere ich nur den Preis, aber es gibt neue Anforderungen und ich möchte nicht mehrere Unterabfragen verwenden, wenn möglich.,
McNets
Warum können Sie CROSS APPLY und SET nicht verwenden? o.Price = ca.Price, o.Base = ca.Base, o.AnyOtherColumn = ca.AnyOtherColumn?
Scott Hodgin
Ja, ich denke schon.
McNets

Antworten:

5

Ich habe Ihren dbfiddler-Code in der folgenden Lösung verwendet, die CROSS APPLY verwendet

declare @price_list table(product varchar(20), price int, date_price datetime, base int, active tinyint);
insert into @price_list values
('0125',  90, '2017-01-01', 1200, 0), 
('0125', 100, '2017-01-25', 1000, 1), 
('0125', 110, '2017-02-27', 500,  1),
('1200', 140, '2017-01-01', 2000, 0), 
('1200', 150, '2017-02-01', 1500, 1), 
('1200', 160, '2017-02-27', 1000, 1);

declare @orders table(product varchar(20), order_date datetime, price int, base int);
insert into @orders values
('0125', '2017-02-19', 100, 1000), 
('0125', '2017-02-20', 100, 1000),
('0125', '2017-02-21', 100, 1000), 
('0125', '2017-02-22', 100, 1000),
('0125', '2017-02-23', 100, 1000), 
('0125', '2017-02-28', 110,  500),
('1200', '2017-02-19', 150, 1500), 
('1200', '2017-02-20', 150, 1500),
('1200', '2017-02-21', 150, 1500), 
('1200', '2017-02-22', 150, 1500),
('1200', '2017-02-23', 150, 1500), 
('1200', '2017-02-28', 160, 1000);



declare @new_date_price datetime = '2017-02-21';


-- add a new price to the list
--
insert into @price_list values ('0125', 105, @new_date_price - 1, 1300, 1);
insert into @price_list values ('1200', 155, @new_date_price - 1, 1400, 1);


-- update orders price and base, according products price list date price
--
-- I'd like to avoid use 2 (or more) subqueries
--
UPDATE o
SET o.Price = ca.price
    ,o.base = ca.base
FROM @Orders o
CROSS APPLY (
    SELECT TOP 1 pl.price AS price
        ,pl.base AS base
    FROM @Price_List pl
    WHERE pl.product = o.product
        AND pl.date_price <= o.order_date
        AND active = 1
    ORDER BY pl.date_price DESC
    ) ca
WHERE o.product IN (
        '0125'
        ,'1200'
        );--<<< select distinct product from inserted
-- final result
--
select * from @price_list order by product, date_price;
select * from @orders order by product, order_date;
Scott Hodgin
quelle
Ok, lass es mich in meiner Datenbank überprüfen, aber es sieht so gut aus. Vielen Dank.
McNets
Hallo, ich teste auf meinem Staging-Server, es funktioniert gut, aber jetzt sind alle Bestellungen aktualisiert. Glauben Sie, dass es möglich ist, nur Bestellungen zu aktualisieren, die von der neuen Preisliste betroffen sind, da dies auf einem Auslöser ausgeführt wird?
McNets
Mit neuer Preisliste meine ich mitselect price, base, date_price from inserted
McNets
Nun - das "klingt" vernünftig - ich kann mir nicht sofort einen Grund vorstellen, der nicht funktionieren würde ... Ich würde das sicherlich in einer Nicht-Produktionsumgebung "testen".
Scott Hodgin
0

Zunächst möchte ich @ScottHodgin für seine Arbeit zur Lösung meiner Frage danken.

Obwohl seine Antwort das Problem korrekt gelöst hat, werden alle aktiven Bestellungen für ein bestimmtes Produkt aktualisiert. Und ich wollte eine Lösung finden, die nur Bestellungen aktualisiert, die vom neuen Preislistendatum betroffen sind.

Da diese Prozedur innerhalb eines Triggers ausgeführt wird, muss ich eine Liste von Datumsintervallen erstellen, um sie mit dem Datum der Bestellungen zu vergleichen.

Ich füge 'next_date' einen Tag hinzu, wenn es keinen nächsten PriceList-Datensatz gibt, nur um ihn zu vergleichen <=.

SELECT product, price, date_price, base, active,
       (SELECT TOP 1     pl.date_price
               FROM      PriceList pl
               WHERE     pl.product = i.product
               AND       pl.date_price > i.date_price
               AND       pl.active > 0
               ORDER BY  pl.date_price ASC) next_date
FROM inserted i

Sobald das Problem des Datumsbereichs gelöst ist, muss ich nur noch Bestellungen innerhalb dieses Bereichs aktualisieren.

WITH PList AS
(
SELECT product, price, date_price, base, active,
       (SELECT TOP 1     pl.date_price
               FROM      PriceList pl
               WHERE     pl.product = i.product
               AND       pl.date_price > i.date_price
               AND       pl.active > 0
               ORDER BY  pl.date_price ASC) next_date
FROM inserted i
)
UPDATE     Orders
SET        price = PList.price,
           base  = PList.base,
           [status] = 'Updated'
FROM       Orders o
INNER JOIN Plist
ON         Plist.product = o.product 
AND        o.order_date >= Plist.date_price
AND        o.order_date < COALESCE(Plist.next_date, DATEADD(DAY, 1, Plist.next_date);

Nach dem Einfügen von zwei Datensätzen:

DECLARE @new_date_price datetime = '2017-02-21';

INSERT INTO PriceList VALUES 
('0125', 105, @new_date_price, 1300, 2),
('1200', 155, @new_date_price, 1400, 2);

Dies ist das Endergebnis:

|product|order_date  |price|base|status |
|:------|:-----------|----:|---:|:------|
|0125   |Feb 19 2017 |  100|1000|       |
|0125   |Feb 20 2017 |  100|1000|       |
|0125   |Feb 21 2017 |  105|1300|Updated|
|0125   |Feb 22 2017 |  105|1300|Updated|
|0125   |Feb 23 2017 |  105|1300|Updated|
|0125   |Feb 28 2017 |  110| 500|       |
|1200   |Feb 19 2017 |  150|1500|       |
|1200   |Feb 20 2017 |  150|1500|       |
|1200   |Feb 21 2017 |  155|1400|Updated|
|1200   |Feb 22 2017 |  155|1400|Updated|
|1200   |Feb 23 2017 |  155|1400|Updated|
|1200   |Feb 28 2017 |  160|1000|       |

Wenn jemand interessiert ist, habe ich ein dbfiddle- Beispiel eingerichtet.

McNets
quelle