I've been tasked with making output that fetches number of days passed between an order and its shipment, like this:
order_date | orders | Days0 | Days1 | Days7Plus |
---|---|---|---|---|
2022-11-01 | 12 | 9 | 3 | 1 |
2022-11-15 | 22 | 20 | 0 | 2 |
2022-12-02 | 77 | 65 | 5 | 7 |
I'm sure you can imagine example underlying data, where there's an orders table with a unique ID per record, an order date that can share multiple IDs, and each order has its own ship date.
The hard part is counting only business days, which required subtracting weekends and holidays from the date range days. I got that all figured out but it required copy-pasting these ugly sub-queries 7 more times :/ While this can be dynamically generated in other code, I figured there must be a cleaner way, since other people (some non-devs) may be testing or reviewing this, and I'll probably get grief about it.
Here's the query essentially:
# get orders shipped count
SELECT
...
# orders that were shipped x number of days from receipt
SUM(COALESCE(DATEDIFF(shipped, ordered), 0)
- ( # subtract weekend days
5 * (DATEDIFF('2022-12-05', '2022-11-01') DIV 7)
+ MID('0123444401233334012222340111123400012345001234550',
7 * WEEKDAY('2022-11-01') + WEEKDAY('2022-12-05') + 1, 1
)
)
- ( # subtract holidays
SELECT COUNT(`date`) FROM holiday WHERE active = 1
AND `date` BETWEEN '2022-11-01' AND '2022-12-05'
AND DAYOFWEEK(`date`) < 6
)
= 0) AS 0Days, # subsequently 1Days, 2Days, 3Days, etc
...
SUM(COALESCE(DATEDIFF(shipped, ordered), 0)
- ( # subtract weekend days
5 * (DATEDIFF('2022-12-05', '2022-11-01') DIV 7)
+ MID('0123444401233334012222340111123400012345001234550',
7 * WEEKDAY('2022-11-01') + WEEKDAY('2022-12-05') + 1, 1
)
)
- ( # subtract holidays
SELECT COUNT(`date`) FROM holiday WHERE active = 1
AND `date` BETWEEN '2022-11-01' AND '2022-12-05'
AND DAYOFWEEK(`date`) < 6
)
>= 7) AS Days7Plus
FROM orders
WHERE
AND ordered BETWEEN :startDate AND :endDate
GROUP BY CAST(ordered AS DATE)
ORDER BY ordered
I got the MID calculation from https://stackoverflow.com/a/6762805/14744970
I feel pretty proud of getting it all together, but I feel like I'm a small step away from collapsing the redundancy down somehow that I'm not quite understanding.
Note that I don't know if the GROUP BY actually matters with any sort of simplifying of the redundant statements.