Не итеративный / нецикличный способ расчета даты вступления в силу? - PullRequest
3 голосов
/ 18 сентября 2008

У меня есть таблица OffDays, где хранятся выходные и праздничные дни. У меня есть таблица LeadTime, где хранится количество времени (в днях), в течение которого производится изделие. Наконец, у меня есть таблица под названием «Заказ», в которой хранится информация о товаре и дате заказа.

Можно ли запросить, когда продукт будет закончен без использования хранимых процедур или циклов?

Например:

  • OffDays имеет 2008-01-10, 2008-01-11, 2008-01-14.
  • LeadTime имеет 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-запрос.

Редактировать (как я это делаю сейчас): Мой текущий метод состоит в том, что у меня есть дополнительный столбец в таблице заказов для хранения вычисленной даты, затем запланированное задание / задание хрон выполняет вычисления для всех заказов каждый час. Это не идеально по нескольким причинам.

Ответы [ 8 ]

2 голосов
/ 18 сентября 2008

Вы можете заранее составить таблицу рабочих дней.

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 голосов
/ 18 сентября 2008

Лучший подход - использовать таблицу календаря.

См. 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.

1 голос
/ 18 сентября 2008

вот один из способов - использование функции 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
1 голос
/ 18 сентября 2008

Просто посчитайте это в коде приложения ... намного проще, и вам не придется писать действительно уродливый запрос в вашем sql

0 голосов
/ 18 сентября 2008

Это синтаксис 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), поэтому вы должны позаботиться о том, чтобы вставлять выходные дни вовремя. начало в выходные дни.

0 голосов
/ 18 сентября 2008

Одним способом (без создания другой таблицы) является использование своего рода потолочной функции: для каждого устаревшего времени выясните, сколько «дат» предшествует ему относительно даты заказа в подзапросе. Затем возьмите наибольшее число, которое меньше, чем время выполнения заказа. Используйте соответствующую ей дату плюс остаток.

Этот код может быть специфичен для 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 голосов
/ 18 сентября 2008

Хм ... одним из решений может быть сохранение таблицы дат со смещением на основе количества нерабочих дней с начала года Скажем, Ян. 2 выходной день. 1/1/08 будет иметь смещение 1 (или 0, если вы хотите начать с 0). 1/3/08 будет иметь смещение 2, потому что счет пропускает 1/2/08. Оттуда его простой расчет. Получите смещение даты заказа, добавьте время выполнения заказа, а затем просмотрите рассчитанное смещение, чтобы получить дату окончания.

0 голосов
/ 18 сентября 2008

Почему вы не используете петли?

// некоторый псевдокод

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

это кажется слишком простой функцией, чтобы пытаться найти нецикличный путь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...