代码之家  ›  专栏  ›  技术社区  ›  Martin

计算生效日期的非迭代/非循环方式?

  •  3
  • Martin  · 技术社区  · 16 年前

    我有一张叫做“休息日”的桌子,上面放着周末和假日。我有一个名为leadtime的表,其中存储了生产产品的时间(以天为单位)。最后,我有一个名为“订单”的表,其中保存了一个产品和订单日期。

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

    例如:

    • 休息日为2008-01-10、2008-01-11、2008-01-14。
    • 产品9的LeadTime有5个。
    • 订单编号为2008-01-09,适用于产品9。

    我要计算的是:

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

    我想知道是否可以在不使用存储过程的情况下获得查询返回2008-01-16,或者在应用程序代码中计算它。

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

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

    8 回复  |  直到 16 年前
        1
  •  2
  •   finnw    16 年前

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

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

        2
  •  2
  •   Community CDub    6 年前

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

    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。

        3
  •  1
  •   Joel Martinez    16 年前

    只需在应用程序代码中计算…更简单,您不必在SQL中编写一个非常难看的查询。

        4
  •  1
  •   kamajo    16 年前

    这里有一种方法-使用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
    
        5
  •  0
  •   Stormenet    16 年前

    你为什么反对使用循环?

    //一些伪代码

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

    这似乎是一个太简单的函数,试图找到一个非循环的方式。

        6
  •  0
  •   Russell Leggett    16 年前

    隐马尔可夫模型。。一种解决方案是存储一个日期表,其中包含基于年初非休假天数计数的偏移量。假设1月2日是休息日。1/1/08的偏移量为1(如果您想从0开始,则为0)。1/3/08的偏移量为2,因为计数跳过1/2/08。从那里,它是一个简单的计算。获取订单日期的偏移量,添加提前期,然后查找计算的偏移量以获取结束日期。

        7
  •  0
  •   Kev    16 年前

    一种方法(不创建另一个表)是使用一种上限函数:对于每个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;
    
        8
  •  0
  •   user17957    16 年前

    这是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)的最后一个日期之后将完成的订单的结果,因此必须注意按时插入非工作日。假定订单不会在非工作日开始。