유효 날짜를 계산하는 비반복/비루핑 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/92699

  •  01-07-2019
  •  | 
  •  

문제

주말과 공휴일 날짜가 보관되는 OffDays라는 테이블이 있습니다.제품을 제조하는 데 걸리는 시간(일수)이 저장되는 LeadTime이라는 테이블이 있습니다.마지막으로 제품과 주문 날짜가 보관되는 Order라는 테이블이 있습니다.

저장 프로시저나 루프를 사용하지 않고 제품 제조가 언제 완료되는지 쿼리할 수 있나요?

예를 들어:

  • OffDays에는 2008-01-10, 2008-01-11, 2008-01-14가 있습니다.
  • 리드타임은 제품 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 쿼리만 제어할 수 있는 타사 보고 도구입니다.

편집(지금 내가 어떻게 하고 있는지):현재 방법은 계산된 날짜를 보관하기 위해 주문 테이블에 추가 열이 있는 다음 예약된 작업/크론 작업이 매 시간마다 모든 주문에 대해 계산을 실행하는 것입니다.이는 여러 가지 이유로 이상적이지 않습니다.

도움이 되었습니까?

해결책

작업일 테이블을 미리 생성할 수 있습니다.

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일은 쉬는 날이다.2008년 1월 1일의 오프셋은 1(또는 0에서 시작하려는 경우 0)입니다.1/3/08은 카운트가 1/2/08을 건너뛰기 때문에 오프셋이 2가 됩니다.거기에서 간단한 계산이 이루어집니다.주문 날짜의 오프셋을 가져오고 리드 타임을 추가한 다음 계산된 오프셋을 조회하여 종료 날짜를 가져옵니다.

한 가지 방법은 (다른 테이블을 만들지 않고) 일종의 천장 기능을 사용하는 것입니다.각 할인 날짜에 대해 하위 쿼리에서 주문 날짜를 기준으로 그 앞에 오는 "날짜"가 몇 개인지 알아보세요.그런 다음 리드타임보다 짧은 가장 높은 숫자를 선택합니다.해당 날짜와 나머지 날짜를 사용하세요.

이 코드는 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