How can do a sum search by 'id=1' and add up their hours.
The first line will be there in time, the next will be their out time.
Below should add up to 5:43:29
id, ts
1,2016-06-20 04:25:32
3,2016-06-20 07:40:09
1,2016-06-20 09:37:46
3,2016-06-20 14:40:57
1,2016-06-20 15:12:14
1,2016-06-20 15:43:29
2,2016-06-20 15:47:01
2,2016-06-20 17:47:03
You can add row number with user defined variable. Here odd rows represents starting time, multiply -1 when it is starting time with if(r%2=0,1,-1), and add them up:
select sec_to_time(sum(time_to_sec(ts)*if(r%2=0,1,-1)))
from (
select #row:=#row+1 r, a.*
from Table1 a
join (select #row:=0) b
where a.id = 1) a;
Oh boy, this was a fun one:
SELECT SEC_TO_TIME(
SUM(UNIX_TIMESTAMP(
(SELECT ts FROM example_table e2 WHERE id = ranked.id AND ts > ranked.ts ORDER BY id, ts LIMIT 1)
)-UNIX_TIMESTAMP(ts))
)
FROM (
SELECT
#row := #row +1 AS rownum, id, ts
FROM (
SELECT #row :=0) r, example_table
WHERE id = 1
ORDER BY id ASC, ts ASC
) ranked
WHERE ranked.rownum % 2 = 1
First we fetch all the odd rows: 1, 3, 5, 7 and soforth. Next, we use a subquery to fetch the next timestamp matching that row (The punch out time), and get the differences (In seconds). Sum will total up the seconds, and sec_to_time will convert that into a friendly readable format.
Here my answer with a sample
Use sql like this:
SELECT
t_from.id,
SEC_TO_TIME(SUM(TIME_TO_SEC( TIMEDIFF(
( SELECT ts
FROM t t_to
WHERE
t_to.ts > t_from.ts_from
AND
t_to.id = t_from.id
ORDER BY t_to.id,t_to.ts
LIMIT 1
)
, t_from.ts_from
)))) AS ts_diff
FROM (
SELECT #nr:=((#nr+1) %2) AS nr,t.id,t.ts AS ts_from
FROM t
CROSS JOIN ( SELECT #nr:=0) AS parameter
ORDER BY id
) AS t_from
WHERE nr =1
GROUP BY t_from.id;
my Table
MariaDB [yourSchema]> select * from t;
+----+---------------------+
| id | ts |
+----+---------------------+
| 1 | 2016-06-20 04:25:32 |
| 3 | 2016-06-20 07:40:09 |
| 1 | 2016-06-20 09:37:46 |
| 3 | 2016-06-20 14:40:57 |
| 1 | 2016-06-20 15:12:14 |
| 1 | 2016-06-20 15:43:29 |
| 2 | 2016-06-20 15:47:01 |
| 2 | 2016-06-20 17:47:03 |
+----+---------------------+
8 rows in set (0.00 sec)
convert to start end times
MariaDB [yourSchema]> SELECT
-> t_from.id
-> , t_from.ts_from,
-> ( SELECT ts
-> FROM t t_to
-> WHERE
-> t_to.ts > t_from.ts_from
-> AND
-> t_to.id = t_from.id
-> ORDER BY t_to.id,t_to.ts
-> LIMIT 1
-> ) AS ts_to
-> FROM (
-> SELECT #nr:=((#nr+1) %2) AS nr,t.id,t.ts AS ts_from
-> FROM t
-> CROSS JOIN ( SELECT #nr:=0) AS parameter
-> ORDER BY id
-> ) AS t_from
-> WHERE nr =1;
+----+---------------------+---------------------+
| id | ts_from | ts_to |
+----+---------------------+---------------------+
| 1 | 2016-06-20 04:25:32 | 2016-06-20 09:37:46 |
| 1 | 2016-06-20 15:12:14 | 2016-06-20 15:43:29 |
| 2 | 2016-06-20 15:47:01 | 2016-06-20 17:47:03 |
| 3 | 2016-06-20 07:40:09 | 2016-06-20 14:40:57 |
+----+---------------------+---------------------+
4 rows in set (0.00 sec)
MariaDB [yourSchema]>
Sum the diffs and group by id
MariaDB [yourSchema]> SELECT
-> t_from.id,
-> SEC_TO_TIME(SUM(TIME_TO_SEC( TIMEDIFF(
-> ( SELECT ts
-> FROM t t_to
-> WHERE
-> t_to.ts > t_from.ts_from
-> AND
-> t_to.id = t_from.id
-> ORDER BY t_to.id,t_to.ts
-> LIMIT 1
-> )
-> , t_from.ts_from
-> )))) AS ts_diff
-> FROM (
-> SELECT #nr:=((#nr+1) %2) AS nr,t.id,t.ts AS ts_from
-> FROM t
-> CROSS JOIN ( SELECT #nr:=0) AS parameter
-> ORDER BY id
-> ) AS t_from
-> WHERE nr =1
-> GROUP BY t_from.id;
+----+----------+
| id | ts_diff |
+----+----------+
| 1 | 05:43:29 |
| 2 | 02:00:02 |
| 3 | 07:00:48 |
+----+----------+
3 rows in set (0.00 sec)
MariaDB [yourSchema]>
Join the table with itself and search for the next timestamp:
select t1.id,
t1.ts as ts_from,
min(t2.ts) as ts_to,
timediff(min(t2.ts), t1.ts) as uptime
from uptime t1
join uptime t2
on t2.id = t1.id
and t2.ts > t1.ts
where t1.id = 1
group by t1.id, t1.ts
Result:
| id | ts_from | ts_to | uptime |
|----|---------------------|---------------------|----------|
| 1 | 2016-06-20 04:25:32 | 2016-06-20 09:37:46 | 05:12:14 |
| 1 | 2016-06-20 09:37:46 | 2016-06-20 15:12:14 | 05:34:28 |
| 1 | 2016-06-20 15:12:14 | 2016-06-20 15:43:29 | 00:31:15 |
Demo
You will need an index on id, ts.
ALTER TABLE `uptime` ADD INDEX `id_ts` (`id`, `ts`);
Update:
After reading you question again i think i got what you are after. You can use the obove query in a subquery, a session variable to switch betwen even and odd rows and a conditional SUM:
select sec_to_time(sum(
case when #switch := 1 - #switch then time_to_sec(sub.uptime) end
)) as uptime
from (
select timediff(min(t2.ts), t1.ts) as uptime
from uptime t1
join uptime t2
on t2.id = t1.id
and t2.ts > t1.ts
where t1.id = 1
group by t1.id, t1.ts
order by t1.ts
) sub
join (select #switch := 0) init_switch
Demo
Related
If I have a table T that look like this: where id is the unique auto-increment primary key. Difference column is default to 0. I want to UPDATE only the difference of largestId - secondLargestId in each id_str group while the rest remains unchanged.
id_str id Value Difference
2380 1 21.01 0
2380 3 22.04 0
2380 5 22.65 0
2380 8 23.11 0
2380 10 35.21 0
20100 2 37.07 0
20100 4 38.17 0
20100 6 38.97 0
20103 7 57.98 0
20103 9 60.83 0
The result I want is:
id_str id Value Difference
2380 1 21.01 0
2380 3 22.04 0
2380 5 22.65 0
2380 8 23.11 0
2380 10 35.21 12.1
20100 2 37.07 0
20100 4 38.17 0
20100 6 38.97 0.8
20103 7 57.98 0
20103 9 60.83 2.85
How can I write the query?
This should do the trick in MySQL.
CREATE TABLE SomeTable
( id_str VARCHAR(10),
id INTEGER,
value_ DECIMAL(7,5),
difference DECIMAL(7,5)
);
INSERT INTO SomeTable VALUES(2380,1,21.01,0);
INSERT INTO SomeTable VALUES(2380,3,22.04,0);
INSERT INTO SomeTable VALUES(2380,5,22.65,0);
INSERT INTO SomeTable VALUES(2380,8,23.11,0);
INSERT INTO SomeTable VALUES(2380,10,35.21,0);
INSERT INTO SomeTable VALUES(20100,2,37.07,0);
INSERT INTO SomeTable VALUES(20100,4,38.17,0);
INSERT INTO SomeTable VALUES(20100,6,38.97,0);
INSERT INTO SomeTable VALUES(20103,7,57.98,0);
INSERT INTO SomeTable VALUES(20103,9,60.83,0);
UPDATE SomeTable,
(SELECT T1.id AS id_updt,
T1.value_ - T2.value_ AS diff_updt
FROM (SELECT id_str,
id,
value_,
(
CASE id_str
WHEN #curStr THEN #curRow := #curRow + 1
ELSE #curRow := 1
AND #curStr := id_str
END
) AS rnk
FROM SomeTable,
(SELECT #curRow := 0, #curStr := '') r
ORDER
BY id_str DESC,
id DESC
) AS T1
INNER
JOIN (SELECT id_str,
id,
value_,
(
CASE id_str
WHEN #curStr THEN #curRow := #curRow + 1
ELSE #curRow := 1
AND #curStr := id_str
END
) AS rnk
FROM SomeTable,
(SELECT #curRow := 0, #curStr := '') r
ORDER
BY id_str DESC,
id DESC
) AS T2
ON T1.id_str = T2.id_str
AND T1.rnk = 1
AND T2.rnk = 2
) AS UPDT
SET SomeTable.difference = UPDT.diff_updt
WHERE SomeTable.id = UPDT.id_updt;
Deprecated solution - This will work for a DBMS that supports the rank function.
UPDATE SomeTable
FROM ( SELECT RNK1.id AS id_updt,
RNK1.value_ - RNK2.value_ AS diff_updt
FROM (SELECT id_str,
RANK() OVER
( PARTITION BY id_str
ORDER BY id DESC
) AS id_rnk
FROM SomeTable
) AS RNK1
INNER
JOIN (SELECT id_str,
RANK() OVER
( PARTITION BY id_str
ORDER BY id DESC
) - 1 AS id_rnk_decrement
FROM SomeTable
) AS RNK2
ON RNK1.id_str = RNK2.id_str
AND RNK1.id_rnk = RNK2.id_rnk_decrement
WHERE RNK1.id_rnk = 1
) AS UPDT
SET SomeTable.difference_ = UPDT.diff_updt
WHERE SomeTable.id = UPDT.id_updt;
You can find the two greatest ids per group with the following query:
select t1.id_str, max(t1.id) as id1, (
select max(t2.id)
from mytable t2
where t2.id_str = t1.id_str
and t2.id < max(t1.id)
) as id2
from mytable t1
group by t1.id_str;
Result:
| id_str | id1 | id2 |
|--------|-----|-----|
| 2380 | 10 | 8 |
| 20100 | 6 | 4 |
| 20103 | 9 | 7 |
Use it as subquery in your update statement:
update mytable u
join (
select t1.id_str, max(t1.id) as id1, (
select max(t2.id)
from mytable t2
where t2.id_str = t1.id_str
and t2.id < max(t1.id)
) as id2
from mytable t1
group by t1.id_str
) t on t.id1 = u.id
join mytable t1 on t1.id = t.id1
join mytable t2 on t2.id = t.id2
set u.Difference = t1.Value - t2.Value;
The table will now contain:
| id_str | id | Value | Difference |
|--------|----|-------|------------|
| 2380 | 1 | 21.01 | 0 |
| 2380 | 3 | 22.04 | 0 |
| 2380 | 5 | 22.65 | 0 |
| 2380 | 8 | 23.11 | 0 |
| 2380 | 10 | 35.21 | 12.1 |
| 20100 | 2 | 37.07 | 0 |
| 20100 | 4 | 38.17 | 0 |
| 20100 | 6 | 38.97 | 0.8 |
| 20103 | 7 | 57.98 | 0 |
| 20103 | 9 | 60.83 | 2.85 |
http://rextester.com/CCO40873
Im using Database MySql57. OS- WIN7 . My question is I want to calculate running hours of a machine when it is in ON and OFF condition (In my table 1 indicates ON and 0 indicates OFF). I tried but its not accurate . My Table Structure like..
This is my table and Table name is workshop
For this i tried a query like
SELECT count, min(DATETIME), MAX(DATETIME),
TIMEDIFF((MAX(DATETIME)),(min(DATETIME))) as totalhours
from signode.press1
WHERE (DateTime between '2017-07-10 00:00:00' and '2017-07-12 00:00:00') AND status='1' group by count
But it is not accurate.
Can anyone please help me for this . Im trying this for since 2 days.
A little messy but given this
DROP TABLE IF EXISTS T;
CREATE TABLE T(ID INT AUTO_INCREMENT PRIMARY KEY,TIM DATETIME,STATUS INT);
INSERT INTO T (TIM,STATUS) VALUES
('2017-07-30 07:07:00',1),('2017-07-30 07:08:00',1),('2017-07-30 07:09:00',0),
('2017-07-30 07:10:00',1),('2017-07-30 07:11:00',0),('2017-07-30 07:12:00',0);
I would first generate a dummy final status record, generate a block number (for each block of status = 1 and status = 0) and a sequence number (so that I can join each row to the next row and get a time difference).After that I just need to group and finally sum times.
So this code
MariaDB [sandbox]> SELECT X.ID IDX,X.SEQNO SEQNOX,X.TIM TIMX,X.STATUS STATUSX,X.BN BNX
-> ,Y.ID IDY,Y.SEQNOY,DATE_aDD(Y.TIM, INTERVAL -1 SECOND) TIMY,Y.STATUS STATUSY,Y.BNY
-> FROM
-> (
-> SELECT ID,
-> #SEQNO:=#SEQNO+1 SEQNO,
-> TIM,STATUS,
-> IF(STATUS <> #P, #BN:=#BN+1,#BN) BN,
-> IF(STATUS <> #P, #RN:=1,#RN:=#RN+1) RN,
-> #P:=STATUS
-> FROM
-> (
-> SELECT ID,TIM,STATUS
-> FROM T
-> UNION SELECT 999999,NOW(),9
-> ) S
-> , (SELECT #BN:=0,#RN:=0,#P:=0,#SEQNO:=0) BN
-> ORDER BY ID
-> ) X
->
-> JOIN
->
-> (
-> SELECT ID,
-> #SEQNOY:=#SEQNOY+1 SEQNOY,
-> TIM,STATUS,
-> IF(STATUS <> #PY, #BNY:=#BNY+1,#BNY) BNY,
-> IF(STATUS <> #PY, #RNY:=1,#RNY:=#RNY+1) RNY,
-> #PY:=STATUS
-> FROM
-> (
-> SELECT ID,TIM,STATUS
-> FROM T
-> UNION SELECT 999999,NOW(),9
-> ) S
-> , (SELECT #BNY:=0,#RNY:=0,#PY:=0,#SEQNOY:=0) BNY
-> ORDER BY ID
-> ) Y
-> ON Y.SEQNOY = X.SEQNO + 1;
results in
+-----+--------+---------------------+---------+------+--------+--------+---------------------+---------+------+
| IDX | SEQNOX | TIMX | STATUSX | BNX | IDY | SEQNOY | TIMY | STATUSY | BNY |
+-----+--------+---------------------+---------+------+--------+--------+---------------------+---------+------+
| 1 | 1 | 2017-07-30 07:07:00 | 1 | 1 | 2 | 2 | 2017-07-30 07:07:59 | 1 | 1 |
| 2 | 2 | 2017-07-30 07:08:00 | 1 | 1 | 3 | 3 | 2017-07-30 07:08:59 | 0 | 2 |
| 3 | 3 | 2017-07-30 07:09:00 | 0 | 2 | 4 | 4 | 2017-07-30 07:09:59 | 1 | 3 |
| 4 | 4 | 2017-07-30 07:10:00 | 1 | 3 | 5 | 5 | 2017-07-30 07:10:59 | 0 | 4 |
| 5 | 5 | 2017-07-30 07:11:00 | 0 | 4 | 6 | 6 | 2017-07-30 07:11:59 | 0 | 4 |
| 6 | 6 | 2017-07-30 07:12:00 | 0 | 4 | 999999 | 7 | 2017-07-30 11:31:32 | 9 | 5 |
+-----+--------+---------------------+---------+------+--------+--------+---------------------+---------+------+
6 rows in set (0.12 sec)
Then if I wrap this with a group by and sum
MariaDB [sandbox]> SELECT STATUSX, SEC_TO_TIME(SUM(TIME_TO_SEC(DIFF)))
-> FROM
-> (
->
-> SELECT BNX,STATUSX,MIN(TIMX), MAX(TIMY), TIMEDIFF(MAX(TIMY) , MIN(TIMX)) DIFF
-> FROM
-> (
-> SELECT X.ID IDX,X.SEQNO SEQNOX,X.TIM TIMX,X.STATUS STATUSX,X.BN BNX
-> ,Y.ID IDY,Y.SEQNOY,DATE_aDD(Y.TIM, INTERVAL -1 SECOND) TIMY,Y.STATUS STATUSY,Y.BNY
-> FROM
-> (
-> SELECT ID,
-> #SEQNO:=#SEQNO+1 SEQNO,
-> TIM,STATUS,
-> IF(STATUS <> #P, #BN:=#BN+1,#BN) BN,
-> IF(STATUS <> #P, #RN:=1,#RN:=#RN+1) RN,
-> #P:=STATUS
-> FROM
-> (
-> SELECT ID,TIM,STATUS
-> FROM T
-> UNION SELECT 999999,NOW(),9
-> ) S
-> , (SELECT #BN:=0,#RN:=0,#P:=0,#SEQNO:=0) BN
-> ORDER BY ID
-> ) X
->
-> JOIN
->
-> (
-> SELECT ID,
-> #SEQNOY:=#SEQNOY+1 SEQNOY,
-> TIM,STATUS,
-> IF(STATUS <> #PY, #BNY:=#BNY+1,#BNY) BNY,
-> IF(STATUS <> #PY, #RNY:=1,#RNY:=#RNY+1) RNY,
-> #PY:=STATUS
-> FROM
-> (
-> SELECT ID,TIM,STATUS
-> FROM T
-> UNION SELECT 999999,NOW(),9
-> ) S
-> , (SELECT #BNY:=0,#RNY:=0,#PY:=0,#SEQNOY:=0) BNY
-> ORDER BY ID
-> ) Y
-> ON Y.SEQNOY = X.SEQNO + 1
-> ) Z
->
-> GROUP BY STATUSX,BNX
->
-> ) A
-> GROUP BY STATUSX;
+---------+-------------------------------------+
| STATUSX | SEC_TO_TIME(SUM(TIME_TO_SEC(DIFF))) |
+---------+-------------------------------------+
| 0 | 04:24:12 |
| 1 | 00:02:58 |
+---------+-------------------------------------+
2 rows in set (0.00 sec)
I'm little bit new in SQL, need to sort column by dates.
Compare how it should to be and how it seems now
Tried this code, not working:
SELECT *
FROM `onetime_contest`
ORDER BY `onetime_contest`.`status` ASC,
IF (#status = 'live', `onetime_contest`.`valid_till`, '') ASC,
IF (#status = 'waiting', `onetime_contest`.`valid_till`, '') ASC,
IF (#status = 'completed', `onetime_contest`.`valid_till`, '') DESC,
IF (#status = 'not_actual', `onetime_contest`.`valid_till`,'') DESC
To be honest I have no idea why this works except that it seems to convince mysql to honour the order by clause in the sub queries.
drop table if exists `onetime_contest`;
create table `onetime_contest`(id int, status varchar(20),valid_till date);
insert into `onetime_contest` values
(1,'live','2017-01-01'),
(2,'waiting','2017-01-01'),
(3,'completed','2017-01-01'),
(4,'not-actual','2017-01-01'),
(5,'live','2017-06-01'),
(6,'waiting','2017-06-01'),
(7,'completed','2017-06-01'),
(8,'not-actual','2017-06-01');
ariaDB [sandbox]> select * from
-> (SELECT 1 sortorder,id,status,valid_till
-> #if(ot.valid_till <> #p,#rn:=#rn - 1 , #rn:=#rn) rn,
-> ##p:=ot.valid_till
-> FROM (select #rn:=999999,#p:='1957-01-01') b,`onetime_contest` ot
-> where status = 'live'
-> order by valid_till asc
-> ) a
-> union
-> select * from
-> (SELECT 2 sortorder,id,status,valid_till
-> #if(ot.valid_till <> #p1,#rn1:=#rn1 + 1 , #rn1:=#rn1) rn,
-> ##p1:=ot.valid_till
-> FROM (select #rn1:=0,#p1:='1957-01-01') b,`onetime_contest` ot
-> where status = 'not-actual'
-> order by valid_till desc
-> ) b
-> union
-> select * from
-> (SELECT 3 sortorder,id,status,valid_till
-> #if(ot.valid_till <> #p2,#rn2:=#rn2 - 1 , #rn2:=#rn2) rn,
-> ##p2:=ot.valid_till
-> FROM (select #rn2:=999999,#p2:='1957-01-01') b,`onetime_contest` ot
-> where status = 'completed'
-> order by valid_till desc
-> ) c
-> ;;
+-----------+------+------------+------------+
| sortorder | id | status | valid_till |
+-----------+------+------------+------------+
| 1 | 1 | live | 2017-01-01 |
| 1 | 5 | live | 2017-06-01 |
| 2 | 8 | not-actual | 2017-06-01 |
| 2 | 4 | not-actual | 2017-01-01 |
| 3 | 7 | completed | 2017-06-01 |
| 3 | 3 | completed | 2017-01-01 |
+-----------+------+------------+------------+
6 rows in set (0.00 sec)
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
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