2017-05-29 49 views
1

我有一個名爲subscriptions的數據庫表。表中的每一行都有一個created_at的日期,從今天起,每個月(之後)都必須開具發票。MySQL獲取月度發票的記錄,其中考慮到月份的不同長度

因此,在2017-05-29上,所有具有created_at day == '29'的訂閱都必須開具發票,無論月份或年份如何。所以我想這個:

SELECT * FROM subscriptions WHERE DAY(created_at) = DAY(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) 

但在這種情況下,我遇到麻煩時,與上月有30天,因爲30日,則返回30,但在31日也返回30.因此,所有訂閱與created_at一天'30'將開具發票兩次。 2月份也會出現問題。

另一個問題是相反,如果4月份沒有第31天,我將如何發票2017-03-31。

我可以在PHP中進行多重檢查,並檢查是否已經開具了該月的發票等等。但是我不知道我是否可以用MySQL解決這個問題。

我創建了一個sqlfiddle的例子,根據上面的查詢有些日期將會失敗。

# Create subscription table 
CREATE TABLE `subscription` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `company` varchar(100) DEFAULT NULL, 
    `created_at` datetime DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

# Fill subscription table 
INSERT INTO `subscription` (`id`, `company`, `created_at`) 
VALUES 
    (1, 'Acme', '2017-04-15 09:56:00'), 
    (2, 'Equmbo', '2017-02-28 10:00:00'), 
    (3, 'Megajo', '2017-03-31 08:10:34'), 
    (4, 'Astrotude', '2017-04-30 08:10:49'); 

# This is my base query for monthly invoice 
SELECT * FROM subscription WHERE DAY(created_at) = DAY(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)); 

# On 28th March, also fine 
SELECT * FROM subscription WHERE DAY(created_at) = DAY(DATE_SUB('2017-03-28', INTERVAL 1 MONTH)); 

# But on 29th March, Equmbo is invoice again 
SELECT * FROM subscription WHERE DAY(created_at) = DAY(DATE_SUB('2017-03-29', INTERVAL 1 MONTH)); 

# On 30st April, Astrotude AND Megajo must be invoiced. Only Astrotude returns. 
SELECT * FROM subscription WHERE DAY(created_at) = DAY(DATE_SUB('2017-04-30', INTERVAL 1 MONTH)); 
+1

這樣的事情如何:如果LAST_DAY()= CURDATE()目標所有日期的值大於今天的數字? ...我會看看我是否可以把它變成一個答案(除非有人擊敗我)。 – mickmackusa

+0

@mickmackusa,我明白你要去哪裏,但我不知道如何完成它! – Timo002

+0

請參閱https://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-a-very-simple-sql-查詢 – Strawberry

回答

2
WHERE 
    /* don't invoice new subs from this month */ 
    LAST_DAY(created_at)<LAST_DAY(CURDATE()) 
    AND 
    (
    /* exactly match sub's day value with today's day value */ 
    DAY(created_at)=DAY(CURDATE()) 
    OR 
    /* on last day of month, match sub's day if greater than today's day */ 
    (CURDATE()=LAST_DAY(CURDATE()) AND DAY(created_at)>DAY(CURDATE())) 
    ) 

CURDATE()LAST_DAY()返回一個完整的日期字符串(YYYY-MM-DD)。

DAY()返回整數(d或dd)。

+0

這真的很棒!最後一部分是最困難的,我無法弄清楚自己! – Timo002

+1

@ Timo002我需要做一個小的改正/改進。最終條件不需要檢查'> ='。 'OR'之前的'DAY()= DAY()'已經檢查了'='。在「OR」後面再次檢查'='是沒有意義的。我已經更新了我的答案,並且我建議在您的項目中進行這種微調。 – mickmackusa