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

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;

Related

MySQL SELECT query with another SELECT

I have the next sql query:
SELECT CONCAT(v.p_sery, v.p_id) AS sery,
(SELECT COUNT(1) FROM v where p_delivery_result = 1) AS delivery_count,
(SELECT COUNT(1) FROM v where p_delivery_result = 2) AS ND1,
(SELECT COUNT(1) FROM v where p_delivery_result = 3) AS ND2,
(SELECT COUNT(1) FROM v where p_delivery_result = 4) AS ND3,
(SELECT COUNT(1) FROM v where p_delivery_result = 5) AS ND4,
(SELECT COUNT(1) FROM v where p_delivery_result = 6) AS ND5,
(SELECT COUNT(1) FROM v where p_delivery_result = 7) AS ND6,
(SELECT COUNT(1) FROM v where p_delivery_result = 8) AS ND7
FROM (
SELECT p_sery, p_id, d.p_delivery_result
FROM registries AS a, registry_regulations r, delivery d
WHERE a.p_id = r.registry_id AND d.p_id = r.regulation_id AND (SELECT
STR_TO_DATE(a.p_date_created, '%Y-%m-%d') BETWEEN '2017-04-01' AND '2017-06-01')
) as v;
But this not working.
Error: Table v doesn't exist
What I do wrong?
I have this tables:
And I want get the count of one of the status in table delivery
You cannot access the derived table from the context of a correlated subquery. Try this query instead:
SELECT CONCAT(v.p_sery, v.p_id) AS sery,
COUNT(CASE WHEN p_delivery_result = 1 THEN 1 END) AS delivery_count,
COUNT(CASE WHEN p_delivery_result = 2 THEN 1 END) AS ND1,
COUNT(CASE WHEN p_delivery_result = 3 THEN 1 END) AS ND2,
COUNT(CASE WHEN p_delivery_result = 4 THEN 1 END) AS ND3,
COUNT(CASE WHEN p_delivery_result = 5 THEN 1 END) AS ND4,
COUNT(CASE WHEN p_delivery_result = 6 THEN 1 END) AS ND5,
COUNT(CASE WHEN p_delivery_result = 7 THEN 1 END) AS ND6,
COUNT(CASE WHEN p_delivery_result = 8 THEN 1 END) AS ND7
FROM (
SELECT p_sery, p_id, d.p_delivery_result
FROM registries AS a
JOIN registry_regulations r ON a.p_id = r.registry_id
JOIN delivery d d.p_id = r.regulation_id
WHERE STR_TO_DATE(a.p_date_created, '%Y-%m-%d')
BETWEEN '2017-04-01' AND '2017-06-01') as v;
Note: Always uses modern, explicit JOIN syntax instead of old-fashioned, implicit syntax.
just remove as before V
FROM (
SELECT p_sery, p_id, d.p_delivery_result
FROM registries AS a, registry_regulations r, delivery d
WHERE a.p_id = r.registry_id AND d.p_id = r.regulation_id AND (SELECT
STR_TO_DATE(a.p_date_created, '%Y-%m-%d') BETWEEN '2017-04-01' AND '2017-06-01')
) v

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 IF statement return issues

I have this query where the IF lines are only partially returning correct result:
SELECT
COALESCE((SELECT COUNT(ID) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID = USERS.ID),0) AS ADDED,
COALESCE((SELECT SUM(CASE WHEN USERS_BUCKETS.STATUS='Completed' THEN 1 ELSE 0 END) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID = USERS.ID),0) AS DONE,
COALESCE((SELECT COUNT(ID) FROM USERS_LIKES WHERE USERS_LIKES.USERID = USERS.ID),0) AS NUM_LIKES,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.USER_ID=USERS.ID),0) AS FOLLOWING,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.FOLLOW_ID=USERS.ID),0) AS FOLLOWERS,
(SELECT IF(ADDED >= 5,1,0)) AS IFADDED,
(SELECT IF(DONE >= 3,1,0)) AS IFDONE,
(SELECT IF(NUM_LIKES >= 5,1,0)) AS IFNUM_LIKES,
(SELECT IF(FOLLOWING >= 5,1,0)) AS IFFOLLOWING,
(SELECT IF(FOLLOWERS >= 3,1,0)) AS IFFOLLOWERS,
(SELECT IF(ADDED >= 5,1,0) + IF(DONE >= 3,1,0) + IF(NUM_LIKES >= 5,1,0) + IF(FOLLOWING >= 5,1,0) + IF(FOLLOWERS >= 3,1,0)) AS PROGRESS
FROM USERS
WHERE USERS.ID=?
Result:
ADDED: 20
IFADDED: 1
DONE: 9
IFDONE: 0 //should be 1
NUM_LIKES: 11
IFNUM_LIKES: 1
FOLLOWING: 11
IFFOLLOWING: 0 //should be 1
FOLLOWERS: 10
IFFOLLOWERS: 0 //should be 1
PROGRESS: 2
What's wrong? All IF values should be 1 and PROGRESS should be 5.
Note: I am using PDO in php, but I don't think that matters at all.
I have no idea why your code is working. The columns are definitely not the column aliases defined earlier in the query.
To do what you want, use a subquery and simplify the logic:
SELECT u.*, (ADDED >= 5) AS IFADDED,
(DONE >= 3) AS IFDONE,
(NUM_LIKES >= 5) AS IFNUM_LIKES,
(FOLLOWING >= 5) AS IFFOLLOWING,
(FOLLOWERS >= 3) AS IFFOLLOWERS,
((ADDED >= 5) + (DONE >= 3) + (NUM_LIKES >= 5) + (FOLLOWING >= 5) +
(FOLLOWERS >= 3)
) AS PROGRESS
FROM (SELECT COALESCE((SELECT COUNT(ID) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID = USERS.ID),0) AS ADDED,
COALESCE((SELECT SUM(CASE WHEN USERS_BUCKETS.STATUS='Completed' THEN 1 ELSE 0 END) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID = USERS.ID),0) AS DONE,
COALESCE((SELECT COUNT(ID) FROM USERS_LIKES WHERE USERS_LIKES.USERID = USERS.ID),0) AS NUM_LIKES,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.USER_ID=USERS.ID),0) AS FOLLOWING,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.FOLLOW_ID=USERS.ID),0) AS FOLLOWERS
FROM USERS
WHERE USERS.ID = ?
) u;
This takes advantage of the fact that MySQL treats booleans as integers in a numeric context, with "1" for true and "0" for false.
EDIT: I should also note that the coalesce() is unnecessary because count() will return 0 when there are no matches.

Mysql Ranking on on Left JOIN

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

Adding columns you created in mysql?

So say I have a mysql statement like this:
SELECT username, #n := #n + 1 ranking, `1st places`, `2nd places`, `3rd places`, `top5`, `top3
FROM
(
SELECT username,
SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) `1st places`,
SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) `2nd places`,
SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) `3rd places`,
SUM(CASE WHEN rating < 6 THEN 5 ELSE 0 END) `top5`,
SUM(CASE WHEN rating < 4 THEN 5 ELSE 0 END) `top3`
FROM Table1
GROUP BY username
ORDER BY `1st places` DESC
) q, (SELECT #n := 0) n
How can I add up the columns that I created called, '1st places' + '2nd places' + '3rd places' in this sql statement and create another column called "total finishes"? Seems easy, but I cannot seem to figure it out.
You can just add them together and give an alias to that extra column:-
SELECT username, #n := #n + 1 ranking, `1st places`, `2nd places`, `3rd places`, `top5`, `top3, `1st places` + `2nd places` + `3rd places` AS `total finishes`
FROM
(
SELECT username,
SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) `1st places`,
SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) `2nd places`,
SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) `3rd places`,
SUM(CASE WHEN rating < 6 THEN 5 ELSE 0 END) `top5`,
SUM(CASE WHEN rating < 4 THEN 5 ELSE 0 END) `top3`
FROM Table1
GROUP BY username
ORDER BY `1st places` DESC
) q, (SELECT #n := 0) n
I'm pretty sure that you can't re-use a field with a calculated value in the same SELECT query, however wrapping into an additional sub-query may be an option:
SELECT username, #n := #n + 1 ranking, `1st places`, `2nd places`, `3rd places`, `top5`, `top3
FROM
(
SELECT
*,
`1st places`+`2nd places`+`3rd places` `total finishes`
FROM (
SELECT username,
SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) `1st places`,
SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) `2nd places`,
SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) `3rd places`,
SUM(CASE WHEN rating < 6 THEN 5 ELSE 0 END) `top5`,
SUM(CASE WHEN rating < 4 THEN 5 ELSE 0 END) `top3`
FROM Table1
GROUP BY username
ORDER BY `1st places` DESC
)
)
q, (SELECT #n := 0) n
Haven't tried, still...