2008-09-18 30 views
3

我有一张名为OffDays的表格,周末和假期日期保留。我有一个名为LeadTime的表格,其中存储了要制造的产品的时间量(以天计)。最后,我有一张名为Order的表格,其中保存了产品和订单日期。非迭代/非循环计算生效日期的方法?

是否有可能查询产品何时完成制造而不使用存储过程或循环?

例如:

  • OffDays具有2008-01-10,2008-01-11,2008-01-14。
  • 前置时间有5个产品9
  • 令已2008-01-09产品9.

我找的计算是这样的:

  • 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

我不知道是否有可能有一个查询返回2008-01-16而不必使用存储过程,或者在我的应用程序代码中进行计算。

编辑(为什么没有存储过程/循环): 我不能使用存储过程的原因是它们不受数据库支持。我只能添加额外的表格/数据。该应用程序是第三方报告工具,我只能控制SQL查询。

编辑(如何我做现在): 我现在的方法是,我有顺序表一个额外的列来保存计算的日期,然后计划的任务/ cron作业运行在所有的计算订单每小时。由于几个原因,这并不理想。

回答

2

您可以提前生成一个工作日表格。

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 

然后做一个查询,如

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 

您将需要一个存储过程使用循环来生成WorkingDays表,但不是定期查询。与使用应用程序代码来计算日期相比,它往返服务器的次数也更少。

1

就计算它在应用程序代码...容易得多,你会不会在你的SQL

2

最好的方法是使用一个日历表写下真难看查询。

请参阅http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html

然后将查询可能类似于:

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 
    ) 

希望帮助,

RB。

+0

不好意思,但这是我见过的最棘手的问题之一。您正在将日历与订单表交叉连接,其中日历上唯一的过滤器是两个布尔值,并且您有一个前导时间表,并加入了您加入order.LeadId的leadId。咦? – 2008-09-18 15:16:59

+0

嗨达雷尔 - 是的,我刚刚意识到我错过了他提到的产品表。 Mea culpa! 我确实意识到日历交叉连接会使这个非常缓慢 - 我正试图考虑如何重写它...... – 2008-09-18 15:28:37

0

你为什么反对使用循环?

//一些伪

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

这似乎是一个过于简单的功能,试图找到一个非循环方式。

0

嗯..一个解决方案可以是存储日期的表与偏移是基于从年初的非休息日的计数。让我们说1月。 2是休息日。 1/1/08的偏移量为1(如果你想从0开始,则为0)。 1/3/08的偏移量为2,因为计数跳过1/2/08。从那里简单的计算。获取订单日期的偏移量,添加提前期,然后查看计算出的偏移量以获取结束日期。

1

这里有一种方法 - 使用dateadd函数。

我需要把这个答案从桌子上拿走。这在长期交付时间内无法正常工作。这只不过是增加了交货时间中找到的天数,并推出了日期。这会导致一个问题,当有更多的休息日出现在新的范围内。

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

的一种方式(不包括创建另一个表)使用排序功能天花板的:每个offdate,找出有多少“约会”才来,相对于订单日期,在一个子查询。然后取最后一个小于交付时间的数字。使用相应的日期,加上余数。

此代码可能特定于PostgreSQL,对不起,如果这不是你正在使用的。

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

这是PostgreSQL的语法,但它应该很容易转换为其它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 

这将不会返回结果为,将截止日期后在offdays表完成订单(订单3号),所以你必须小心按时插入星期五。假设订单不会在星期一开始。