Click here to Skip to main content
15,894,337 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have two tables in a MySql database.

ProductShip table		
SN	  Ship_Month	Customer_Name
Q003	2021-08	    Store100
Q004	2021-08	    Store101
Q005	2021-08	    Store102
Q006	2021-08	    Store103
Q007	2021-09	    Store104
Q008	2021-09	    Store105
Q004	2021-10	    Store106
Q005	2021-10	   

ProductReturn table		
SN	Return_Month	Customer_Name
Q004	2021-10	    Store101
Q005	2021-11	    Store102
Q007	2021-12	    Store104


I would like the query to pivot and return the data in a Nevada chart format - like this:

Ship_Month	Ret_2021-10	Ret_2021-11	Ret_2021-12	Not_Returned	Total
2021-08	          1	        1                       2             4
2021-09			                          1	        1	          2
2021-10			                                    2	          2

As the returns grows month to month, the return columns will increase (dynamic number of columns)


What I have tried:

I have spent countless hours researching online for examples and everything I found does not produce the output I need.

I'm thinking it's not easy to achieve this in MySQL.

If anyone knows how to write a query to accomplish this and can share an example would be greatly appreciated.


If I run this query, I can export it to Excel and use the pivot function to produce a Nevada chart.

Select a.SN,
Case
WHEN a.Date_Shipped IS NOT NULL Then (
	IF (month(a.Date_Shipped) < '10',
	CONCAT(year(a.Date_Shipped),'-0',month(a.Date_Shipped)),
	CONCAT(year(a.Date_Shipped),'-',month(a.Date_Shipped))
	) 
) Else 'NULL' 
end as Ship_Month, 
a.customer_name, 
Case
WHEN b.Date_Returned IS NOT NULL Then (
	IF (month(b.Date_Returned) < '10',
	CONCAT(year(b.Date_Returned),'-0',month(b.Date_Returned)),
	CONCAT(year(b.Date_Returned),'-',month(b.Date_Returned))
	) 
) Else 'NULL' 
end as Return_Month
FROM
(
	SELECT * From ProductShip
)a
LEFT JOIN
(
	SELECT * From ProductReturn
)b ON  a.SN = b.SN AND a.Date_Shipped < b.Date_Returned AND  a.Customer_Name = b.Customer_Name
;
Posted
Updated 17-Mar-22 2:52am
v2
Comments
CHill60 15-Mar-22 13:41pm    
The first problem is your data - consider Q004 returned 2021-10 - is this the one that was shipped 2021-08 or shipped 2021-10. Same for Q005 - is the return on 2021-11 for 2021-08 or 2021-10.
Perhaps you need a unique transaction id rather than the product serial number.
Member 14805492 15-Mar-22 14:01pm    
Q004 was shipped 2021-08 and returned 2 months later 2021-10. Nevada charts keep track of how many devices were shipped yyyy-mm and when those same devices were returned yyyy-mm, thus the need to keep count of the sn's (joined by when shipped and returned). This data format can be inserted into Weibull software to get the reliability of the product and probability of failure for each build (ship) population.
CHill60 15-Mar-22 16:42pm    
You've missed my point. Q004 was shipped 2021-08 AND 2021-10 - which one was returned
Member 14805492 15-Mar-22 17:07pm    
You are correct. It was returned 2021-10, repaired, and sent back out 2021-10... you will see that no devices that were sent out on 2021-10 have been returned yet. When I write a query to get shipped records and left join the return data, it's easy to produce the Nevada format in Excel (using the pivot function). I Just thought it could be done via MySQL query.
CHill60 16-Mar-22 2:46am    
How are you joining those two tables on Excel?
Personally I would have only had one table, with a shipped date and a return date (which could be blank) Or a "transaction id" for each shipment which would also need to be reflected in the returns table

1 solution

This is not a final solution for your question but will hopefully lead you to it. It is somewhat easier to see the issues now that you have posted some code - I also notice that you have added customer_name to the sample data and are including that in the join. A better solution would be to have a transaction id for every shipment (and return). Including the customer_name in both tables does not conform to normalisation and to be honest having the returns in a separate table is not a good design (in my opinion).

Based on your query you also didn't supply us with the correct sample data '2021-09' is a string, not a date but you attempt to use the month() function on it. Also on that subject the MONTH() function returns an integer but you are comparing it to a string - '10'. It will probably work, but it is not good practice.

You are using 'NULL' instead of NULL - is there a specific reason for that? It's not really good practice as it means you have to test for that specific string instead of exploiting built-in functions that can handle null values.

You have unnecessary sub-queries
SQL
FROM
(
	SELECT * From ProductShip
)a
LEFT JOIN
(
	SELECT * From ProductReturn
)b ON  a.SN = b.SN AND a.Date_Shipped < b.Date_Returned AND  a.Customer_Name = b.Customer_Name
is the same as
SQL
FROM ProductShip a
LEFT JOIN ProductReturn
	b ON  a.SN = b.SN AND a.Date_Shipped < b.Date_Returned AND  a.Customer_Name = b.Customer_Name
except the latter is easier to read and executes faster.

Your IF statement is unnecessary - instead of
SQL
Case
WHEN a.Date_Shipped IS NOT NULL Then (
	IF (month(a.Date_Shipped) < '10',
	CONCAT(year(a.Date_Shipped),'-0',month(a.Date_Shipped)),
	CONCAT(year(a.Date_Shipped),'-',month(a.Date_Shipped))
	) 
) Else 'NULL' 
end as Ship_Month,
You could just use the DATE_FORMAT() function and allow it to return NULL naturally if Date_Shipped is null (if it ever could be ... how do you return something that was never shipped?). E.g.
SQL
DATE_FORMAT(a.Date_Shipped, 'yyyy-MM') as Ship_Month

So your query can be very much simplified to something like the following - CAVEAT I can't access MySQL at the moment so this is completely untested in MySQL - I have changed the MS-SQL functions to their MySQL equivalents from my testing, but I may have made mistakes. Note I am also returning the string 'null' instead of NULL as you may have a good reason for that
SQL
Select a.SN,
ifnull(DATE_FORMAT(a.Date_Shipped, 'yyyy-MM'), 'null') as Ship_Month,
a.customer_name, 
ifnull(DATE_FORMAT(b.Date_Returned, 'yyyy-MM'), 'null') as Return_Month
FROM ProductShip a
LEFT JOIN ProductReturn b 
       ON  a.SN = b.SN AND a.Date_Shipped < b.Date_Returned AND  a.Customer_Name = b.Customer_Name;
You can redirect the output from your query into a temporary table (MySQL :: MySQL 8.0 Reference Manual :: 13.1.20.2 CREATE TEMPORARY TABLE Statement[^]) or use a Common Table Expression (MySQL :: MySQL 8.0 Reference Manual :: 13.2.15 WITH (Common Table Expressions)[^]) or even use it as sub-query. I would personally use a temporary table as you may want to visit the results more than once to get your final results

You can then PIVOT on that data to get the results you want - see Pivot Tables in MySQL[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900