Question

J'ai une table appelée OffDays, où les week-ends et les dates de vacances sont conservés. J'ai une table appelée LeadTime où la quantité de temps (en jours) pour qu'un produit soit fabriqué est stockée. Enfin, j’ai une table appelée Commande où un produit et la date de commande sont conservés.

Est-il possible de demander quand un produit sera fini de fabrication sans utiliser de procédures stockées ou de boucles?

Par exemple:

  • OffDays a 2008-01-10, 2008-01-11, 2008-01-14.
  • LeadTime en a 5 pour le produit 9.
  • La commande a 2008-01-09 pour le produit 9.

Le calcul que je recherche est le suivant:

  • 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

Je me demande s'il est possible qu'une requête renvoie le 2008-01-16 sans avoir à utiliser une procédure stockée, ou la calcule dans le code de mon application.

Modifier (pourquoi pas de procédures / boucles stockées): La raison pour laquelle je ne peux pas utiliser de procédures stockées est qu'elles ne sont pas prises en charge par la base de données. Je ne peux ajouter que des tableaux / données supplémentaires. L’application est un outil de reporting tiers dans lequel je ne peux contrôler que la requête SQL.

Modifier (comment je le fais maintenant): Ma méthode actuelle consiste à avoir une colonne supplémentaire dans la table des commandes pour conserver la date calculée, puis une tâche planifiée / périodique exécute le calcul sur toutes les commandes toutes les heures. C’est moins qu’idéal pour plusieurs raisons.

Était-ce utile?

La solution

Vous pouvez générer un tableau des jours ouvrables à l'avance.

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

Faites ensuite une requête telle que

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

Vous auriez besoin d’une procédure stockée avec une boucle pour générer la table WorkingDays, mais pas pour les requêtes classiques. De plus, le nombre d'allers-retours au serveur est moins important que si vous utilisiez le code de l'application pour compter le nombre de jours.

Autres conseils

La meilleure approche consiste à utiliser une table de calendrier.

Voir http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an auxili-calendar-table.html .

Ensuite, votre requête pourrait ressembler à quelque chose comme:

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 
    )

J'espère que cela vous aidera,

RB.

Calculez-le simplement dans le code de l'application ... beaucoup plus facilement et vous n'aurez pas à écrire une requête vraiment moche dans votre sql

voici un moyen - d’utiliser la fonction dateadd.

Je dois retirer cette réponse de la table. Cela ne fonctionnera pas correctement pendant de longs délais. Il s’agissait simplement d’ajouter le nombre de jours de congé trouvés dans le délai d’exécution et de repousser la date. Cela posera un problème lorsque plusieurs jours d'arrêt apparaissent dans la nouvelle plage.

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

Pourquoi êtes-vous contre l'utilisation de boucles?

// pseudocode

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

cela semble être une fonction trop simple pour essayer de trouver un moyen de ne pas utiliser de boucle.

Hmm .. une solution pourrait consister à stocker un tableau de dates avec un décalage basé sur le nombre de jours non chômés à partir du début de l’année. Disons jan. 2 est un jour de congé. 1/1/08 aurait un décalage de 1 (ou 0 si vous souhaitez commencer à partir de 0). 1/3/08 aurait un décalage de 2, car le compte saute le 1/2/08. A partir de là, c'est un calcul simple. Obtenez le décalage de la date de la commande, ajoutez le délai de livraison, puis effectuez une recherche sur le décalage calculé pour obtenir la date de fin.

L’une des méthodes (sans créer une autre table) consiste à utiliser une sorte de fonction de plafonnement: pour chaque date de péremption, déterminez le nombre de "" on dates". venir avant, par rapport à la date de la commande, dans une sous-requête. Ensuite, prenez le nombre le plus élevé qui est inférieur au délai. Utilisez la date correspondante, plus le reste.

Ce code peut être spécifique à PostgreSQL, désolé si ce n'est pas ce que vous utilisez.

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;

Ceci est la syntaxe PostgreSQL mais il devrait être facile de le traduire en un autre dialecte SQL

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

Ceci ne renverra pas le résultat des commandes qui seraient finies après la dernière date du tableau des jours de fermeture (numéro de commande 3). Vous devez donc veiller à insérer les jours de fermeture à l’heure. ne commence pas les jours de fermeture.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top