Count total flow inventory in and out php mysql - mysql

For the record I'm not using different table, I calculate using the same table but I added more column called stock.
I have a record table:
Table A
=======================================================
**id** | **code** | **status** | **total** | **date** |
1 | B01 | IN | 500 |2013-01-15|
2 | B01 | OUT | 100 |2013-01-20|
3 | B01 | OUT | 200 |2013-02-01|
4 | B01 | IN | 300 |2013-02-05|
The output that I want using select mysql is like this:
Table A
==================================================================
**id** | **code** | **status** | **total** | **date** | **stock**
1 | B01 | IN | 500 |2013-01-15| 500
2 | B01 | OUT | 100 |2013-01-20| 400
3 | B01 | IN | 200 |2013-02-01| 600
4 | B01 | OUT | 300 |2013-02-05| 300
As you can see I added the stock column in table A.. so my question is how can I achieved that using mysql ?
UPDATE
I've been saved by #Juergen D answer so I'm using his method:
select t.*, #stock := #stock + case when status = 'IN'
then total
else -total
end as stock
from your_table t
cross join (select #stock := 0) s
order by t.id
in case you have a same problem as me :)

select t.*, #stock := #stock + case when status = 'IN'
then total
else -total
end as stock
from your_table t
cross join (select #stock := 0) s
order by t.id

Use a CASE statement inside of SUM to determine whether to add or subtract.
SELECT
t1.id,
t1.code,
t1.status,
t1.date,
SUM(
CASE
WHEN t2.status = 'IN'
THEN t2.total
WHEN t2.status = 'OUT'
THEN (t2.total * -1)
END
) stock
FROM table t1
JOIN table t2
ON t2.date <= t1.date
AND t2.code = t1.code
GROUP BY t1.id

Related

MySQL CASE ELSE not returning 0 in multi join select statement

I have a view like this:
VIEW `my_view` AS (
SELECT
t1.sku AS sku,
FORMAT(SUM((CASE
WHEN (t3.order_date > (CURDATE() - INTERVAL 42 DAY)) THEN t2.qty
ELSE 0
END)),
0) AS qty_sold,
FORMAT(SUM((CASE
WHEN (t3.order_status_id = 3) THEN t2.qty
ELSE 0
END)),
0) AS qty_ordered
FROM
inventory_products t1
JOIN inventory_sales t2 ON (t2.sku = t1.sku)
JOIN inventory_orders t3 ON (t3.id = t2.order_id)
where t1.sku=1001 or t1.sku=1002 or t1.sku=1010
GROUP BY t1.sku
)
The view works as expected, and returns correct results, except that when a sku doesn't exist on the inventory_sales table, the row for that sku doesn't return at all.
Note that the sku=1010 doesn't exist on the inventory_sales table, but it does exist on the inventory_products table.
for example:
SELECT * FROM my_view
WHERE sku=1001 OR sku=1002 OR sku=1010
returns this:
+-----------+--------------+------------+
| sku | qty_sold |qty_ordered |
+-----------+--------------+------------+
| 1001 | 2 | 2 |
+-----------+--------------+------------+
| 1002 | 3 | 3 |
+-----------+--------------+------------+
but I need it to return this:
+-----------+--------------+------------+
| sku | qty_sold |qty_ordered |
+-----------+--------------+------------+
| 1001 | 2 | 2 |
+-----------+--------------+------------+
| 1002 | 3 | 3 |
+-----------+--------------+------------+
| 1010 | 0 | 0 |
+-----------+--------------+------------+
What am I missing here? Any advice would be appreciated :)
I think you just need left join:
SELECT . . .
FROM inventory_products t1 LEFT JOIN
inventory_sales t2
ON t2.sku = t1.sku LEFT JOIN
inventory_orders t3
ON t3.id = t2.order_id
WHERE t1.sku IN (1001, 1002, 1010)
GROUP BY t1.sku
I think the ELSE 0 will turn the NULL into a 0 -- your desired result.

in sum, use case to compare incoming value with another column (MYSQL)

ie:
| id | num |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
| e | 5 |
and this query is essentially what I'm trying to do:
select num as number, sum(case num > number then num else 0 end) as summation from table;
(I'm trying to sum up all the ints larger than the currently selected num in the column num.)
example output from above table:
| num | summation |
| 1 | 14 |
| 2 | 12 |
| 3 | 9 |
| 4 | 5 |
|5 | 0 |
The problem lies in the fact that I can't use the alias defined in the same select statement; is there another way?
Thanks!
If you're on MySQL 8.0 you can use window functions.
SELECT num,
sum(num) OVER (ORDER BY num DESC) - num summation
FROM elbat
ORDER BY num;
Prior to MySQL 8.0 you can use a correlated subquery.
SELECT t1.num,
coalesce((SELECT sum(t2.num)
FROM elbat t2
WHERE t2.num > t1.num),
0) summation
FROM elbat t1
ORDER BY t1.num;
You can write this using a correlated subquery:
select num,
(select sum(num)
from t 2
where t2.num >= t.num
) - num as summation
from t;
You can use correlated subquery :
select num,
(select sum(num)
from table t2
where t2.num > t.num
) as summation
from table t1;
this works, although a bit messy:
select num, (select sum(case when table.num > temp.num then num else 0 end)
from (select * from table) as temp
) as summation
from table;

MySql counting the number of groups of rows containing a certain value

How can I get the number of "groups" of a status, where status == 0, excluding groups which start the table and groups that span <= hour? (If the time constraint is too difficult, we can alternatively exclude groups with counts <= 40 instead of groups spanning <= hour, since a row is logged about every 1:30 minutes.)
For example, the following SAMPLE table WITHOUT the time constraint would produce 3 if grouping by status == 0.
+------+----------+----------+
| id | status |time |
+------+----------+----------+
| 0001 | 1 |11:32:48 |
+------+----------+----------+
| 0002 | 0 |11:30:26 |
+------+----------+----------+
| 0003 | 0 |11:28:54 |
+------+----------+----------+
| 0004 | 1 |11:27:23 |
+------+----------+----------+
| 0005 | 0 |11:25:52 |
+------+----------+----------+
| 0006 | 1 |11:24:20 |
+------+----------+----------+
| 0007 | 1 |11:22:48 |
+------+----------+----------+
| 0008 | 0 |11:21:17 |
+------+----------+----------+
| 0009 | 0 |11:19:45 |
+------+----------+----------+
| 0010 | 0 |11:18:14 |
+------+----------+----------+
| 0011 | 0 |11:16:43 |
+------+----------+----------+
| 0012 | 0 |11:15:11 |
+------+----------+----------+
| 0013 | 0 |11:13:39 |
+------+----------+----------+
| 0002 | 0 |11:12:08 |
+------+----------+----------+
| 0014 | 1 |11:10:37 |
+------+----------+----------+
| 0015 | 1 |11:09:05 |
+------+----------+----------+
| 0016 | 1 |11:07:33 |
+------+----------+----------+
| 0017 | 0 |11:06:02 |
+------+----------+----------+
One solution I can think of would be to grab the entire table and produce the result with Java, but I am afraid this would be too inefficient given that the table can have millions of entries.
select sum(is_different_from_previous) , status
from (
select status,
(#prevStatus <> status and #prevStatus <> -1) is_different_from_previous,
#prevStatus := status
from myTable t1
cross join (select #prevStatus := -1) t2
order by t1.time
) t1 group by status
for a specific status
select * from (
select sum(is_different_from_previous) , status
from (
select status,
(#prevStatus <> status and #prevStatus <> -1) is_different_from_previous,
#prevStatus := status
from myTable t1
cross join (select #prevStatus := -1) t2
order by t1.time
) t1 group by status
) t1 where status = 0
Edit
To only count groups with a certain # of 0s
select count(*) from (
select * from (
select status,
(#prevStatus <> status and #prevStatus <> -1) is_different_from_previous,
if(#prevStatus <> status and #prevStatus <> -1,#groupNumber := #groupNumber + 1, #groupNumber) groupNumber,
#prevStatus := status
from myTable t1
cross join (select #prevStatus := -1, #groupNumber := 0) t2
order by t1.id
) t1
where status = 0
group by groupNumber
having count(*) > 4
) t1
http://sqlfiddle.com/#!9/e4a49/23
Try the following modified query, which is more efficient than the earlier one, because another table scan is eliminated and we restrict the data to only the last one hour. Also, the first group is not counted.
EDIT: I changed the JOIN condition back to st2.id = st1.id+1 to satisfy the requirements.
select
st1.status,
count(st1.id)
from sampletable st1
inner join sampletable st2
on (st2.id = st1.id+1 and st2.status <> st1.status)
where st1.status = 0 AND st1.time >= DATE_SUB(NOW(), INTERVAL 1 hour)
group by st1.status;
Updated SQL Fiddle demo with same id, status data:
SQL Fiddle demo

Calculate delta(difference of current and previous row) mysql group by specific column

I have a table like : session is the name of the table for example
With columns: Id, sessionDate, user_id
What i need:
Delta should be a new calculated column
Id | sessionDate | user_id | Delta in days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-03-21 00:00:00 | 2 | NULL
3 | 2011-04-22 00:00:00 | 2 | NULL
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-03-21 00:00:00 | 4 | NULL
6 | 2011-04-22 00:00:00 | 4 | NULL
Delta is the Difference between the timestamps
What i want is a result for Delta Timestamp (in Days) for the the previous row and the current row grouped by the user_id.
this should be the result:
Id | sessionDate | user_id | Delta in Days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-02-21 00:00:00 | 2 | 1
3 | 2011-02-22 00:00:00 | 2 | 1
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-02-23 00:00:00 | 4 | 3
6 | 2011-02-25 00:00:00 | 4 | 2
I already have a solution for a specific user_id:
SELECT user_id, sessionDate,
abs(DATEDIFF((SELECT MAX(sessionDate) FROM session WHERE sessionDate < t.sessionDate and user_id = 1), sessionDate)) as Delta_in_days
FROM session AS t
WHERE t.user_id = 1 order by sessionDate asc
But for more user_ids i didnĀ“t find any solution
Hope somebody can help me.
Try this:
drop table a;
create table a( id integer not null primary key, d datetime, user_id integer );
insert into a values (1,now() + interval 0 day, 1 );
insert into a values (2,now() + interval 1 day, 1 );
insert into a values (3,now() + interval 2 day, 1 );
insert into a values (4,now() + interval 0 day, 2 );
insert into a values (5,now() + interval 1 day, 2 );
insert into a values (6,now() + interval 2 day, 2 );
select t1.user_id, t1.d, t2.d, datediff(t2.d,t1.d)
from a t1, a t2
where t1.user_id=t2.user_id
and t2.d = (select min(d) from a t3 where t1.user_id=t3.user_id and t3.d > t1.d)
Which means: join your table to itself on user_ids and adjacent datetime entries and compute the difference.
If id is really sequential (as in your sample data), the following should be quite efficient:
select t.id, t.sessionDate, t.user_id, datediff(t2.sessiondate, t.sessiondate)
from table t left outer join
table tprev
on t.user_id = tprev.user_id and
t.id = tprev.id + 1;
There is also another efficient method using variables. Something like this should work:
select t.id, t.sessionDate, t.user_id, datediff(prevsessiondate, sessiondate)
from (select t.*,
if(#user_id = user_id, #prev, NULL) as prevsessiondate,
#prev := sessiondate,
#user_id := user_id
from table t cross join
(select #user_id := 0, #prev := 0) vars
order by user_id, id
) t;
(There is a small issue with these queries where the variables in the select clause may not be evaluated in the order we expect them to. This is possible to fix, but it complicates the query and this will usually work.)
Although you have choosen an answer here is another way of achieving it
SELECT
t1.Id,
t1.sessionDate,
t1.user_id,
TIMESTAMPDIFF(DAY,t2.sessionDate,t1.sessionDate) as delta
from myTable t1
left join myTable t2
on t1.user_id = t2.user_id
AND t2.Id = (
select max(Id) from myTable t3
where t1.Id > t3.Id AND t1.user_id = t3.user_id
);
DEMO

MySQL calculate score by rank in percentage

I am using MYSQL to create a rating system to implement my database. What I want to do is to rate each attribute by its percentage with some calculation. Here is the example database:
| ID | VALUE1 | VALUE2|
-----------------------
| 2 | 5 | 20 |
| 4 | 5 | 30 |
| 1 | 3 | 5 |
| 3 | 2 | 8 |
Here is the ideal output I need:
| ID | VALUE1 | RANK1 | Score1 | VALUE2 | RANK2 | Score2 |
---------------------------------------------------------
| 2 | 5 | 1 | 10 | 20 | 2| 8.3|
| 4 | 5 | 1 | 10 | 30 | 1| 10|
| 1 | 3 | 2 | 7.5| 5 | 4| 5|
| 3 | 2 | 3 | 5 | 8 | 3| 6.6|
The formula for score calculation is
5+5*(MaxRank-rank)/(MaxRank-MinRank)
How to generate multiple ranking like the table? I have tried
SELECT
#min_rank := 1 AS min_rank
, #max_rank1 := (SELECT COUNT(DISTINCT value1) FROM table) AS max_rank1
, #max_rank2 := (SELECT COUNT(DISTINCT value2) FROM table) AS max_rank2
;
SELECT
ID
, R1
, TRUNCATE(5.0+5.0 * (#max_rank1 - R1) / (#max_rank1 - #min_rank), 2) AS Score1
, R2
, TRUNCATE(5.0+5.0 * (#max_rank2 - R2) / (#max_rank2 - #min_rank), 2) AS Score2
FROM (
SELECT
ID
, value1
, FIND_IN_SET( `value1`, (SELECT GROUP_CONCAT(DISTINCT `value1` ORDER BY `value1` DESC) FROM table)) AS R1
, value2
, FIND_IN_SET( `value2`, (SELECT GROUP_CONCAT(DISTINCT `value2` ORDER BY `value2` DESC) FROM table)) AS R2
FROM table
) ranked_table;
It works fine with ranking below 170. My database has approximate 200+ ranking for some values and ranks larger then 170 will be seen as 0 when it returns. In that case, the scores with ranks >170 will be miscalculated. Thank you guys.
That looks nasty to calculate.
Something like this might do it
SELECT a.ID, a.VALUE1, Sub1.Rank1, (5.0+5.0 * (Sub3.MaxRank1 - Sub1.Rank1) / (Sub3.MaxRank1 - 1)) AS Score1, a.VALUE2, Sub2.Rank2, (5.0+5.0 * (Sub4.MaxRank2 - Sub2.Rank2) / (Sub4.MaxRank2 - 1)) AS Score2
FROM TestTable a
INNER JOIN (SELECT DISTINCT z.VALUE1, (SELECT ((COUNT(DISTINCT VALUE1) + 1)) FROM TestTable y WHERE z.VALUE1 < y.VALUE1) AS RANK1
FROM TestTable z
) Sub1 ON a.VALUE1 = Sub1.VALUE1
INNER JOIN (SELECT DISTINCT z.VALUE2, (SELECT ((COUNT(DISTINCT VALUE2) + 1)) FROM TestTable y WHERE z.VALUE2 < y.VALUE2) AS RANK2
FROM TestTable z
) Sub2 ON a.VALUE2 = Sub2.VALUE2
CROSS JOIN (SELECT COUNT(*) + 1 AS MaxRank1 FROM TestTable CROSS JOIN (SELECT MAX(VALUE1) AS MaxValue1 FROM TestTable) Sub3a WHERE VALUE1 < MaxValue1) Sub3
CROSS JOIN (SELECT COUNT(*) + 1 AS MaxRank2 FROM TestTable CROSS JOIN (SELECT MAX(VALUE2) AS MaxValue2 FROM TestTable) Sub4a WHERE VALUE2 < MaxValue2) Sub4
Note I am not sure on your score calculation. The equation you give doesn't appear to me to give the results in your example. But I might just be misreading it.