SQL select orders depending on status and date - mysql

I have a table order_history that is similar to the following:
+-------------------------------------------------------------+
| order_history_id | order_id | order_status_id | date_addded |
+-------------------------------------------------------------+
| 1 | 1 | 1 | 2014-03-20 |
| 2 | 1 | 2 | 2014-03-21 |
| 3 | 1 | 3 | 2014-03-29 |
| 4 | 2 | 1 | 2014-03-20 |
| 5 | 2 | 2 | 2014-03-21 |
| 6 | 2 | 3 | 2014-04-02 |
| 7 | 3 | 1 | 2014-04-20 |
| 8 | 3 | 2 | 2014-04-21 |
| 9 | 3 | 3 | 2014-04-22 |
+-------------------------------------------------------------+
The order_status represents the status of an order
+-------------------------------+
| order_status_id | name |
+-------------------------------+
| 1 | received |
| 2 | processed |
| 3 | shipped |
+-------------------------------+
what i want to do is to pull out all the orders that have been received before 2014-04-01 but not shipped until after 2014-04-01.
So in this case the query would just return order_id 2 as this is the only order that was received before 2014-04-01 yet shipped after.
I can't even seem to get started... Any help, hints, or pointers much appreciated.

You can do so ,by joining your tables and count the statues shipped for each order by using expression in sum i.e SUM(os.name ='shipped') shipped
SELECT o.*
,SUM(os.name ='shipped') shipped
FROM
orders o
LEFT JOIN orders_status os USING(order_status_id)
WHERE o.date_addded < '2014-04-01'
GROUP BY o.order_id
HAVING shipped =0
Fiddle Demo

You can use INNER JOIN with this, if I get what you really want you can try this:
SELECT DISTINCT order_id
FROM order_history A
INNER JOIN order_status B
ON A.order_status_id = B.order_status_id
WHERE (A.order_Status_id = '1' AND A.date_added < #date) AND (A.order_status_id = '3' AND A.date_added < #date)

SELECT h1.order_id
FROM order_history h1
JOIN order_status s1
ON s1.order_status_id = h1.order_status_id
JOIN order_history h2
ON h2.order_id = h1.order_id
JOIN order_status s2
ON s2.order_status_id = h2.order_status_id
WHERE h1.date_addded < '2014-04-01'
AND s1.name = 'received'
AND h2.date_addded >= '2014-04-01'
AND s2.name = 'shipped';
Note: Too many 'd's in addded

SELECT r.order_id
FROM (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'received'
AND oh.date_addded < '2014-04-01'
) AS r
JOIN (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'shipped'
AND oh.date_addded > '2014-04-01'
) AS s ON (s.order_id = r.order_id)
demo

What about this simple and light query:
SELECT DISTINCT order_id
FROM order_history o1
JOIN order_history o2
ON o1.order_id = o2.order_id
AND o1.order_status_id=1 AND o1.date_added<'2014-04-01'
AND o2.order_status_id=3 AND o2.date_added>'2014-04-01';

Not tested, but try this:
SELECT A.ORDER_ID
FROM ORDER_HISTORY A, ORDER_HISTORY B
WHERE A.ORDER_ID = B.ORDER_ID
AND A.order_status_id = 1
AND A.date_addded < TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD')
AND B.order_status_id = 3
AND B.date_addded > TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD');

Related

How to sum and count Id's of different tables from a Union

I have 3 tables councils, station_levy, market_levy, and the station table is joined to get councils in station_levy.
I need to get data by council sum the number of station_levy + market_levy and get the total amount tendered.
Tables are as follows
councils
---------------+----------+
| council_id | Name |
+--------------+----------+
| 1 | LSK |
---------------+----------+
| 2 | KBW |
---------------+----------+
station_levy
------------------+-------------+-----------------+
| station_levy_id | station_id | amount_tendered |
+-----------------+-------------+-----------------+
| 1 | 3 | 10.00 |
+-----------------+-------------+-----------------+
| 2 | 3 | 10.00 |
+-----------------+-------------+-----------------+
| 3 | 1 | 5.00 |
+-----------------+-------------+-----------------+
(station_id = 1 is found in the LSK council_id=1 And station_id = 3 is found in the KBW council_id=2)
market_levy
------------------+-------------+-----------------+
| market_levy_id | council_id | amount_tendered |
+-----------------+-------------+-----------------+
| 1 | 1 | 5.00 |
+-----------------+-------------+-----------------+
| 2 | 2 | 5.00 |
+-----------------+-------------+-----------------+
| 3 | 1 | 5.00 |
+-----------------+-------------+-----------------+
mysql
SELECT c.council_name, (COUNT(market_levy.market_levy_id)+ COUNT(st.station_levy_id )) count, SUM(amount_tendered) revenue
FROM councils c
JOIN (
(SELECT council_id, amount_tendered,market_levy_id FROM market_levy WHERE transaction_date >= CURDATE() )
UNION ALL
(SELECT station_levy_id , councils.council_id, amount_tendered
FROM station_levy st
JOIN stations ON stations.station_id = st.station_id
JOIN councils ON councils .council_id= stations .council_id
WHERE transaction_datetime >= CURDATE()
)) totalCouncilRevenue USING (council_id)
group by council_id, c.council_name ORDER BY SUM(amount_tendered) DESC
Expected result
------------------+-------------+-----------------+
| council_name | count | revenue |
+-----------------+-------------+-----------------+
| LSK | 3 | 15.00 |
+-----------------+-------------+-----------------+
| KBW | 3 | 25.00 |
+-----------------+-------------+-----------------+
You are confusing columns in your UNION ALL matching council_id with station_levy_id, amount_tendered with council_id, and market_levy_id with amount_tendered.
Then, in your main query you try to access market_levy.market_levy_id and st.station_levy_id, but these columns are not accessible, as you select from a subquery called totalCouncilRevenue, not from tables labelled market_levy and st there.
Your query fixed:
SELECT
c.council_name,
COUNT(*) AS transaction_count,
SUM(amount_tendered) AS revenue
FROM councils c
JOIN
(
SELECT council_id, amount_tendered
FROM market_levy
WHERE transaction_date >= CURDATE()
UNION ALL
SELECT s.council_id, st.amount_tendered
FROM station_levy st
JOIN stations s ON s.station_id = st.station_id
WHERE st.transaction_datetime >= CURDATE()
) totalCouncilRevenue USING (council_id)
GROUP BY council_id, c.council_name
ORDER BY SUM(amount_tendered) DESC;
I prefer aggregating before joining, though:
SELECT
c.council_name,
COALESCE(t1.cnt, 0) + COALESCE(t2.cnt, 0) AS transaction_count,
COALESCE(t1.total, 0) + COALESCE(t2.total, 0) AS revenue
FROM councils c
LEFT JOIN
(
SELECT council_id, SUM(amount_tendered) as total, COUNT(*) as cnt
FROM market_levy
WHERE transaction_date >= CURDATE()
GROUP BY council_id
) t1 USING (council_id)
LEFT JOIN
(
SELECT s.council_id, SUM(st.amount_tendered) as total, COUNT(*) as cnt
FROM station_levy st
JOIN stations s ON s.station_id = st.station_id
WHERE st.transaction_datetime >= CURDATE()
GROUP BY s.council_id
) t2 USING (council_id)
ORDER BY revenue DESC;
Such queries are usually less prone to errors and are sometimes faster, because they may be able to use indexes more efficiently.

Getting wrong sum when using group by with inner join

With reference to this question (How to get the sum in a joined table when using group by - getting wrong results) I have two tables orders and order_items. I need to group the results by days. But I also need to get the sum of energy_used for each day from another table. When I try that using a join, I get wrong order_sum for each day (they are not being summed up). Not sure what I am doing wrong.
I would like to get for each day
the sum of order_items.energy_used for all orders created that day
the sum of orders.order_sum for all orders created that day
the created_at and order_sum that correspond to the latest order created on that day
Here is my orders table
+----+-----------+---------+---------------------+
| id | order_sum | user_id | created_at |
+----+-----------+---------+---------------------+
| 1 | 25.13 | 7 | 2020-01-25 09:13:00 |
| 2 | 10.00 | 7 | 2020-01-25 15:23:00 |
| 3 | 14.00 | 5 | 2020-01-26 10:14:00 |
| 4 | 35.00 | 1 | 2020-01-27 11:13:00 |
+----+-----------+---------+---------------------+
And here is my order_items table
+----+----------+-------------+---------------------+
| id | order_id | energy_used | created_at |
+----+----------+-------------+---------------------+
| 1 | 1 | 65 | 2020-01-25 09:13:00 |
| 2 | 1 | 12 | 2020-01-25 09:13:00 |
| 3 | 2 | 70 | 2020-01-26 10:14:00 |
| 4 | 2 | 5 | 2020-01-26 10:14:00 |
| 5 | 3 | 0 | 2020-01-27 11:13:00 |
+----+----------+-------------+---------------------+
And this is the desired result that I am trying to achieve
+---------------+-----------------+-------------------+---------------------+----------------+
| date_of_month | total_order_sum | total_energy_used | last_order_date | last_order_sum |
+---------------+-----------------+-------------------+---------------------+----------------+
| 2020-01-25 | 35.13 | 77 | 2020-01-25 09:13:00 | 25.13 |
| 2020-01-26 | 14.00 | 75 | 2020-01-26 10:14:00 | 14.00 |
| 2020-01-27 | 35.00 | 0 | 2020-01-27 11:13:00 | 35.00 |
+---------------+-----------------+-------------------+---------------------+----------------+
And here is the query that I have tried but I'm getting wrong results, the order_sum is not being calculated correctly. It is showing the same as last_order_sum
select
date(o.created_at) date_of_month,
i.total_energy_used,
o.created_at last_order_date,
o.order_sum last_order_sum,
sum(order_sum) as total_order_sum
from orders o
inner join (
select date(o1.created_at) date_of_month, sum(i1.energy_used) total_energy_used
from orders o1
inner join order_items i1 on o1.id = i1.order_id
group by date(o1.created_at)
) i on i.date_of_month = date(o.created_at)
where o.created_at = (
select max(o1.created_at)
from orders o1
where date(o1.created_at) = date(o.created_at)
)
Here is a fiddle:
https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=92b8cc2920ad9f7a7cdd56bded5a3bf2
Always join tables together on their relationships (in this case orders.id with order_items.order_id) and then group. to avoid duplicating order_sums for multiple order_items when joining, first group order_items by order_id.
select
date(o.created_at) date_of_month,
sum(i.total_energy_used),
max(o.created_at),
sum(order_sum) as total_order_sum
from orders o
inner join (
select order_id, sum(total_energy_used) total_energy_used
from order_items i
group by order_id
) i on o.id = i.order_id
group by date(o.created_at)
from this point onwards you can do a join again on orders with max(o.created_at) to get the order_sum of the last order.
moral of the story: keep an eye on your granularity.
Your problem is that you are selecting from orders, where you actually want an aggregate of orders by date. So select from two aggregtating subqueries that you join. Only problem is last_order_sum, which we can select in a further subquery, once we know the last order date.
select
order_date,
o.total_order_sum,
oi.total_energy_used,
o.last_order_date,
(
select order_sum
from orders last_order
where lastorder.created_at = o.last_order_date
) as last_order_sum
from
(
select
date(created_at) as order_date,
sum(order_sum) as total_order_sum,
max(created_date) as last_order_date
from orders
group by date(created_at)
) o
inner join
(
select
date(created_at) as order_date,
sum(energy_used) as total_energy_used
from order_items
group by date(created_at)
) oi using(order_date)
order by order_date;
What you are asking and what you show us in output are not correlated. Assuming that it is a typo:
select so.dtDay as date_of_month, so.order_sum as total_order_sum,
eu.energy_used as total_energy_used,
o.created_at as last_order_date,
o.order_sum as last_order_sum
from (
select left(created_at,10) as dtDay, sum(order_sum) as order_sum, max(id) as last_insert_id
from orders
group by left(created_at,10)
order by created_at
) so
inner join orders o on o.id = so.last_insert_id
left join (select left(created_at,10) as dtDay, sum(energy_used) as energy_used
from order_items
group by left(created_at,10)) eu on so.dtDay = eu.dtDay;
DBFiddle

MySql LEFT JOIN with COUNT

I have two tables, customers and sales. I want to count sales for each customer and create a table of sales per month for each store.
I would like to produce something like;
------------------------------
month | customers | sales |
------------------------------
1/2013 | 5 | 2 |
2/2013 | 21 | 9 |
3/2013 | 14 | 4 |
4/2013 | 9 | 3 |
but I am having trouble getting the sales count to be correct when using the following;
SELECT CONCAT(MONTH(c.added), '/', YEAR(c.added)), count(c.id), count(s.id)
FROM customers c
LEFT JOIN sales s
ON s.customer_id = c.id AND MONTH(c.added) = MONTH(s.added) AND YEAR(c.added) = YEAR(s.added)
WHERE c.store_id = 1
GROUP BY YEAR(c.added), MONTH(c.added);
Customers table;
-------------------------------
id | store_id | added |
-------------------------------
1 | 1 |2013-02-01 |
2 | 1 |2013-02-02 |
3 | 1 |2013-03-16 |
sales table;
---------------------------------
id | added | customer_id |
---------------------------------
1 | 2013-02-18 | 3 |
2 | 2013-03-02 | 2 |
3 | 2013-03-16 | 3 |
Can anyone help here?
thanks
(Updated) The existing query will only count sales made in the same month that the customer was added. Try this, instead:
SELECT CONCAT(MONTH(sq.added), '/', YEAR(sq.added)) month_year,
sum(sq.customer_count),
sum(sq.sales_count)
FROM (select s.added, 0 customer_count, 1 sales_count
from customers c
JOIN sales s ON s.customer_id = c.id
WHERE c.store_id = 1
union all
select added, 1 customer_count, 0 sales_count
from customers
WHERE store_id = 1) sq
GROUP BY YEAR(sq.added), MONTH(sq.added);
SELECT c.* , s.sales_count<br>
FROM customers c<br>
LEFT JOIN (SELECT customer_id, count(id) as sales_count FROM sales GROUP BY customer_id) s on c.id=s.customer_id<br>
WHERE c.store_id = 1<br>

Joining tables and making count operation - MySQL

I have those tables:
Members
---------------------------
MemberID | Name |.....
1
2
3
4
---------------------------
RentedMovies
---------------------------
MemberID | MovieID | DateOfLease | ReturnDate | .....
1 | 1 | 2012-12-20 | 2013-01-05
1 | 2 | 2012-12-15 | 2012-12-30
1 | 3 | 2012-12-16 | 2013-01-06
2 | 1 | 2012-12-17 | 2012-12-18
2 | 4 | 2012-12-18 | 2013-01-05
3 | 1 | 2012-12-19 | 2013-01-04
I need to get this:
--------------------------------------------------------
MemberID | NumberOfRentedMovies | ReturnData < curdate())
1 | 3 | 1
2 | 2 | 1
3 | 1 | 0
4 | 0 | 0
---------------------------------------------------------
And i used next code:
SELECT Members.MemberID,
COUNT(rented.MemberID) AS NumberOfRentedMovies,
COUNT(notTakenBackOnTime.idClana) AS NumberOfMoviesLate
FROM Members
left JOIN RentedMovies as rented ON rented.MemberID = Members.MemberID
left JOIN RentedMovies as notTakenBackOnTime ON notTakenBackOnTime.MemberID
= Members.MemberID AND notTakenBackOnTime.ReturnDate< CURDATE()
group by Members.MemberID
But it doesnt work corrextly!
And I also tried with this:
SELECT MemberID,my,my2
FROM Members as mem
JOIN (SELECT COUNT(* )AS my FROM RentedMovies) b
ON b.MemberID = mem.MemberID
JOIN (SELECT COUNT(* )AS my2 FROM RentedMovies WHERE ReturnDate< CURDATE()) c
ON c.MemberID = mem.MemberID
But i got some errors!
So the question is how to accomplish right solution?
You were close. Try this:
SELECT M.MemberID,
COUNT(RM.MemberID) NumberOfRentedMovies,
SUM(CASE WHEN RM.ReturnDate < CURDATE() THEN 1 ELSE 0 END) ReturnData
FROM Members M
LEFT JOIN RentedMovies RM
ON M.MemberID = RM.MemberID
GROUP BY M.MemberID
The desired result you showed can be accomplished by:
SELECT MemberID,
COALESCE(COUNT(MovieID), 0) AS NumberOfRentedMovies,
COALESCE(SUM(ReturnDate < CURDATE()), 0) AS NotYetReturned
FROM Members
LEFT JOIN RentedMovies USING (MemberID)
GROUP BY MemberID
See it in action: http://sqlfiddle.com/#!2/a192c/1

MYSQL: returning zero when no value is present, categorized monthly

I have the following two tables that record expenditure and provide expenditure category information:
Table transactions:
+-------+--------+--------+
| month | cat_id | amount |
+-------+--------+--------+
| 1 | 2 | 3 |
| 1 | 2 | 8 |
| 2 | 1 | 7 |
| 2 | 1 | 5 |
+-------+--------+--------+
Table categories:
+--------+-------------+
| cat_id | cat_desc |
+--------+-------------+
| 1 | Stock |
| 2 | Consumables |
+--------+-------------+
What I would like is to construct a query that displays a sum of the amounts for each category, for each month, even if there is no expenditure in that category for that month like this:
+-------+-------------+--------+
| month | cat_desc | amount |
+-------+-------------+--------+
| 1 | Stock | 0 |
| 1 | Consumables | 11 |
| 2 | Stock | 12 |
| 2 | Consumables | 0 |
+-------+-------------+--------+
I suspect an outer join would need to be used but I haven't found a statement to do it yet.
Thank you for any help.
This one should provide you with the correct result. The inner select prepares a list of all months combined with all categories, and the LEFT JOIN handles the rest.
SELECT t.month, t.cat_desc, COALESCE(SUM(t2.amount), 0) amount
FROM (
SELECT DISTINCT t.month, c.cat_id, c.cat_desc
FROM categories c
CROSS JOIN transactions t
) t
LEFT JOIN transactions t2 ON ( t2.month = t.month AND t2.cat_id = t.cat_id )
GROUP BY t.month, t.cat_desc
Performance might be better with the following (using DISTINCT only where necessary), but you will have to try:
SELECT t.month, t.cat_desc, COALESCE(SUM(t2.amount), 0) amount FROM (
SELECT t.month, c.cat_id, c.cat_desc
FROM
(SELECT DISTINCT month FROM transactions) t
CROSS JOIN categories c
) t
LEFT JOIN transactions t2 ON ( t2.month = t.month AND t2.cat_id = t.cat_id )
GROUP BY t.month, t.cat_desc
SELECT c.cat_id, c.cat_desc,t.month, SUM(t.amount)
FROM categories c
LEFT JOIN transactions t ON (t.cat_id = c.cat_id)
GROUP BY c.cat_id,t.month
SELECT c.cat_id, c.cat_desc,t.month, SUM(t.amount)
FROM categories c
LEFT JOIN transactions t ON (t.cat_id = c.cat_id)
GROUP BY t.month,c.cat_id
Order By t.month