I'm new here, so I hope I'll do everything right.
I'm trying to get a table on MySQL where I would get the sum of orders from a certain productline per month, and then comparing it with the orders from the same month in the previous year, to calculate the delta.
For that i need to group by each orders with the productline and month, and also month of last year. Of course, sometimes, there are no orders in certain months for certain productlines.
The tables columns would look like this :
ProductLine - MONTH(date) - SUM(ORDERS) - LAST_YEAR-MONTH(date) - LAST_YEAR_SUM(ORDERS) - DELTA
I can get current year table and last year table separately in views, but I can't effectively join them without messing the numbers, maybe because of the group-by clauses
Here what I tried (I seriously tried a 100 possibilities) :
Creating two views and left join the last-year table
Creating a subquery for the LAST_YEAR orders
Here is an example of what I tried, (forget about the delta, that part is the easy one) :
SELECT p.productLine pL
, MONTH(o.orderDate) oD
, SUM(od.quantityOrdered)
, MONTH(date_sub(o.orderDate, interval 1 year)) previous_year
, (SELECT SUM(orderdetails.quantityOrdered)
FROM orderdetails
LEFT
JOIN orders
ON orderdetails.orderNumber = orders.orderNumber
LEFT
JOIN products
on products.productCode = orderdetails.productCode
WHERE orderDate in (previous_year)
) previous_order
FROM products p
JOIN orderdetails od
ON p.productCode = od.productCode
JOIN orders o
ON od.orderNumber = o.orderNumber
GROUP
BY pL
, oD
, previous_year
ORDER
BY oD DESC
If you are running MySQL 8.0, you can use window functions and a range specification:
select
p.productline,
date_format(o.orderdate, '%Y-%m-01') as ordermonth
sum(od.quantityordered) quantityordered,
max(sum(od.quantityordered)) over(
partition by productline
order by date_format(o.orderdate, '%Y-%m-01')
range between interval 1 year preceding and interval 1 year preceding
) previousquantityordered
from products as p
join orderdetails as od on p.productcode = od.productcode
join orders as o on od.ordernumber = o.ordernumber
group by p.productline, ordermonth
order by ordermonth desc
For simplified comparison based on a calendar year comparison, I would start Jan 1 of the prior year to now of current year. Doing this, you would get Jan 2019/2020, Feb 2019/2020, etc to current month. Since Oct, Nov, Dec have not happened yet, you could add in an "and month( order date ) is LESS than the current month we are in" since it did not happen yet this year to compare against
select o.*
from orders o
where o.orderDate >= '2019-01-01'
AND month( o.orderDate ) < month( curdate())
Now that the underlying record base is simplified, lets try to get your details.
To differentiate between last year and current, I am doing a conditional SUM(). So within the sum is a CASE/WHEN test. If the year of the order is same as current year, sum its qty, otherwise 0. Likewise, if less than current year, put into the last year qty bucket total. This way, you get all product lines, even those that may have sold last year and not this and vice-versa too. This would result in zero values in those respective columns. But obviously below is a much more simplified query to follow.
select
p.productLine,
MONTH(o.orderDate) OrderMonth,
SUM( case when year( o.orderDate ) = year( curdate())
then od.quantityOrdered else 0 end ) CurYearQty,
SUM( case when year( o.orderDate ) < year( curdate())
then od.quantityOrdered else 0 end ) LastYearQty
from
orders o
JOIN orderdetails od
on o.orderNumber = od.orderNumber
JOIN products p
on od.productCode = p.productCode
where
o.orderDate >= '2019-01-01'
AND month( o.orderDate ) < month( curdate())
group by
MONTH(o.orderDate),
p.productLine
order by
MONTH(o.orderDate),
p.productLine
Related
Based on current date which is march 17 2022 (thrusday) i am trying to find the same day last week(10th march 2022)(thrusday) sale cumulative product wise
i used following query but unfortunately had an issue with duplicates
select t1.order_date,t1.product as product, t1.total_order_demand as 'order_demand', t2.total_order_demand as 'lastweek same_day demand'
from(
SELECT date(od.CREATED_DATE) as order_date, p.name as product, Sum(od.QUANTITY) as total_order_demand
from order_detail od left join product p on p.id=od.PRODUCT
where date(od.CREATED_DATE)<=CURDATE() and date(od.CREATED_DATE)>=date(curdate()) - interval 1 WEEK
and od.STATUS='Y' and (od.ORDER_END_DATE>=od.ORDER_START_DATE or od.ORDER_END_DATE is null) group by date(od.created_date)
) t1
left join(
SELECT date(od.CREATED_DATE) as order_date, p.name as product, Sum(od.QUANTITY) as total_order_demand
from order_detail od left join product p on p.id=od.PRODUCT
where date(od.CREATED_DATE)<=CURDATE() and date(od.CREATED_DATE)>=date(curdate()) - interval 1 WEEK
and od.STATUS='Y' and (od.ORDER_END_DATE>=od.ORDER_START_DATE or od.ORDER_END_DATE is null) group by date(od.created_date)
) t2
on WEEKDAY(DATE_SUB(CURDATE() , INTERVAL 1 WEEK)) = WEEKDAY(CURDATE()) and t1.product =t2.product
where t1.order_date >= date(curdate()) - interval 1 WEEK group by t1.product,t2.product
order by t1.order_date desc ;
I am working on a query to get total orders placed in last year by some specific customer (id = 329) using two tables viz. orders and calendar (this is to get zero fill values when no corresponding record exists) tables.
orders table:
calendar table:
query:
SELECT c.datefield AS date
, IFNULL((SELECT COUNT(o.order_date) FROM orders
WHERE o.customer_id = 329 LIMIT 1), 0) AS TotalOrders
FROM calendar AS c
LEFT
JOIN orders AS o
ON c.datefield = DATE(o.order_date)
WHERE YEAR(c.datefield) = YEAR(CURRENT_DATE - INTERVAL 1 YEAR)
GROUP
BY date
, o.customer_id
ORDER
BY date ASC
output:
From above picture, you can see that 2 orders were placed by customer 329 on 2020-01-02. But two extra rows for the same date with 0 order are retrieved. I guess it might be probably from customer 6882, and 670. This is wrong! I only need to fetch orders of customer 329.
How can I exclude these unwanted rows in my query and retrieve total orders by only customer 329?
To count total orders placed monthly in last year by a particular customer, use this query:
SELECT MONTHNAME(c.datefield) AS Month,
YEAR(CURRENT_DATE - INTERVAL 1 YEAR) AS Year,
IFNULL(o.TotalOrders, 0) AS Orders
FROM calendar AS c
LEFT JOIN (
SELECT MONTH(o.order_date) AS Month,
YEAR(o.order_date) AS Year,
COUNT(o.customer_id) AS TotalOrders
FROM orders AS o
WHERE YEAR(o.order_date) = YEAR(CURRENT_DATE - INTERVAL 1 YEAR) AND o.customer_id = 329
GROUP BY Month) AS o
ON MONTH(c.datefield) = o.Month
GROUP BY MONTH(c.datefield)
I have restaurants and orders tables, in orders table I have restaurant_id, status and date fields - for each day I save one row in orders table. If for some day there is no order - it means the is no row for that day in orders table.
I want to show on the calendar the data for the current month for each restaurant, according to these 2 separate conditions.
1) in first case show only those restaurants that have at least one free
day during this month(which means for this month at least one date is missing in orders table).
2) in second case show only those restaurants that are free for today
(which means there is no row for today in orders table)
for both cases, if the condition is satisfied, I should fetch all the orders for the current month - this is the tricky part.
The usual anti-join with left, or inner join do not give the desired result.
Thanks.
edit
outputs should be like this
1) http://img826.imageshack.us/img826/3114/e6zt.png
2) http://img13.imageshack.us/img13/6397/44l0.png
This is all the listings for this month for all restaurants that are free today:
SELECT r.`id`, r.`name`, o.`date`, o.`status`, o.`id` order_id
FROM restaurants r
INNER JOIN orders o
ON r.id = o.restaurant_id
LEFT JOIN
( SELECT DISTINCT o2.Restaurant_ID
FROM orders o2
WHERE o2.date = DATE(CURRENT_TIMESTAMP)
) o2
ON r.id = o2.restaurant_id
WHERE o.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01')
AND o2.Restaurant_ID IS NULL;
This simply gets all the restaurants with bookings today (subquery o2), then excludes these restaurants:
AND o2.Restaurant_ID IS NULL;
This is all the listings for this month for all restaurants that have at least one free day this month:
SELECT r.`id`, r.`name`, o.`date`, o.`status`, o.`id` order_id
FROM restaurants r
INNER JOIN orders o
ON r.id = o.restaurant_id
AND o.date BETWEEN '2013-08-10' AND '2013-08-31'
INNER JOIN
( SELECT o2.Restaurant_ID
FROM orders o2
WHERE o2.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o2.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01')
GROUP BY o2.Restaurant_ID
HAVING COUNT(DISTINCT o2.Date) < DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
) o2
ON r.id = o2.restaurant_id
WHERE o.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01');
The trick is to get the number of days in this month:
DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
Then limit the results to restaurant_id's that have less bookings than this:
HAVING COUNT(DISTINCT o2.Date) < DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
Example of Both on SQL Fiddle
I have 3 tables:
doctors (id, name) -> has_many:
patients (id, doctor_id, name) -> has_many:
health_conditions (id, patient_id, note, created_at)
Every day each patient gets added a health condition with a note from 1 to 10 where 10 is a good health (full recovery if you may).
What I want to extract is the following 3 statistics for the last 30 days (month):
- how many patients got better
- how many patients got worst
- how many patients remained the same
These statistics are global so I don't care right now of statistics per doctor which I could extract given the right query.
The trick is that the query needs to extract the current health_condition note and compare with the average of past days (this month without today) so one needs to extract today's note and an average of the other days excluding this one.
I don't think the query needs to define who went up/down/same since I can loop and decide that. Just today vs. rest of the month will be sufficient I guess.
Here's what I have so far which obv. doesn't work because it only returns one result due to the limit applied:
SELECT
p.id,
p.name,
hc.latest,
hcc.average
FROM
pacients p
INNER JOIN (
SELECT
id,
pacient_id,
note as LATEST
FROM
health_conditions
GROUP BY pacient_id, id
ORDER BY created_at DESC
LIMIT 1
) hc ON(hc.pacient_id=p.id)
INNER JOIN (
SELECT
id,
pacient_id,
avg(note) AS average
FROM
health_conditions
GROUP BY pacient_id, id
) hcc ON(hcc.pacient_id=p.id AND hcc.id!=hc.id)
WHERE
date_part('epoch',date_trunc('day', hcc.created_at))
BETWEEN
(date_part('epoch',date_trunc('day', hc.created_at)) - (30 * 86400))
AND
date_part('epoch',date_trunc('day', hc.created_at))
The query has all the logic it needs to distinguish between what is latest and average but that limit kills everything. I need that limit to extract the latest result which is used to compare with past results.
Something like this assuming created_at is of type date
select p.name,
hc.note as current_note,
av.avg_note
from patients p
join health_conditions hc on hc.patient_id = p.id
join (
select patient_id,
avg(note) as avg_note
from health_conditions hc2
where created_at between current_date - 30 and current_date - 1
group by patient_id
) avg on t.patient_id = hc.patient_id
where hc.created_at = current_date;
This is PostgreSQL syntax. I'm not sure if MySQL supports date arithmetics the same way.
Edit:
This should get you the most recent note for each patient, plus the average for the last 30 days:
select p.name,
hc.created_at as last_note_date
hc.note as current_note,
t.avg_note
from patients p
join health_conditions hc
on hc.patient_id = p.id
and hc.created_at = (select max(created_at)
from health_conditions hc2
where hc2.patient_id = hc.patient_id)
join (
select patient_id,
avg(note) as avg_note
from health_conditions hc3
where created_at between current_date - 30 and current_date - 1
group by patient_id
) t on t.patient_id = hc.patient_id
SELECT SUM(delta < 0) AS worsened,
SUM(delta = 0) AS no_change,
SUM(delta > 0) AS improved
FROM (
SELECT patient_id,
SUM(IF(DATE(created_at) = CURDATE(),note,NULL))
- AVG(IF(DATE(created_at) < CURDATE(),note,NULL)) AS delta
FROM health_conditions
WHERE DATE(created_at) BETWEEN CURDATE() - INTERVAL 1 MONTH AND CURDATE()
GROUP BY patient_id
) t
I have read almost 10+ questions related to mine but no one worked in my case. As i have 3 tables in my DB and i am trying to calculate sale from them with respect to time (Yearly sale). where i need to GROUP BY my query by date_added. In MYSQL it worked fine and give me fine result but in redshift i am stuck.
MYSQL QUERY:
SELECT
MONTHNAME(o.date_added) AS MONTH,
YEAR(o.date_added) AS YEAR,
COUNT(o.order_id) AS orders,
FROM
order o
LEFT JOIN(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON
op.order_id = o.order_id
LEFT JOIN(
SELECT
order_id,
SUM(IF(CODE = 'coupon', VALUE, 0)) AS coupon
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON
ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= DATE_ADD(NOW(), INTERVAL - 24 MONTH) AND DATE(o.date_added) <=
DATE(NOW())) AND(o.order_status_id = '22' OR o.order_status_id = '23')
GROUP BY
MONTH(o.date_added),
YEAR(o.date_added)
ORDER BY
date_added ASC
LIMIT 0, 25
This MYSQL query working very fine but when i convert it to RedShift's POSTGRE format it gives me error of Invalid operation: column "o.date_added" must appear in the GROUP BY clause or be used in an aggregate function;
POSTGRES Query:
SELECT
EXTRACT(MONTH FROM o.date_added) AS month,
EXTRACT(YEAR FROM o.date_added) AS year,
COUNT(o.order_id) AS orders
FROM
orders o
LEFT JOIN
(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON op.order_id = o.order_id
LEFT JOIN
(
SELECT
order_id,
SUM(CASE
WHEN CODE = 'coupon' THEN VALUE
ELSE 0
END) AS coupon
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= now() + interval '-24 month'
AND DATE(o.date_added) <= DATE(NOW())
)
AND (
o.order_status_id = '22'
OR o.order_status_id = '23'
)
GROUP BY
(EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added))
ORDER BY
o.date_added ASC LIMIT 25
Is there any syntax error in postgre query and also why i need to add o.date_added in GROUP BY
Your ORDER BY clause has o.date_added in it but your actual result set does not have it. The ORDER BY is done after the query is done.
You can use:
ORDER BY month asc, year asc LIMIT 25
Also, you can remove the extra parentheses from the GROUP BY:
GROUP BY EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added)
DB-Fiddle
See Redshift documentation for use of now() function. Use getdate() instead, as the now() seems to be deprecated.
A column in an ORDER BY clause in a query containing GROUP BY works as if it were mentioned in the SELECT clause.
You have
SELECT
MONTHNAME(o.date_added) AS MONTH,
YEAR(o.date_added) AS YEAR,
COUNT(o.order_id) AS orders,
FROM
order o
...
GROUP BY
MONTH(o.date_added),
YEAR(o.date_added)
ORDER BY
date_added ASC
LIMIT 0, 25
In MySQL this takes advantage of its notorious nonstandard handling of GROUP BY (read this. https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html)
What you want, to get a query that works in multiple DBMSs, is this instead.
ORDER BY
MONTH(o.date_added) ASC,
YEAR(o.date_added) ASC
Adding o.date_added to your GROUP BY clause is incorrect, because you are grouping not by the entire date, but by the month and year of the date.
I found the ERROR.I used SQL Workbench and get the Error about NOW() function that i was using in my POSTGRE query.
Simply, i juts replaced NOW() with CURRENT_DATE and things worked for me. Also, i get Error for LIMIT 0, 25 but in POSTGRE , they allow LIMIT 25 [OFFSET n] .
Now my query looks like:
SELECT
EXTRACT(MONTH FROM o.date_added) AS month,
EXTRACT(YEAR FROM o.date_added) AS year,
COUNT(o.order_id) AS orders
FROM
orders o
LEFT JOIN
(
SELECT
op.order_id,
SUM(op.quantity) AS op_qty,
SUM(op.total) AS total,
SUM(op.cost) AS cost
FROM
order_product op
GROUP BY
op.order_id
) op
ON op.order_id = o.order_id
LEFT JOIN
(
SELECT
order_id
FROM
order_total
WHERE
1
GROUP BY
order_id
) ot
ON ot.order_id = o.order_id
WHERE
(
DATE(o.date_added) >= CURRENT_DATE + interval '-24 month'
AND DATE(o.date_added) <= DATE(CURRENT_DATE)
)
GROUP BY EXTRACT(MONTH FROM o.date_added), EXTRACT(YEAR FROM o.date_added)
ORDER BY year ASC, month ASC LIMIT 25
NOTE: IF YOU ARE WORKING ON REDSHIFT. IT DO NOT BERIEF ABOUT ERROR. I RECOMMEND TO USE SQL WORKBENCH. IT EXPLAIN ABOUT ERROR.