Frage

Ich habe eine Tabelle namens OffDays, wo Wochenenden und Urlaubsdaten gehalten werden. Ich habe eine Tabelle namens Vorbereitungszeit, wo viel Zeit (in Tagen) für ein Produkt gespeichert werden hergestellt. Schließlich habe ich eine Tabelle namens Order, wo ein Produkt und das Bestelldatum gehalten wird.

Ist es möglich abfragen, wenn eine Produktherstellung abgeschlossen wird ohne die Verwendung von gespeicherten Prozeduren oder Schleifen?

Zum Beispiel:

  • OffDays hat 2008-01-10, 2008-01-11, 2008-01-14.
  • Vorbereitungszeit hat 5 für das Produkt 9.
  • Auftrag hat 2008-01-09 für das Produkt 9.

Die Berechnung, die ich suche, ist dies:

  • 2008-01-09 1
  • 2008-01-10 x
  • 2008-01-11 x
  • 2008-01-12 2
  • 2008-01-13 3
  • 2008-01-14 x
  • 2008-01-15 4
  • 2008-01-16 5

Ich frage mich, ob es möglich ist, eine Abfrage 2008-01-16 Rückkehr zu haben, ohne eine gespeicherte Prozedur zu verwenden, oder es in meinem Anwendungscode zu berechnen.

Edit (warum keine gespeicherten Procs / Schleifen): Der Grund, warum ich kann nicht gespeicherte Prozeduren verwendet, ist, dass sie von der Datenbank nicht unterstützt werden. Ich kann nur zusätzliche Tabellen / Daten hinzufügen. Die Anwendung ist ein Dritte-Reporting-Tool, wo ich nur die SQL-Abfrage steuern kann.

Edit (wie ich tue es jetzt): Meine aktuelle Methode ist, dass ich eine zusätzliche Spalte in der Reihenfolge Tabelle habe das berechnete Datum, dann ein geplante Aufgabe / Cron-Job läuft die Berechnung auf allen Aufträgen pro Stunde zu halten. Dies ist weniger als ideal aus mehreren Gründen.

War es hilfreich?

Lösung

Sie können eine Tabelle des Arbeitstages im Voraus erzeugen.

WDId | WDDate
-----+-----------
4200 | 2008-01-08
4201 | 2008-01-09
4202 | 2008-01-12
4203 | 2008-01-13
4204 | 2008-01-16
4205 | 2008-01-17

Dann machen Sie eine Abfrage wie

SELECT DeliveryDay.WDDate FROM WorkingDay OrderDay, WorkingDay DeliveryDay, LeadTime, Order where DeliveryDay.WDId = OrderDay.WDId + LeadTime.LTDays AND OrderDay.WDDate = '' AND LeadTime.ProductId = Order.ProductId AND Order.OrderId = 1234

Sie würden eine gespeicherte Prozedur mit einer Schleife müssen das werk Tabelle zu generieren, aber nicht für die regelmäßigen Abfragen. Es ist auch weniger Umläufe an den Server, als wenn Sie den Anwendungscode verwenden, um die Tage zu zählen.

Andere Tipps

Der beste Ansatz ist es, eine Kalender-Tabelle zu verwenden.

Siehe http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an- Hilfs-Kalender-table.html .

Dann ist Ihre Abfrage etwas aussehen könnte:

SELECT c.dt, l.*, o.*, c.*
    FROM [statistics].dbo.[calendar] c, 
    [order] o  JOIN
    lead l ON l.leadId = o.leadId
    WHERE c.isWeekday = 1 
    AND   c.isHoliday =0 
    AND   o.orderId = 1
    AND   l.leadDays = ( 
        SELECT COUNT(*)  
            FROM [statistics].dbo.Calendar c2 
            WHERE c2.dt >= o.startDate
            AND c2.dt <= c.dt 
            AND c2.isWeekday=1 
            AND c2.isHoliday=0 
    )

Ich hoffe, das hilft,

RB.

berechnen Sie es einfach in Anwendungscode ... viel einfacher, und Sie werden nicht eine wirklich hässliche Abfrage in der SQL schreiben müssen

Hier ist eine Möglichkeit - die dateadd Funktion.

Ich brauche diese Antwort vom Tisch zu nehmen. Das wird nicht richtig für lange Laufzeiten arbeiten. Es wurde das Hinzufügen einfach die Anzahl der freien Tage in der Vorlaufzeit gefunden und das Datum aus drücken. Dadurch wird ein Problem, wenn mehr freie Tage im neuen Bereich angezeigt.

-- Setup test
create table #odays (offd datetime)
create table #leadtime (pid int , ltime int)
create table [#order] (pid int, odate datetime)


insert into #odays 
select '1/10/8'
insert into #odays 
select '1/11/8'
insert into #odays 
select '1/14/8'


insert into #Leadtime
values (3,5)
insert into #leadtime
values (9, 5)

insert into #order 
values( 9, '1/9/8')

select dateadd(dd, 
(select count(*)-1 
   from #odays 
   where offd between odate and  
    (select odate+ltime 
       from #order o 
       left join #leadtime l 
         on o.pid = l.pid 
       where l.pid = 9
     )
 ),
 odate+ltime) 
 from #order o 
 left join #leadtime l  
   on o.pid = l.pid 
 where o.pid = 9

Warum sind Sie gegen Loops?

// einig Pseudo-Code

int leadtime = 5;
date order = 2008-01-09;
date finishdate = order;
while (leadtime > 0) {
finishdate.addDay();
if (!IsOffday(finishdate)) leadtime--;
}
return finishdate;

Dies scheint eine zu einfache Funktion zu versuchen, einen nicht-Looping Weg zu finden.

Hmm .. Eine Lösung könnte eine Tabelle der Daten mit einer basierend auf einer Zählung von nicht-off Tagen ab dem Beginn des Jahres speichern ausgeglichen werden. Lassen Sie uns sagen Januar 2 ist ein Off Tag. 1.1.08 einen Versatz von 1 wäre (oder 0, wenn Sie von 0 bis starten möchten). 1/3/08 einen Versatz von 2 hätte, weil die Graf 1/2/08 überspringt. Von dort seine eine einfache Rechnung. der Versatz des Bestelldatums erhalten, fügen Sie die Vorlaufzeit, dann einen Nachschlag auf dem Offset berechnet das Enddatum zu erhalten.

Eine Möglichkeit (ohne eine weitere Tabelle erstellen) eine Art Decke Funktion verwendet: für jeden offdate, herauszufinden, wie viele „zu Terminen“ kommen, bevor sie, bezogen auf das Bestelldatum, in einer Unterabfrage. Dann nehmen Sie die höchste Zahl, die kleiner ist als die Vorlaufzeit ist. Verwenden Sie das Datum, das dem entspricht, zuzüglich der Rest.

Dieser Code spezifisch für PostgreSQL, sorry, wenn das ist nicht das, was Sie verwenden.

CREATE DATABASE test;
CREATE TABLE offdays
(
  offdate date NOT NULL,
  CONSTRAINT offdays_pkey PRIMARY KEY (offdate)
);
insert into offdays (offdate) values ('2008-01-10');
insert into offdays (offdate) values ('2008-01-11');
insert into offdays (offdate) values ('2008-01-14');
insert into offdays (offdate) values ('2008-01-18'); -- just for testing
CREATE TABLE product
(
  id integer NOT NULL,
  CONSTRAINT product_pkey PRIMARY KEY (id)
);
insert into product (id) values (9);
CREATE TABLE leadtime
(
  product integer NOT NULL,
  leaddays integer NOT NULL,
  CONSTRAINT leadtime_pkey PRIMARY KEY (product),
  CONSTRAINT leadtime_product_fkey FOREIGN KEY (product)
      REFERENCES product (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
insert into leadtime (product, leaddays) values (9, 5);
CREATE TABLE "order"
(
  product integer NOT NULL,
  "start" date NOT NULL,
  CONSTRAINT order_pkey PRIMARY KEY (product),
  CONSTRAINT order_product_fkey FOREIGN KEY (product)
      REFERENCES product (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
insert into "order" (product, "start") values (9, '2008-01-09');

-- finally, the query:

select e.product, offdate + (leaddays - ondays)::integer as "end"
from
(
    select c.product, offdate, (select (a.offdate - c."start") - count(b.offdate) from offdays b where b.offdate < a.offdate) as ondays, d.leaddays
    from offdays a, "order" c
    inner join leadtime d on d.product = c.product
) e
where leaddays >= ondays
order by "end" desc
limit 1;

Dies ist PostgreSQL-Syntax, aber es sollte auch auf anderen SQL-Dialekt zu übersetzen einfach sein

--Sample data
create table offdays(datum date);

insert into offdays(datum)
select to_date('2008-01-10','yyyy-MM-dd') UNION 
select to_date('2008-01-11','yyyy-MM-dd') UNION 
select to_date('2008-01-14','yyyy-MM-dd') UNION 
select to_date('2008-01-20','yyyy-MM-dd') UNION
select to_date('2008-01-21','yyyy-MM-dd') UNION
select to_date('2008-01-26','yyyy-MM-dd');

create table leadtime (product_id integer , lead_time integer);
insert into leadtime(product_id,lead_time) values (9,5);

create table myorder (order_id integer,product_id integer, datum date);
insert into myorder(order_id,product_id,datum) 
values (1,9,to_date('2008-01-09','yyyy-MM-dd'));
insert into myorder(order_id,product_id,datum) 
values (2,9,to_date('2008-01-16','yyyy-MM-dd'));
insert into myorder(order_id,product_id,datum) 
values (3,9,to_date('2008-01-23','yyyy-MM-dd'));

--Query
select order_id,min(finished_date)
FROM 
    (select mo.order_id,(mo.datum+lead_time+count(od2.*)::integer-1) as finished_date
     from 
         myorder mo
         join leadtime lt on (mo.product_id=lt.product_id)
         join offdays od1 on (mo.datum<od1.datum)
         left outer join offdays od2 on (mo.datum<od2.datum and od2.datum<od1.datum)
     group by  mo.order_id,mo.datum,lt.lead_time,od1.datum
     having (mo.datum+lead_time+count(od2.*)::integer-1) < od1.datum) tmp
group by 1;       

--Results :
1    2008.01.16
2    2008.01.22

Dies wird nicht zurück Ergebnis für Bestellungen, die nach dem letzten Datum in offdays Tabelle (Bestellnummer 3) fertig gestellt werden würden, so dass man darauf achten muss offdays auf time.It einfügen wird davon ausgegangen, dass Aufträge tun beginnen Sie nicht auf offdays.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top