Mysql Ranking on on Left JOIN - mysql

I have two tables (teams and matches) and I want calculate the game table using points and #rank position.
The tables are very simple: Teams and matches (battles). The last column in 'battle' means if the battle was played or not.
DROP TABLE IF EXISTS monsters;
DROP TABLE IF EXISTS battles;
CREATE TABLE monsters (id_monster int auto_increment primary key, monster varchar(50));
CREATE TABLE battles (id_battle int auto_increment primary key, monster_h int, monster_a int, score_a int, score_h int, played tinyint);
Here some fake data
(Edit: I've updated the script removing random score and apply fixed scored.)
INSERT INTO monsters (monster) VALUES ("Nembo"), ("Kid"), ("Captain"), ("OOI"), ("Koky"), ("Rudes");
INSERT INTO battles (monster_h, monster_a, score_h, score_a, played) VALUES
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 1, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 5, 2,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1, 1,1),
(6,4, 0, 0,0),
(4,6, 0, 0,0);
Here my question
As you can see the Points are calculated correctly and table is good! But... #rank I wrong! I think I should join it too. Can you help me on #rank ?
SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND b.score_a = b.score_h THEN 1 ELSE 0 END)
) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END) ) as pl,
(#rank := #rank + 1) AS rank
FROM ( monsters as m )
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
,( SELECT #rank := 0 ) AS vars
where b.played = 1
GROUP BY m.monster
order by pt DESC, rand();
The result: with a wrong #rank
Monster, PTs, BATTLES, #RANK (good rank should be)
Nembo, 10, 4, 1 #1
Captain, 6, 4, 3 #2
Rudes, 3, 1, 6 #3
Kid, 2, 3, 2 #4
Koky, 1, 2, 5 #5
OOI, 0, 2, 4 #6
UPDATE
Use this data to have more monsters with same points and scores:
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 3, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 16, 12,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1,1,1),
(6,4, 0, 0,0),
(4,6, 0, 1,1),
(4,2,1,0,1),
(4,5,5,0,1),
(2,3,2,0,1),
(2,1,1,1,1);
and then use this script as Stefan said:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl,
(SUM(CASE WHEN
(b.monster_h= m.id_monster ) THEN b.score_h ELSE 0 END) +
SUM(CASE WHEN
(b.monster_a= m.id_monster ) THEN b.score_a ELSE 0 END)) as scored
, floor(rand()*1000) as coin
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC, scored DESC , coin DESC
The table is ordered by POINTS as first criteria, then scored. And it works. But if I use the "coin" as 3th criteria.... it doesn't works!

Great that you included all scripts to recreate your situation.
Put your results from your current query into a subquery, the trick with the #rank doesn't work with GROUP BY.
With this script I get the correct results:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC

Related

MySql get balance of accounts

i want to calculate the balance of 3 accounts.
I have 2 tables:
accounts with id, name and start-balance
transactions with value, charge-account, type and paid
To calculate the balance i have to add the start-balance (from accounts) with alle the transaction-values where charge-account = account-id, paid = 1 and type = 1. Then i have to subtract (correct word?) all the transaction-values where charge-account = account-id, paid = 1 and type = 0
At the end, if everything would work i just want to see what balance the accounts have right now.
i tried this query but i get wrong results, it looks like it adds the start-balance multiple times...
SELECT
SUM(IF(a.id = 1, IF(t.type = 1 AND t.charge_account = 1, t.value, 0) - IF(t.type = 0 AND t.charge_account = 1, t.value, 0), 0) + a.start-balance) as "balanc_1",
SUM(IF(a.id = 2, IF(t.type = 1 AND t.charge_account = 2, t.value, 0) - IF(t.type = 0 AND t.charge_account = 2, t.value, 0), 0) + a.start-balance) as "balance_2",
SUM(IF(a.id = 3, IF(t.type = 1 AND t.charge_account = 3, t.value, 0) - IF(t.type = 0 AND t.charge_account = 3, t.value, 0), 0) + a.start-balance) as "balance_3"
FROM test.transactions t, test.accounts a
WHERE t.paid = 1;
transactions:
accounts:
how it should be like:
SELECT a.id,
MAX ( a.`start-balance` ) +
SUM ( CASE WHEN t.type = 1 then t.value
WHEN t.type = 2 then -t.value
ELSE 0
END ) as balance
FROM accounts a
JOIN transactions t
ON a.id = t.`charge-account`
WHERE a.id IN (1,2,3)
AND t.paid = 1
GROUP BY id
You need to use UNION and then group by account id
select accountid, sum(amount ) as amount from (
select accountid, startamount as amount from accounts
union
select accountid, transactionamount from transactions WHERE ....
) t
group by accountid

How to create a view that show last 4 quarters data?

My environment is 5.1.72-community.
I have a table named production, like:
id, person_id, num, p_year, p_quarter
1, 1, 43, 2018, 2
2, 1, 92, 2018, 1
3, 1, 108, 2017, 4
4, 2, 41, 2018, 2
...
By the time of '2018-04-14', we know quarter=2, and year=2018. I define quarter_1 is current quarter of current year, and quarter_2 is previous quarter, ans so on. Now I want to create a view like:
person_id, num_quarter_1, num_quarter_2, num_quarter_3, num_quarter_4
1, 43, 92, 108, 0
2, 41, 0, 0, 0
As that, if there no num in that quarter, fill 0.
How to create that view please?
Hmmm. If I interpret "last four quarters" as "the last four quarters that are in the data", then here is one method.
First, you can get the four quarters by doing:
select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4;
You can enumerate them using variables:
select p_year, p_quarter, (#rn := #rn + 1) as enum
from (select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4
) p cross join
(select #rn := 0) params
Then, you can use this in a query to pivot the data:
select p.person_id,
sum(case when seqnum = 1 then num else 0 end) as num_quarter_1,
sum(case when seqnum = 2 then num else 0 end) as num_quarter_2,
sum(case when seqnum = 3 then num else 0 end) as num_quarter_3,
sum(case when seqnum = 4 then num else 0 end) as num_quarter_4
from production p join
(select p_year, p_quarter, (#rn := #rn + 1) as seqnum
from (select distinct p_year, p_quarter
from production p
order by p_year desc, p_quarter desc
limit 4
) p cross join
(select #rn := 0) params
) yq
using (p_year, p_quarter)
group by p.person_id;
EDIT:
If you are defining the quarters based on the current quarter, you can do something quite similar:
select p.person_id,
sum(case when seqnum = 1 then num else 0 end) as num_quarter_1,
sum(case when seqnum = 2 then num else 0 end) as num_quarter_2,
sum(case when seqnum = 3 then num else 0 end) as num_quarter_3,
sum(case when seqnum = 4 then num else 0 end) as num_quarter_4
from production p join
(select year(curdate()) as p_year, quarter(curdate()) as p_quarter, 1 as seqnum union all
select year(curdate() - interval 1 quarter) as p_year, month(curdate() - interval 1 quarter) as p_quarter, 2 as seqnum union all
select year(curdate() - interval 2 quarter) as p_year, month(curdate() - interval 2 quarter) as p_quarter, 3 as seqnum union all
select year(curdate() - interval 2 quarter) as p_year, month(curdate() - interval 3 quarter) as p_quarter, 4 as seqnum
) yq
using (p_year, p_quarter)
group by p.person_id;
There are other approaches, such as:
select person_id,
sum(case when year(curdate()) = p_year and quarter(curdate()) = p_quarter
then num else 0
end) as num_quarter_1,
sum(case when year(curdate() - interval 1 quarter) = p_year and quarter(curdate() - interval 1 quarter) = p_quarter
then num else 0
end) as num_quarter_2,
sum(case when year(curdate() - interval 2 quarter) = p_year and quarter(curdate() - interval 2 quarter) = p_quarter
then num else 0
end) as num_quarter_3,
sum(case when year(curdate() - interval 3 quarter) = p_year and quarter(curdate() - interval 3 quarter) = p_quarter
then num else 0
end) as num_quarter_4
from production p
group by person_id;
SELECT
person_id,
SUM((p_quarter = 1) * num) as num_quarter_1,
SUM((p_quarter = 2) * num) as num_quarter_2,
SUM((p_quarter = 3) * num) as num_quarter_3,
SUM((p_quarter = 4) * num) as num_quarter_4
FROM production
WHERE p_year = 2018
GROUP BY person_id;

How to Select same column in 1 table twice in mysql

How can i select 1 column twice using only 1 table, the problem is i have to separate 1 column into 2 return
ex: table Logs
TIMELOGS 11:00 , 12:12 , 13:00 , 17:01 , 17:05 , 17:10
TIMEMODE 0 , 0 , 0 , 1 , 1 , 1
the output should be
IN 11:00 , 12:12 , 13:00
OUT 17:01, 17:05 , 17:10
How can I combine these to queries
Select TIMELOGS as IN FROM table_logs where TIMEMODE = 0;
and
Select TIMELOGS as OUT FROM table_logs where TIMEMODE = 1;
If I understand correctly, you want to enumerate the 0s and the 1s and then group by that:
select max(case when mode = 0 then time end),
max(case when mode = 1 then time end)
from (select l.*,
(#rn := if(#m = mode, #rn + 1,
if(#m := mode, 1, 1)
)
) as rn
from logs l cross join
(select #rn := 0, #m := -1) params
order by mode, time
) l
group by rn;
To output the format you described, (suppose you're using MySQL) you could do:
select
case when TIMEMODE = 0 then 'IN' else 'OUT' end,
group_concat(TIMELOGS)
from table_logs
group by TIMEMODE

mysql count zeros in sequence

I got mysql database and I need to get number of zeros in sequence and print them all with date from first zero, so for example I got a table like this
id, date, impuls_count
1, '12-05-15 12:00:00', 60
2, '12-05-15 12:01:00', 0
3, '12-05-15 12:02:00', 0
4, '12-05-15 12:03:00', 49
5, '12-05-15 12:04:00', 0
6, '12-05-15 12:05:00', 0
7, '12-05-15 12:06:00', 0
8, '12-05-15 12:07:00', 0
9, '12-05-15 12:08:00', 30
10, '12-05-15 12:09:00', 0
this should give the result like this:
'12-05-15 12:01:00', 2
'12-05-15 12:04:00', 4
'12-05-15 12:09:00', 1
I tried to solve it on my own but my query works very slow(I got 5000 rows in a table) and it sometimes prints same row twice
SELECT qwe.date, ile
FROM (SELECT p.date,
(SELECT COUNT(*)
FROM performance_v2
WHERE date > p.date
AND date <
(SELECT MIN(date)
FROM performance_v2
WHERE date > p.date AND impuls_count > 0)) ile
FROM performance_v2 p
WHERE p.impuls_count > 0
AND (date(p.date)
BETWEEN '2015-05-08%'
AND '2015-05-08%')
AND (time(p.date)
between '14:00:00' and '22:00:00')
ORDER BY 1) qwe
WHERE ile > 0
In MySQL, this is easiest to solve using variables. The idea is to have a counter increment each time the value of impuls_count changes. This defines groups of common values. You can then filter the values and aggregate to get what you want:
select min(date), count(*)
from (select t.*,
(#g := if(#ic = impuls_count, #g,
if(#ic := impuls_count, #g + 1, #g + 1)
)
) as grp
from table t cross join
(select #ic := 0, #g := 0)
order by id
) t
where impuls_count = 0
group by grp

MySQL order rank as a column

I have a MySQL query that selects data from multiple tables and then orders the results based on some arbitrary criteria as below:
SELECT [columns] FROM (
SELECT *, COUNT(*) as `matches`
FROM [table1]
JOIN [table2] USING (id)
JOIN [table3] USING (id)
WHERE [criteria]
GROUP BY `id`
ORDER BY `matches` DESC
) AS `grouped`
ORDER BY (
(CASE WHEN [1st rank criteria] THEN 3 ELSE 0 END) +
(CASE WHEN [2nd rank criteria] THEN 2 ELSE 0 END) +
(CASE WHEN [3rd tank criteria] THEN 1 ELSE 0 END)
) DESC
LIMIT 100
This works fine, but my question is: can I have the ranking score displayed as a column? I've looked at trying to use variables, but I'm quite new to SQL so all this is a bit beyond me.
Sorry if this is an obvious question, but many thanks in advance for your time and assistance.
Try this:
SELECT [columns],
(
(CASE WHEN [1st rank criteria] THEN 3 ELSE 0 END) +
(CASE WHEN [2nd rank criteria] THEN 2 ELSE 0 END) +
(CASE WHEN [3rd tank criteria] THEN 1 ELSE 0 END)
) AS MyRank
FROM (
SELECT *, COUNT(*) as `matches`
FROM [table1]
JOIN [table2] USING (id)
JOIN [table3] USING (id)
WHERE [criteria]
GROUP BY `id`
ORDER BY `matches` DESC
) AS `grouped`
ORDER BY MyRank DESC
LIMIT 100;
put it in the select
((CASE WHEN [1st rank criteria] THEN 3 ELSE 0 END) +
(CASE WHEN [2nd rank criteria] THEN 2 ELSE 0 END) +
(CASE WHEN [3rd tank criteria] THEN 1 ELSE 0 END)) as ranking