طريقة غير تكرارية / غير تكرارية لحساب تاريخ النفاذ؟

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

  •  01-07-2019
  •  | 
  •  

سؤال

لدي جدول يسمى OffDays، حيث يتم الاحتفاظ بتواريخ عطلات نهاية الأسبوع والعطلات.لدي جدول يسمى LeadTime حيث يتم تخزين مقدار الوقت (بالأيام) لتصنيع المنتج.أخيرًا لدي جدول يسمى الطلب حيث يتم الاحتفاظ بالمنتج وتاريخ الطلب.

هل من الممكن الاستعلام عن موعد الانتهاء من تصنيع المنتج دون استخدام الإجراءات أو الحلقات المخزنة؟

على سبيل المثال:

  • OffDays لديه 2008-01-10، 2008-01-11، 2008-01-14.
  • لدى LeadTime 5 للمنتج 9.
  • يحتوي الطلب على 09-01-2008 للمنتج 9.

الحساب الذي أبحث عنه هو هذا:

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

أتساءل عما إذا كان من الممكن الحصول على استعلام يعود بتاريخ 16-01-2008 دون الحاجة إلى استخدام إجراء مخزن، أو حسابه في رمز التطبيق الخاص بي.

تحرير (لماذا لا توجد عمليات/حلقات مخزنة):السبب وراء عدم قدرتي على استخدام الإجراءات المخزنة هو أنها غير مدعومة من قاعدة البيانات.يمكنني فقط إضافة جداول/بيانات إضافية.التطبيق عبارة عن أداة تقارير تابعة لجهة خارجية حيث يمكنني فقط التحكم في استعلام 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 
    )

امل ان يساعد،

ر.ب.

فقط احسبها في رمز التطبيق ...أسهل بكثير ولن تضطر إلى كتابة استعلام قبيح حقًا في 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