2014-09-05 35 views
0

對不起,關於標題 - 我無法想出一個更好的。我有一個發票系統,我需要計算在給定的日期間隔內哪些客戶協議(合同)將開具發票。合約運行的時間段由start_dateend_date日期列定義,其中持續時間始終以月爲單位(即自然月數)。每個invoice_interval月份都會對客戶開具發票,這是每個合同定義的。如果此列包含數字3,則客戶每三個月發票一次。我需要的是瞭解哪些合同/協議將在兩個日期之間開具發票。日期將是一個月的21日和下個月的20日,都包括 - 例如,在2014-05-212014-06-20之間。查詢在給定日期間隔內要開具發票的客戶

基於該start_dateend_date,這可以通過添加invoice_interval個月到start_date的給定次數,例如可以計算與DATE_ADD()。如果這些日期中的任何一個在X和Y之間,則存在匹配。問題是我不知道如何用SQL查詢來做這件事,並且沒有循環。我做了類似的事情,當我需要知道,如果一個合同將在特定月份開具發票(2014年6月在這個例子中):

SELECT * 
FROM contracts 
WHERE start_date <= '2014-06-01' 
AND end_date >= '2014-06-30' 
AND (PERIOD_DIFF('2014-06-01', DATE_FORMAT(start_date, '%Y%m')) % invoice_interval) = 0 

這裏的邏輯是start_date和月份之間檢查有無差異月我檢查除以invoice_interval沒有休息 - 通過使用模數。如果是這樣,那麼合同應該在當月開具發票。例如,

因此,我需要更改上述查詢以確定合同是否應在2014-05-212014-06-20之間開具發票。假設合同開始於2014-01-14,並在2015-01-14結束,並且每3個月開具發票。我想查找將在2014-03-212014-04-20之間開具發票的合同。此給定合同應在結果集中,因爲它將在2014-04-14,2014-07-142014-10-14上開具發票。我怎樣才能做到這一點?在上面的SQL查詢中更爲可取。

我希望我解釋得很好。先謝謝你!

回答

0

爲了簡化事情讓我們寫函數,該函數計算整月兩個日期間數:

-- how many full months between d1 and d2 
CREATE FUNCTION month_count(d1 DATE, d2 DATE) 
    RETURNS INT 
    DETERMINISTIC NO SQL 
BEGIN 
    DECLARE count INT; 

    SET count = PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM d1), EXTRACT(YEAR_MONTH FROM d2)); 
    -- correction 
    RETURN IF(DAY(d1) >= DAY(d2), count, count-1); 
END; 

查詢則:

SELECT * 
FROM contracts 
WHERE start_date <= @y AND end_date >= @x 
    -- check invoice interval 
    AND month_count(@y, start_date) > 0 
    AND (month_count(@y, start_date) % invoice_interval) = 0 
    -- check what invoice date is before contract end 
    AND DATE_ADD(start_date, INTERVAL month_count(@y, start_date) MONTH) <= end_date 

更換@x與間隔的開始日期(例如:「2014 -05-21')和@y,間隔結束日期(例如:'2014-06-20')。

請注意,此解決方案正常工作的只有:

  1. 有整整一個月之間有兩個發票間隔時間(如在你的問題中指定)

  2. 間隔結束日期滿足條件: (日中的月份)< = 28

要解決第二個問題,請使用修改的函數(此函數計數爲1在日期'2014-02-28'和'2014-01-31'之間的月份):

-- how many full months between d1 and d2 
CREATE FUNCTION month_count(d1 DATE, d2 DATE) 
    RETURNS INT 
    DETERMINISTIC NO SQL 
BEGIN 
    DECLARE count INT; 

    SET count = PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM d1), EXTRACT(YEAR_MONTH FROM d2)); 
    -- correction 
    RETURN IF(DAY(d1) = DAY(LAST_DAY(d1)) OR DAY(d1) >= DAY(d2), count, count-1); 
END; 
+0

太棒了,謝謝!我已經做了一些調整,以便合約在最後一個月內不會再有一段時間發票。到目前爲止,一切都看起來不錯,但我會繼續測試它。非常感謝您的回答! – Andy0708 2014-09-08 07:49:14