非迭代/非循环方式来计算生效日期?
我有一个名为OffDays的表,其中保存了周末和节假日的日期。我有一个称为LeadTime的表,其中存储要制造的产品的时间(以天为单位)。最后,我有一个名为Order的表,其中保留了产品和订单日期。
是否可以查询何时不使用存储过程或者循环完成产品的生产?
例如:
- OffDays的时间为2008-01-10、2008-01-11、2008-01-14.
- LeadTime的产品9有5.
- 产品9的订单为2008-01-09.
我正在寻找的计算是这样的:
- 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作业每小时对所有订单运行计算。由于几个原因,这并不理想。
解决方案
只需在应用程序代码中进行计算即可...更加轻松,我们将不必在sql中编写一个非常难看的查询
最好的方法是使用日历表。
请参阅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。
我们为什么反对使用循环?
//一些伪代码
int leadtime = 5; date order = 2008-01-09; date finishdate = order; while (leadtime > 0) { finishdate.addDay(); if (!IsOffday(finishdate)) leadtime--; } return finishdate;
这似乎太简单了,无法尝试找到非循环的方法。
嗯..一种解决方案可以是存储一个基于偏移量的日期表,该偏移量基于从年初开始的非休息日计数。可以说扬。 2是休息日。 1/1/08的偏移量为1(如果要从0开始,则偏移为0)。 1/3/08的偏移量为2,因为计数跳过了1/2/08. 从那里进行简单的计算。获取订单日期的偏移量,添加提前期,然后对计算出的偏移量进行查找以获取结束日期。
我们可以提前生成工作日表。
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表,但对于常规查询则不需要。与使用应用程序代码计算天数相比,与服务器的往返次数也更少。
这是使用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
一种方法(不创建另一个表)是使用某种上限函数:对于每个offdate,在子查询中找出相对于订单日期而言在其之前有多少个" on date"。然后取小于提前期的最大数字。使用与之相对应的日期,再加上其余的日期。
这段代码可能是特定于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;
这是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
对于将在截止日期表中的最后日期之后完成的订单(订单编号3),这不会返回结果,因此我们必须注意准时插入截止时间。