I have a table like this:
CREATE TABLE `PQ_batch` (
`id` int(6) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Batch Id number',
`date` datetime DEFAULT NULL,
`qty` int(11) DEFAULT NULL COMMENT 'Number of units in a batch',
PRIMARY KEY (`bid`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
Id | date | qty
--------------------------------
1 2017-01-06 5
2 2017-01-02 5
3 2017-01-03 100
Given a qty value of: #qtyToTake:=100
*Select the rows that will be needed to fulfill the #qtyToTake and ONLY these rows, the quantity that is to be taken from each row, and the new quantity that remains for that row. The oldest batches should be used up first. *
It should look something like this:
Id | date | qty | newQty | qtyTakenPerRecord
-------------------------------------------------------
1 2017-01-02 5 0 5
2 2017-01-03 100 5 95
3 2017-01-01 5 5 0
newQty = (qty - #qtyToTake) where #qtyToTake = (#qtyToTake - the previous row's qty until #qtyToTake reaches 0)
#qtyToTake should be dynamically assigned to be the difference of the previous row's qty and its current value until it reaches 0.
Here's what I came up with:
SELECT p.bid, p.Orig as origQty, p.NewQty, (p.Orig - p.NewQty) AS NumToTake
FROM(
SELECT b.bid, (#runtot := b.bqty - #runtot) AS remain, ( #runtot := (b.bqty - #runtot) ) leftToGet, b.bqty AS Orig,
(SELECT
(sum(bqty) - #runtot) AS tot FROM PQ_batch
WHERE bid <= b.bid ) AS RunningTotal,
(SELECT
CASE
WHEN (sum(bqty) - #runtot) > 1 THEN (sum(bqty) - #runtot)
ELSE 0
END
FROM PQ_batch
WHERE bid <= b.bid ) AS NewQty
FROM PQ_batch b,(SELECT #runtot:= 100) c
ORDER BY bdate
) AS p
Using #McNets example here's what I came up with:
SELECT bid, bdate,bpid,bcost, bqty, newQty, qtyTakenPerRecord
FROM (
SELECT y.*,
IF(#qtyToTake > 0, 1, 0) AS used,
IF(#qtyToTake > bqty, 0, bqty - #qtyToTake) AS newQty,
IF(#qtyToTake > bqty, bqty, bqty - (bqty - #qtyToTake)) AS qtyTakenPerRecord,
#qtyToTake := #qtyToTake - bqty AS qtyToTake
FROM
(SELECT #qtyToTake := 100) x,
(SELECT * from PQ_batch WHERE bpid =1002 AND bqty > 0 ORDER BY bdate) y
) z
WHERE used = 1
ORDER BY bdate
Related
I'm trying to add missing months in this result set.
Where month is missing, add it with the value 0 for Quantita.
SELECT MONTH(Data) AS Mese,Count(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
ORDER BY Mese ASC
+------+----------+
| Mese | Quantita |
+------+----------+
| 4 | 123 |
+------+----------+
| 5 | 100 |
+------+----------+
| 7 | 377 |
+------+----------+
| 9 | 54 |
+------+----------+
The following is messy and I am sure there has to be a cleaner way...
So, first I create a table with all the months:
CREATE TABLE all_months (
month_num INT
);
INSERT INTO all_months VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
Then I join your query with this table (in my case on the right) and use a case-when to decide if there is a count or not:
SELECT
all_months.month_num,
CASE
WHEN tmp.Quantita is NULL THEN 0
ELSE tmp.Quantita
END as Quantita
FROM (
SELECT MONTH(Data) AS Mese,Count(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
) as tmp
RIGHT JOIN all_months on all_months.month_num=tmp.Mese
ORDER BY all_months.month_num ASC
Your query is now in tmp and ordering is happening after that. The result is:
month_num Quantita
1 1
2 2
3 0
4 0
5 0
6 0
7 0
8 3
9 1
10 0
11 1
12 1
Demo: here
A solution that does not require creating a new table.
SELECT
`months`.`number`,
CASE
WHEN tmp.Quantita IS NULL THEN 0
ELSE tmp.Quantita
END AS Quantita
FROM (
SELECT MONTH(Data) AS Mese, COUNT(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
) AS tmp RIGHT JOIN (
SELECT 1 AS `number`
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) AS `months` ON `months`.`number` = tmp.Mese
ORDER BY `months`.`number` ASC;
One method is to construct or find a table that has all the months you want and use a LEFT JOIN.
In your case, though, conditional aggregation might do this with a simpler query. This assumes that you have at least one row for each month, even if that is filtered out by the existing where clause.
So, this might work:
SELECT MONTH(Data) AS Mese,
SUM(Cancellata IS NULL AND FKCampo = 1 ) AS Quantita
FROM prenotazioni
WHERE YEAR(Data) = YEAR(CURDATE()) - 1
GROUP BY Mese
ORDER BY Mese ASC ;
Of course, if the original table does not have all the months, then they still won't be in the result set and you are back to having to use an outer join.
Same idea of #urban but simplifying the query using left join:
create table prenotazioni
(
id int auto_increment,
Cancellata int default null null,
FKCampo int default 1 null,
Data date default null null,
constraint table_nametes_pk
primary key (id)
);
insert into prenotazioni (Cancellata, FKCampo, Data)
values (null, 1, "2019-04-01"),
(null, 1, "2019-04-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-01-01"),
(null, 1, "2019-01-01"),
(null, 1, "2020-01-01"),
(null, 1, "2018-01-01");
CREATE TABLE all_months (
month_num INT
);
INSERT INTO all_months VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
select month_num, count(p.Data)
from
all_months am left join prenotazioni p on (am.month_num = month(p.Data))
where
p.id is null
or
(Cancellata IS NULL AND FKCampo = 1 AND YEAR(Data) = YEAR(CURDATE()) - 1)
GROUP BY am.month_num
ORDER BY am.month_num;
result:
I have a table (test_matches) with a record of the results of several games, sorted by date.
GHFT = Goals Home Team Full Time.
GAFT = Goals Away Team Full Time.
CREATE TABLE `test_matches` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`match_date` date NOT NULL,
`home_team` varchar(250) DEFAULT NULL,
`away_team` varchar(250) DEFAULT NULL,
GHFT` int(11) NOT NULL DEFAULT '0',
`GAFT` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
INSERT INTO test_matches (match_date, home_team, away_team, GHFT, GAFT )
VALUES ('2019-01-01', 'Real Madrid', 'Zaragoza', 2,0),
('2019-01-03', 'Barcelona', 'Lugo', 1,1),
('2019-01-04', 'Real Madrid', 'Lugo', 2,1),
('2019-01-05', 'Barcelona', 'Compostela', 4,1),
('2019-01-06', 'Real Madrid', 'Barcelona', 0,2),
('2019-01-07', 'Barcelona', 'Zaragoza', 0,0);
http://sqlfiddle.com/#!9/c0f16a/1
I tried this query:
SELECT home_team,
ROUND(SUM(CASE WHEN ghft > gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS W_Home_Team,
ROUND(SUM(CASE WHEN ghft = gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS D_Home_Team,
ROUND(SUM(CASE WHEN ghft < gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS L_Home_Team
FROM ( SELECT home_team, ghft, gaft FROM test_matches ORDER BY id DESC LIMIT 2) average
GROUP BY home_team;
However, the result I get is not correct, since it is taking into account the last two records of the table, not the last two records of each team.
The correct result is:
Barcelona 50-50-0 and Real Madrid 50-0-50.
How can I calculate the percentages of the last 2 matches of each team?
With this SELECT Statement
SELECT
home_team
, ROUND(SUM(IF(ghft > gaft,1,0)) / COUNT(*),2) * 100 W_Home_Team
, ROUND(SUM(IF(ghft = gaft,1,0)) / COUNT(*),2) * 100 D_Home_Team
, ROUND(SUM(IF(ghft < gaft,1,0)) / COUNT(*),2) * 100 L_Home_Team
FROM
(SELECT
home_team
,match_date
, ghft
, gaft
, IF(#team <> home_team,#rank := 0, #rank := #rank) dec1
,#rank := #rank+1 rnk
,#team := home_team
FROM test_matches,(SELECT #rank := 0) r1,(SELECT #team := '') r2
ORDER BY home_team,match_date) t1
WHERE rnk < 3
GROUP BY home_team
;
You get this result
home_team W_Home_Team D_Home_Team L_Home_Team
Barcelona 50 50 0
Real Madrid 100 0 0
This rounds the division to two digit so that you will get 25,25 Procent. You will have to adept this to your needs.
The inner SELECT is only to get the correct number of games per club.
with WHERE rnk < 3 you can change the number of games you need
SQL Fiddle example
I need to get running totals between 2 dates in my sql server table and update the records simultaneoulsy. My data is as below and ordered by date,voucher_no
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
-------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
13/10/2017 3 105 20 125
Now if i insert a record with voucher_no 4 on 12/10/2017 the output should be like
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
12/10/2017 4 105 4 109
13/10/2017 3 109 20 129
I have seen several examples which find running totals upto a certain date but not between 2 dates or from a particular date to end of file
You should consider changing your database structure. I think it will be better to keep DATE, VOUCHER_NO, DEBITS, CREDITS in one table. And create view to calculate balances. In that case you will not have to update table after each insert. In this case your table will look like
create table myTable (
DATE date
, VOUCHER_NO int
, DEBITS int
, CREDITS int
)
insert into myTable values
('20171010', 1, 10, null),( '20171012', 2, null, 5)
, ('20171013', 3, 20, null), ('20171012', 4, 4, null)
And view will be
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
select
a.DATE, a.VOUCHER_NO, a.DEBITS, a.CREDITS
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal, a.DEBITS, a.CREDITS
Here's another solution if you can not change your db structure. In this case you must run update statement each time after inserts. In both cases I assume that initial balance is 100 while recalculation
create table myTable (
DATE date
, VOUCHER_NO int
, OPEN_BAL int
, DEBITS int
, CREDITS int
, CLOS_BAL int
)
insert into myTable values
('20171010', 1, 100, 10, null, 110)
,( '20171012', 2, 110, null, 5, 105)
, ('20171013', 3, 105, 20, null, 125)
, ('20171012', 4, null, 4, null, null)
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
, cte2 as (
select
a.DATE, a.VOUCHER_NO
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal
)
update a
set a.OPEN_BAL = b.OPEN_BAL, a.CLOS_BAL = b.CLOS_BAL
from
myTable a
join cte2 b on a.DATE = b.DATE and a.VOUCHER_NO = b.VOUCHER_NO
I have an issue with the following TIMESTAMPDIFF (which calculates the time difference between changes of location) producing a negative result but only for the first TIMESTAMPDIFF calculation, thereafter the calculation for each subsequent row is correct, it may be that the IFNULL expression is incorrect –
SELECT
assetID
, nodeid
, #changed := IF(nodeid <> previousLocationID, #changed + 1, #changed) AS changed
, IFNULL(TIMESTAMPDIFF(SECOND, previousTs, ts), 0) AS secDiff
FROM
(SELECT
assetID
, nodeid
, #locationID AS previousLocationID
, #locationID := nodeid AS currentLocationID
, ts
, #ts AS previousTs
, #ts := ts AS currentTs
FROM Logs L1
where assetID LIKE varassetid
and ts >= vardtmin
and ts <= vardtmax
ORDER BY ts
) L2
The output (produced by the inner SELECT and simplified to show change of location / nodeid) is correct –
assetID nodeid previousLocationID currentLocationID ts previousTs currentTs
1569 1 NULL 1 2017-09-04 09:33 NULL ...
1569 1 NULL 1 2017-09-04 09:40 NULL ...
1569 2 NULL 2 2017-09-04 09:40 NULL ...
1569 2 NULL 2 2017-09-04 09:41 NULL ...
1569 1 NULL 1 2017-09-04 09:41 NULL ...
The output produced by the outer SELECT is –
assetID nodeid changed secDiff
1569 1 NULL -2676
1569 1 NULL 1
1569 1 NULL 1
1569 1 NULL 3
The first secDiff is minus / incorrect (it should be 392 secs (6mins & 32 secs)).
I believe this statement is not working correctly (for the first calculation only) or one of the variables starts in an unknown state -
, IFNULL(TIMESTAMPDIFF(SECOND, previousTs, ts), 0) AS secDiff
Changing to -
IF(timestampdiff(second, previousTs, ts) < 0, 0, timestampdiff(second, previousTs, ts)) as secDiff
eliminates the minus value but results in zero (instead of 392 secs)
I suspect one of the variables starts off in an arbitary state and this causes an issue for the calculation on the first row (where there is no previousTS as such)
Any thoughts?
I have a result as follows
DATE EID TIME TYPE
2015-07-26 1 10:01:00 IN
2015-07-26 1 15:01:00 OUT
2015-07-26 1 18:33:00 IN
2015-07-26 1 23:11:00 OUT
I want to split IN, OUT into different columns ORDER BY date, eid, time. expected result should be as follows
DATE EID IN TIME OUT TIME
2015-07-26 1 10:01:00 15:01:00
2015-07-26 1 18:33:00 23:11:00
This is what I tried so far
SELECT `date` AS 'DATE', `eid` AS 'EID',
CASE WHEN `type` = 'IN' THEN `time` END AS 'IN TIME',
CASE WHEN `type` = 'OUT' THEN `time` END AS 'OUT TIME'
FROM `attendance`
ORDER BY `date`, `eid`, `time`;
It's fetching some ridiculous result as follows
DATE EID IN TIME OUT TIME
2015-07-26 1 10:01:00 null
2015-07-26 1 null 15:01:00
2015-07-26 1 18:33:00 null
2015-07-26 1 null 23:11:00
UPDATED:
This is my table structure
Field Type Null Key Default Extra
id int(10) unsigned NO PRI NULL auto_increment
eid int(10) unsigned NO NULL
time time NO 00:00:00
date date NO 0000-00-00
type enum('IN', 'OUT') NO NULL
state tinyint(1) unsigned NO 1
Here is more tuples...
DATE EID TIME TYPE
2015-07-26 1 10:01:00 IN
2015-07-26 1 15:01:00 OUT
2015-07-26 1 18:33:00 IN
2015-07-26 1 23:11:00 OUT
2015-07-26 3 09:42:00 IN
2015-07-26 3 15:29:00 OUT
2015-07-26 3 18:20:00 IN
2015-07-26 3 00:34:00 OUT
2015-07-26 6 14:16:00 IN
2015-07-26 6 23:08:00 OUT
2015-07-26 8 13:32:00 IN
2015-07-26 8 23:57:00 OUT
2015-07-26 12 09:14:00 IN
2015-07-26 12 15:07:00 OUT
2015-07-26 12 17:28:00 IN
2015-07-26 12 23:53:00 OUT
2015-07-26 13 13:47:00 IN
2015-07-26 13 23:25:00 OUT
2015-07-26 15 11:07:00 IN
2015-07-26 15 19:50:00 OUT
I have written a query for you. I am hoping it will solve your problem :
SQL FOR TABLE SCHEMA
CREATE TABLE `attendance` (
`date` date DEFAULT NULL,
`eid` int(11) DEFAULT NULL,
`time` time(6) DEFAULT NULL,
`type` varchar(5) DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SQL Query FOR RESULT
I have assumed that there is an auto increment column available in your table as id
SELECT * FROM (
SELECT a.`date` AS 'DATE', a.`eid` AS 'EID',
CASE WHEN `type` = 'IN' THEN a.`time` END AS 'IN TIME',
CASE WHEN `type` = 'IN' THEN
(select `time` from `attendance` where id > a.id AND `type` = 'OUT' LIMIT 1) END AS 'OUT TIME'
FROM `attendance` a
ORDER BY a.`date`, a.`eid`, a.`time`
) as t WHERE t.`IN TIME` IS NOT NULL;
Assuming the data are valid (there is not more than one IN between two OUT), using window functions:
SELECT DATE, EID, `IN`, `OUT` FROM
(SELECT date AS 'DATE', eid AS 'EID',
(SELECT MAX(TIME) from attendance where
DATE = a.DATE AND TIME <= a.TIME AND TYPE = 'IN' AND EID = a.EID) as 'IN',
(SELECT MAX(TIME) from attendance where
DATE = a.DATE AND TIME <= a.TIME AND TYPE = 'OUT' AND EID = a.EID) as 'OUT',
TYPE
FROM attendance a
WHERE TYPE = 'OUT'
ORDER BY date, eid, time) t
here is a fiddle for it.
Edit: I did not check the EID, here is the new fiddle.
I have added another answer for it just to fix the issue what Taher is saying :
Query
SELECT * FROM(
SELECT a.`date` AS 'DATE', a.`eid` AS 'EID',
CASE WHEN `type` = 'IN' THEN a.`time` END AS 'IN TIME',
CASE WHEN `type` = 'IN' THEN
(select `time` from
(SELECT `date`, `eid`, `time`,`type`, #rownum := #rownum + 1 as id FROM `attendance` cross join (select #rownum := 0) r ORDER BY `date`, `eid`, `time`) as b where (b.id > a.id) AND `type` = 'OUT' LIMIT 1)
END AS 'OUT TIME'
FROM(SELECT `date`, `eid`, `time`,`type`, #rownum := #rownum + 1 as id FROM `attendance` cross join (select #rownum := 0) r ORDER BY `date`, `eid`, `time`) as a
) as t WHERE t.`IN TIME` IS NOT NULL;