我有一个名为 OffDays 的表,其中保存周末和节假日日期。我有一个名为 LeadTime 的表,其中存储了要制造的产品的时间(以天为单位)。最后,我有一个名为“订单”的表,其中保存了产品和订单日期。

是否可以在不使用存储过程或循环的情况下查询产品何时完成制造?

例如:

  • 休息日有 2008-01-10、2008-01-11、2008-01-14。
  • 产品 9 的 LeadTime 为 5。
  • 产品 9 的订单已于 2008 年 1 月 9 日订购。

我正在寻找的计算是这样的:

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

我想知道是否可以让查询返回 2008-01-16 而无需使用存储过程,或在我的应用程序代码中计算它。

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

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

有帮助吗?

解决方案

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

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 表,但不需要用于常规查询。与使用应用程序代码计算天数相比,到服务器的往返次数也更少。

其他提示

最好的方法是使用日历表。

http://web.archive.org/web/20070611150639/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。

只需在应用程序代码中计算即可...更容易,你不必在 sql 中编写一个非常丑陋的查询

这是一种方法 - 使用 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

你为什么反对使用循环?

//一些伪代码

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。从那里开始一个简单的计算。获取订单日期的偏移量,添加提前期,然后查找计算出的偏移量以获取结束日期。

一种方法(无需创建另一个表)是使用一种上限函数:对于每个关闭日期,在子查询中找出相对于订单日期在其之前有多少个“开启日期”。然后取小于提前期的最大数字。使用与该日期相对应的日期,加上余数。

此代码可能特定于 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),因此您必须注意按时插入休息日。假设订单不会在休息日开始。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top