Combining two resulted columns on order by when one is null - mysql

I have a inventory table where i have records which refers to inventory movement of products sold by a company. The movement have 'INCOMING', 'OUTGOING' params based on type of inventory movement. It also has one more column which says the type of 'INCOMING' or 'OUTGOING'... like incoming because of NEW STOCK arrival, outgoing because of PURCHASE by customer... etc...
Now am making a report where i want to list non sold products for a long while. So am making the following query...
SELECT p.id as pid, product_name, DATEDIFF(NOW(), MAX(case when movement_type='OUTGOING' and movement_type_category='PURCHASED' then movement_on end)) AS unsold_days_since_last_sale, DATEDIFF(NOW(), MIN(case when movement_type='INCOMING' and movement_type_category='NEW_STOCK' and quantity>0 then movement_on end)) AS unsold_days_since_first_inventory_in, MAX(case when movement_type='INCOMING' and movement_type_category='NEW_STOCK' and quantity>0 then movement_on end) AS last_inv_in from inventory_movement im left join products p on im.product = p.id GROUP BY product having last_inv_in > 0 ORDER BY unsold_days_since_last_sale desc limit 100
And i get the following output as shown in the image.
This output is nearly correct but with one issue. If a product was never sold even once in the past the column where i try to get days different between CURRENT DAY and LAST SOLD DAY will return null. In that case i need the DAYS difference between CURRENT DAY and FIRST INVENTORY IN of that product to be on the place so i can order that column descending and get the output. But i can get those data as only 2 different columns not as one column. Can someone help me to write a query to get it as combined column so i can sort that data to get result. Am attaching my inventory movement table snap also to show how the data look like...

I think IfNull function will resolve your issue.
Here's modified query.
SELECT p.id AS pid,
product_name,
Datediff(Now(), Ifnull(Max(CASE
WHEN movement_type = 'OUTGOING'
AND movement_type_category =
'PURCHASED' THEN
movement_on
end), Min(CASE
WHEN movement_type = 'INCOMING'
AND movement_type_category =
'NEW_STOCK'
AND quantity > 0 THEN
movement_on
end))) AS
unsold_days_since_last_sale,
Datediff(Now(), Min(CASE
WHEN movement_type = 'INCOMING'
AND movement_type_category = 'NEW_STOCK'
AND quantity > 0 THEN movement_on
end)) AS
unsold_days_since_first_inventory_in,
Max(CASE
WHEN movement_type = 'INCOMING'
AND movement_type_category = 'NEW_STOCK'
AND quantity > 0 THEN movement_on
end) AS last_inv_in
FROM inventory_movement im
LEFT JOIN products p
ON im.product = p.id
GROUP BY product
HAVING last_inv_in > 0
ORDER BY unsold_days_since_last_sale DESC
LIMIT 100

Related

Query to get column totals and Subtract MySQL

I have a transaction table
I am able to get all the debit and credit transaction totals by property separately using these queries
SELECT property, SUM(amount) as creditamount FROM transactions WHERE transactions.type="CREDIT" GROUP BY property
SELECT property, SUM(amount) as debitamount FROM transactions WHERE transactions.type="DEBIT" GROUP BY property
I am facing difficulty in having these two queries done in a single query so that i can subtract credit amount and debit amount for each row and list it according to the property
I tried using sub queries but multiple rows are returned.
Any way to achieve this?
Conditional aggregation may be what you are looking for.
SELECT property,
SUM(case when type = 'Credit' then amount else 0 end) as creditamount,
SUM(case when type = 'Debit' then amount else 0 end) as debitamount,
SUM(case when type = 'Credit' then amount else 0 end) -
SUM(case when type = 'Debit' then amount else 0 end) as diff
FROM transactions
GROUP BY property
Use self join
select debitamount , creditamount
from (select sum(amount) income
from transactions
WHERE transactions.type="CREDIT"
GROUP BY property) i
JOIN (select sum(rate) debitamount
from transactions
WHERE transactions.type="DEBIT"
GROUP BY property) e
Refer this question

sql group by date gives same value everyday

So I want to group by days for statistics so I have data on each day (type 1 is bought and 0 is sold) but the query gives me the same result everyday and that is not correct can someone help me with this code?
SELECT
DATE(from_unixtime(credit_transaction_time)) AS data_date,
total_spend AS credits_spend,
total_bought AS credits_bought
FROM credit_transactions
JOIN (SELECT SUM(`credit_transaction_amount`) AS total_spend FROM credit_transactions WHERE `credit_transaction_type` = 0 GROUP BY DATE(from_unixtime(credit_transaction_time))) AS spend
JOIN (SELECT SUM(`credit_transaction_amount`) AS total_bought FROM credit_transactions WHERE `credit_transaction_type` = 1 GROUP BY DATE(from_unixtime(credit_transaction_time))) AS bought
GROUP BY DATE(from_unixtime(credit_transaction_time))
Use conditional aggregation:
SELECT DATE(from_unixtime(credit_transaction_time)) AS data_date,
SUM(CASE WHEN credit_transaction_type = 0 THEN credit_transaction_amount ELSE 0 END) as credits_spend,
SUM(CASE WHEN credit_transaction_type = 1 THEN credit_transaction_amount ELSE 0 END) as credits_bought
FROM credit_transactions
GROUP BY DATE(from_unixtime(credit_transaction_time));
Your query doesn't work because you don't have an ON condition. In most databases, this would result in a syntax error, but MySQL allows this syntax.

MySQL Attendance System

I have made an attendance system for a project, However I'm stuck right now by trying to create a query.
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents
This code works and generates an attendance. However this will show all the dates.
When I add in another line to specify date
AND Attendance.date = DATE(NOW());
It will not show anything..
There's 'Present', 'Late' status for the attendance however if the student's record in that table doesn't exist, it is considered as absent.
How do I do that?
Assuming you're using a case insensitive collation, your purported solution can be rewritten as follows:
SELECT COUNT(p.idStudents) total
, SUM(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) present -- [or just SUM(a.status = 'present')]
, SUM(CASE WHEN a.status = 'late' THEN 1 ELSE 0 END) late
, SUM(CASE WHEN a.status IS NULL THEN 1 ELSE 0 END) absents
FROM Students p
JOIN Schools s
ON s.idSchools = p.idSchools
JOIN Tags t
ON t.idStudents = p.idStudents
LEFT
JOIN Attendance a
ON a.tagCode = t.tagCode
AND a.date = CURDATE() ;
For next time: Your ERD shows 10 tables, but only 4 feature in this problem. If a table isn't likely to be part of the proposed solution, don't show it. Don't provide pictures. Instead, where possible, provide proper DDLs (and/or an sqlfiddle), TOGETHER WITH THE DESIRED RESULT SET based upon a minimal, but properly representative data set.
Welcome to SO.
Figured it out..
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN (SELECT * FROM Attendance WHERE Attendance.date = NOW()) AS Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents
The problem with the original query was you were asking for students where their attendance was "NOW" where no student was ever now but they are current attending classes. They may have signed in at 8am and you run the query at 10am. You'd need to manipulate the datetime to choose your start time and end time based on the current day.
timestampadd(HOUR, 08, CURDATE()) - this will give you 8am, you'd then query for when attendance.date is greater than or equal to 8am and then potentially less than or equal to 4pm?

most recent entry made in table bases on one year interval mysql

Using the following sqlfiddle here How would I find the most recent payment made between the months of 2012-04-1 and 2012-03-31 using the case statement as in the previous queries
I tried this:
max(case when py.pay_date >= STR_TO_DATE(CONCAT(2012, '-04-01'),'%Y-%m-%d') and py.pay_date <= STR_TO_DATE(CONCAT(2012, '-03-31'), '%Y-%m-%d') + interval 1 year then py.amount end) CURRENT_PAY
However the answer I am getting is incorrect, where the actual answer should be:(12, '2012-12-12', 20, 1)
Please Provide me with some assistance, thank you.
Rather than a CASE inside your MAX() aggregate, that condition belongs in the WHERE clause. This joins against a subquery which pulls the most recent payment per person_id by joining on MAX(pay_date), person_id.
SELECT payment.*
FROM
payment
JOIN (
SELECT MAX(pay_date) AS pay_date, person_id
FROM payment
WHERE pay_date BETWEEN '2012-04-01' AND DATE_ADD('2012-03-31', INTERVAL 1 YEAR)
GROUP BY person_id
) maxp ON payment.person_id = maxp.person_id AND payment.pay_date = maxp.pay_date
Here is an updated fiddle with the ids corrected in your table (since a bunch of them were 15). This returns record 18, for 2013-03-28.
Update
After seeing the correct SQL fiddle... To incorporate the results of this query into your existing one, you can LEFT JOIN against it as a subquery on p.id.
select p.name,
v.v_name,
sum(case when Month(py.pay_date) = 4 then py.amount end) april_amount,
(case when max(py.pay_date)and month(py.pay_date)= 4 then py.amount else 0 end) max_pay_april,
sum(case
when Month(py.pay_date) = Month(curdate())
then py.amount end) current_month_amount,
sum(case
when Month(py.pay_date) = Month(curdate())-1
then py.amount end) previous_month_amount,
maxp.pay_date AS last_pay_date,
maxp.amount AS last_pay_amount
from persons p
left join vehicle v
on p.id = v.person_veh
left join payment py
on p.id = py.person_id
/* LEFT JOIN against the subquery: */
left join (
SELECT MAX(pay_date) AS pay_date, amount, person_id
FROM payment
WHERE pay_date BETWEEN '2012-04-01' AND DATE_ADD('2012-03-31', INTERVAL 1 YEAR)
GROUP BY person_id, amount
) maxp ON maxp.person_id = p.id
group by p.name,
v.v_name

Finding the Most current entry for a month using mysql

I am having a mysql problem I am trying to find both the most current payment value, and, for a particular month (in this query I'm using April). Link to the sqlfillde is here
(case when max(py.pay_date)and month(py.pay_date)= 4 then amount else 0 end) max_pay_april,
This is what I have but it doesn't seem to be working. The most current payment value is: (5, '2012-04-20', 90,) therefore it would be 90 I would really appreciate some help please.
How about this:
select p.name,
v.v_name,
sum(case when Month(py.pay_date) = 4 then amount end) april_amount,
max(case
when month(py.pay_date)= 4
and py.pay_date = (select max(pay_date)
from payment
where month(pay_date) =4 )
then amount else 0 end) max_pay_april,
sum(case
when Month(py.pay_date) = Month(curdate())
then amount end) current_month_amount,
sum(case
when Month(py.pay_date) = Month(curdate())-1
then amount end) previous_month_amount
from persons p
left join vehicle v
on p.id = v.person_veh
left join payment py
on p.id = py.person_id
group by p.name,
v.v_name
see SQL Fiddle with demo