MySQL: splitting result into two different columns - mysql

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;

Related

MySQL Add missing Months in resultset

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:

Find percentage of Wins/Draw/Losses of last X games played by a team

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

How to join two table to get day sale

I want to join two table with some condition
Table 1
CREATE TABLE IF NOT EXISTS `payment` (
`paymentcode` int(6) NOT NULL,
`date` int(3) unsigned NOT NULL,
`amount` varchar(200) NOT NULL,
`customer` varchar(200) NOT NULL,
`store` varchar(200) NOT NULL
)
('2', '20190120', '10050','C1','A'),
('2', '20190120', '10050','c2','A'),
('6', '20190120', '9050','c3','A'),
('4', '20190120', '9045','c4','B'),
('6', '20190121', '10050','c5','B'),
('2', '20190121', '20050','c6','A');
Table 2
CREATE TABLE IF NOT EXISTS `customer` (
`code` int(6) NOT NULL,
`name` int(3) NOT NULL,
)
( 'C1','Customer1'),
( 'c2','Customer2'),
( 'c3','Customer3'),
( 'c4','Customer4'),
( 'c5','Customer5'),
( 'c6','Customer6');
select a.date,a.Paymentcode,a.store,b.Amount as document_total
from
(select date ,Paymentcode,store,sum(amount) from payment
group by date ,Paymentcode,store
) a
join
(select date ,store, sum(amount)as Amount from Payment
group by date ,store) b
on a.date = b.date and a.store = b.store
From above query I can fetch below value:
date paymentcode Amount documet_total
20190120 2 20100 29150
20190120 4 9045 9045
20190120 6 18095 29150
20190121 4 20050 20050
20190121 2 10050 10050
This was the query I was trying. Now I want to JOIN customer table to get customer code if Paymentcode='2' else need to take store value
My expected out is below:
date paymentcode Amount customer_type document_total
20190120 2 10050 C1 29150
20190120 2 10050 C2 29150
20190120 4 9045 B 9045
20190120 6 18095 A 29150
20190121 4 20050 B 20050
20190121 2 10050 C6 10050
Where I need to calculate amount based on date,store,customer_type and Paymentcode, customer_type should customercode it paymentcode='2' else store code, document_total is based on date,store.
Please advice me how to join these table and get the output

Go back in time with a SQL request with a join or group by

I'm working on an old DB and I have to create new columns based on old ones. This is a log table, each lines is an event. So uid is a User ID and a user can make several validation requests.
Here's my table
event_id uid event
1 1 REQUEST
2 1 VALIDATION
3 2 REQUEST
4 3 REQUEST
5 3 VALIDATION
6 2 VALIDATION
7 1 REQUEST
8 1 VALIDATION
Here's what I'd like to have
event_id uid event last_event_id request_nb
1 1 REQUEST 8 1
2 1 VALIDATION 8 1
3 2 REQUEST 6 1
4 3 REQUEST 5 1
5 3 VALIDATION 5 1
6 2 VALIDATION 6 1
7 1 REQUEST 8 2
8 1 VALIDATION 8 2
I assume that I need a group by on uid, a sum like 1 + SUM(CASE WHEN EVENT = 'VALIDATION' THEN 1 ELSE 0 END) AS request_nb. And a MAX somewhere for last_event_id. But I'm not familiar with these kinds of join.
Here's my sample data set:
CREATE TABLE IF NOT EXISTS `docs` (
`event_id` int(6) unsigned NOT NULL,
`uid` int(3) unsigned NOT NULL,
`event` varchar(200) NOT NULL,
PRIMARY KEY (`event_id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `docs` (`event_id`, `uid`, `event`) VALUES
('1', '1', 'REQUEST'),
('2', '1', 'VALIDATION'),
('3', '2', 'REQUEST'),
('4', '3', 'REQUEST'),
('5', '3', 'VALIDATION'),
('6', '2', 'VALIDATION'),
('7', '1', 'REQUEST'),
('8', '1', 'VALIDATION');
and fiddle of same: http://sqlfiddle.com/#!9/404dcf/1
Does anyone have an idea ?
Thanks!
E.g.:
SELECT a.event_id
, a.uid
, a.event
, a.i
, b.last_event_id
FROM
( SELECT x.*
, CASE WHEN #prev_uid = uid THEN CASE WHEN #prev_event = event THEN #i:=#i+1 ELSE #i:=1 END ELSE #i:=1 END i
, #prev_uid := uid prev_uid
, #prev_event := event prev_event FROM docs x
, (SELECT #prev_uid := null, #prev_event := null, #i:=1) vars
ORDER
BY uid
, event
, event_id
) a
JOIN
( SELECT uid, MAX(event_id) last_event_id FROM docs GROUP BY uid ) b
ON b.uid = a.uid
ORDER
BY a.event_id;
You can use ANSI-standard window functions:
select t.*,
max(event_id) over (partition by uid) as last_event_id,
row_number() over (partition by uid, event order by event_id) as request_nb
from t;

MySQL Rolling Subtraction of a given qantity

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