How to get max value of a grouped counted variable in MySQL - mysql

I have a MySQL table like this;
recordID| netcall | sign | activity | netid
1 | group1 | wa1 | 1 | 20
2 | group2 | wa2 | 2 | 30
3 | group1 | wa2 | 1 | 20
4 | group2 | wa3 | 2 | 30
5 | group1 | wa1 | 1 | 40
6 | group3 | wa4 | 3 | 50
7 | group3 | wa4 | 3 | 50
8 | group1 | wa2 | 1 | 40
9 | group1 | wa1 | 1 | 40
10 | group2 | wa4 | 2 | 60
What I need from that is:
Netcall | count | activity | netid
Group1 | 3 | 1 | 40
Group2 | 2 | 2 | 30
Group3 | 2 | 3 | 50
I thought I could;
SELECT MAX(xx.mycount) AS MAXcount
FROM (SELECT COUNT(tt.sign) AS mycount ,tt.activity
FROM NetLog tt
WHERE ID <> 0
GROUP BY netcall) xx
But this only brings up the grand total not broken down by netcall. I don't see an example of this question but I'm sure there is one, I'm just asking it wrong.

Your example and desire output are too basic, you should try to expand so include more cases.
Right now you can get the desire output with:
SELECT `netcall`, COUNT(*) as `total`, MAX(`activity`) as `activity`
FROM t
GROUP BY `netcall`;
My guess is you can have different activities for group so you need multiples steps
Calculate the COUNT() for GROUP BY netcall, activity I call it q
Then see what is the MAX(total) for each netcall I call it p
Now you reuse q as o you have all the count, so just select the one with the max count.
SQL DEMO
SELECT o.`netcall`, o.total, o.`activity`
FROM (
SELECT `netcall`, COUNT(*) `total`, `activity`
FROM t
GROUP BY `netcall`, `activity`
) o
JOIN (
SELECT `netcall`, MAX(`total`) as `total`
FROM (
SELECT `netcall`, COUNT(*) `total`
FROM t
GROUP BY `netcall`, `activity`
) q
GROUP BY `netcall`
) p
ON o.`netcall` = p.`netcall`
AND o.`total` = p.`total`
With MySQL v8+ you can use cte and window function to simplify a little bit
with group_count as (
SELECT `netcall`, COUNT(*) as total, `activity`
FROM t
GROUP BY `netcall`, `activity`
), group_sort as (
SELECT `netcall`, total, `activity`,
RANK() OVER (PARTITION BY `netcall`, `activity` ORDER BY total DESC) as rnk
FROM group_count
)
SELECT *
FROM group_sort
WHERE rnk = 1

This question is asked (and answered) every day on SO; it even has its own chapter in the MySQL manual, but anyway...
SELECT a.netcall
, b.total
, a.activity
FROM netlog a
JOIN
( SELECT netcall
, MAX(record_id) record_id
, COUNT(*) total
FROM netlog
GROUP
BY netcall
) b
ON b.netcall = a.netcall
AND b.record_id = a.record_id

SELECT k.netcall, k.netID, MAX(k.logins) highest,
AVG(k.logins) average, netDate, activity
FROM
(SELECT netID, netcall, COUNT(*) logins, DATE(`logdate`) as netDate, activity
FROM NetLog
WHERE netID <> 0 AND status = 1
AND netcall <> '0' AND netcall <> ''
GROUP BY netcall, netID) k
GROUP BY netcall
ORDER BY highest DESC
Resulted in:
Net Call Highest Average Net ID Sub Net Of... ICS
214 309 Map Date Activity
MESN 65 41.5294 339 214 309 MAP 2017-09-03 MESN
W0KCN 34 14.9597 1 214 309 MAP 2016-03-15 KCNARES Weekly 2m Voice Net
W0ERH 31 31.0000 883 214 309 MAP 2018-10-12 Johnson Co. Radio Amateurs Club Meeting Net
KCABARC 29 22.3333 57 214 309 MAP 2016-10-10 KCA Blind Amateurs Weekly 2m Voice Net
....

Related

MySQL get latest consecutive rows set by date

I am retrieving following rows with query,
SELECT id, entry_id, DATE(entry_date)
FROM entries
WHERE entry_id = 51
ORDER BY entry_date DESC
+-----+----------+---------------------+
| id | entry_id | entry_date |
+-----+----------+---------------------+
| 84 | 51 | 2021-02-27 xx:xx:xx |<---
| 81 | 51 | 2021-02-26 xx:xx:xx | |
| 76 | 51 | 2021-02-25 xx:xx:xx | |-- consecutive set
| 74 | 51 | 2021-02-25 xx:xx:xx | |
| 73 | 51 | 2021-02-24 xx:xx:xx |<---
| 52 | 51 | 2021-02-20 xx:xx:xx |
| 44 | 51 | 2021-02-19 xx:xx:xx |
| 32 | 51 | 2021-02-18 xx:xx:xx |
| . | .. | ... |
| . | .. | ... |
+-----+----------+---------------------+
entry_date's data type is timestamp. The time does not matter here in entry_date. I am only concerned with the dates without time.
I want to get rows only with "latest consecutive dates" OR first and last date of the latest consecutive set for an "entry_id".
for example, for entry_id = 51, I want only rows,
+-----+----------+------------+
| id | entry_id | entry_date |
+-----+----------+------------+
| 84 | 51 | 2021-02-27 |
| 81 | 51 | 2021-02-26 |
| 76 | 51 | 2021-02-25 |
| 74 | 51 | 2021-02-25 |
| 73 | 51 | 2021-02-24 |
+-----+----------+------------+
OR I want to get first and last date of "latest consecutive dates" set for entry_id = 51
eg. in this case entry_date 2021-02-24 and 2021-02-27.
I don't have any experience with writing such queries. I can get all the records order by DESC for entry_id = 51 and write a script to get latest consecutive rows but since there are hundreds of thousands of rows which can be sometimes inefficient to process just to get latest consecutive rows.
Please note that there can be some entries with the same date (in this case: 2021-02-25) which are also considered in the result.
Edit: I am using MySQL 5.6.
This is a type of gaps-and-islands problem solved using lead() to determine where there is a gap of more than one day.
select entry_id, min(entry_date), max(entry_date)
from (select e.*,
sum(case when entry_date < next_entry_date - interval 1 day then 1 else 0 end) over (partition by entry_id order by entry_date desc) as grp
from (select e.*,
lead(entry_date) over (partition by entry_id order by entry_date) as next_entry_date
from entries e
) e
) e
where grp = 0
group by entry_id;
Then the cumulative sum is done in reverse order. So the last group has a cumulative sum of 0.
Here is a db<>fiddle.
You might try finding the gap dates with a self join, using group_concat to get them together, then using substring_index to split out the first two, then use that to get all rows bounded by the dates.
SELECT q.*
FROM entries q
JOIN (
SELECT entry_id,
SUBSTRING_INDEX(dts, ',', 1) AS upperDate,
SUBSTRING_INDEX(SUBSTRING_INDEX(dts, ',', 2), ',', -1) AS lowerDate
FROM (
SELECT x.entry_id,
GROUP_CONCAT(if(y.entry_date IS NULL, x.entry_date, NULL) ORDER BY x.entry_date DESC) AS dts
FROM entries x
LEFT JOIN entries y ON (
x.entry_id = y.entry_id AND
x.entry_date = y.entry_date - interval 1 DAY
)
GROUP BY 1
) z
) bounds ON (
q.entry_id = bounds.entry_id AND
q.entry_date <= bounds.upperDate AND
q.entry_date > bounds.lowerDate
)
You can avoid the self join with some variables, but that adds a certain level of complexity of its own and makes the logic a little harder to read and maintain.
SELECT entry_id, entry_date FROM (
SELECT entry_id, entry_date, elapsed,
NOT #latch AS keep,
#latch:= if(elapsed > 1, TRUE, #latch),
#latch := if(#currID <> entry_id, FALSE, #latch),
#currID := entry_id
FROM (
SELECT entry_id, entry_date,
TIMESTAMPDIFF(DAY, #prevDate, entry_date) AS elapsed,
#prevDate := entry_date
FROM (
SELECT entry_id, entry_date
FROM entries
JOIN (SELECT #currID := 0, #prevDate := null, #latch:=false) v
ORDER BY entry_id, entry_date ASC
) z
) y ORDER BY entry_id, entry_date DESC
) x WHERE keep
In MySQL 8, it can be done like this:
WITH e2 AS (
SELECT entry_id, entry_date
, COALESCE(LAG(DATE(entry_date))
OVER (PARTITION BY entry_id
ORDER BY entry_date DESC)
- DATE(entry_date), 0)
AS date_diff
FROM entries
), e3 AS (
SELECT e2.*
, MAX(date_diff)
OVER (PARTITION BY e2.entry_id
ORDER BY e2.entry_date DESC
ROWS UNBOUNDED PRECEDING)
AS max_diff
FROM e2
)
SELECT entry_id
, DATE(MIN(entry_date)) AS min_date
, DATE(MAX(entry_date)) AS max_date
FROM e3
WHERE max_diff <= 1
GROUP BY entry_id;
Result
entry_id
min_date
max_date
51
2021-02-24
2021-02-27
See DB Fiddle for demo.
If you want the WHERE entry_id = 51 condition, it should be added to the first WITH query.

MySQL select and sum from multiple table without duplicate it

i have 3 tables :
table1
code(Primary key) | name | quantity
B001 | sand | 50
B002 | nail | 100
B003 | paint | 10
=======
table2
code | qty_out
B001 | 2
B001 | 1
B001 | 20
B002 | 10
B002 | 30
=======
table3
code | qty_in
B001 | 1
B001 | 5
B002 | 5
B002 | 10
=======
Result that I want is:
table1
code | name | quantity | Out | In | total
B001 | sand | 50 | 23 | 6 | 33
B002 | nail | 100 | 40 | 15 | 75
B003 | paint | 10 | null/0 | null/0 | 10
I used this query :
SELECT table1.code, table1.name, table1.quantity, sum(table2.qty_out ) AS 'Out', sum( table3.qty_in ) AS 'In'
FROM table1
LEFT JOIN table2 ON table2.code = table1.code
LEFT JOIN table3 ON table3.code = table1.code
GROUP BY table1.code
ORDER BY table1.code ASC
In that query I get result like this...code B001 out 46 and in 18, code B002 out 80 and in 30, code B003 out null and in null
How to fix this?
use this query
select t.code,t.name,t.quantity,t.out,t.in,(t.out+t.in) as total
from (
SELECT table1.code, table1.name, table1.quantity,
( select sum(table2.qty_out)
from table2
where table1.code=table2.code ) as out,
( select sum(table3.qty_in)
from table3
where table3.code=table1.code ) as in
FROM table1
) as t
Using a subquery, a UNION clause and GROUPping, I built the following query :
SELECT
p.code,
p.name,
-- Using IFNULL to handle products without stock variation
IFNULL(SUM(q.q_in), 0) AS total_in,
IFNULL(SUM(q.q_out), 0) AS total_out,
-- Compute the new stock level
p.qty + IFNULL(SUM(q.q_in), 0) - IFNULL(SUM(q.q_out), 0) AS qty
FROM (
-- UNION (= concatenate) prod_in and prod_out tables
SELECT
product,
qty AS q_in,
0 AS q_out -- Enforce schema (otherwise, q_out is dropped)
FROM prod_in
UNION
SELECT
product,
0 AS q_in,
qty AS q_out
FROM prod_out
) AS q
-- Using a RIGHT join to show products without stock variations
RIGHT JOIN products AS p
ON p.code = q.product
-- Group by id to get the summarized view
GROUP BY q.product
Here's the query in a working SQLfiddle with your sample data

merge sum of counts from id in mysql

I not mysql expert and I need help to make count query, I need merge sum of count from id 5 and 11 to one count number = 286 and give platform name as GCS in this case.
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
FROM event e, lu_platform p
WHERE e.platform_id = p.id
AND p.id NOT IN ( 10, 15, 17, 18 )
AND e.sourcetype_id = 1
AND e.event_datetime BETWEEN '2013-11-4'
AND '2013-11-10' AND e.sender_id NOT IN ( 759, 73 )
GROUP BY p.id ORDER BY id;
+----+---------------------------+-------+
| id | platform | count |
+----+---------------------------+-------+
| 3 | GGG | 414 |
| 4 | KIKI | 156 |
| 5 | KJC | 284 |
| 6 | LOLO | 4 |
| 7 | MOD | 1147 |
| 8 | MARKT | 1049 |
| 11 | GCS | 2 |
| 12 | POLAR | 30 |
| 14 | GUAE | 145 |
+----+---------------------------+-------+
One possible way to do it - use a subquery, a sum, and IF function, or a case expression CASE WHEN THEN:
SELECT sum( case when id in ( 5, 11 ) then count else 0 end ) as count_5_11,
sum( if( id in ( 3, 4 ), count, 0 ) ) As count_3_4
FROM (
-- your query goes here
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
....
....
....
....
....
) AS some_alias
Try to use sub-query
SELECT SUM(count) as count, `GCG` as platform
FROM
(
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
FROM event e, lu_platform p
WHERE e.platform_id = p.id
AND p.id NOT IN ( 10, 15, 17, 18 )
AND e.sourcetype_id = 1
AND e.event_datetime BETWEEN '2013-11-4'
AND '2013-11-10' AND e.sender_id NOT IN ( 759, 73 )
GROUP BY p.id ORDER BY id;
) T
WHERE id IN (5,11)
Thank you for reply.
In the both above answers I get only one results instead of list. I think the best way is do this in application as was mentioned by sarwar026.
I noticed also that ISNULL(count(e.id), 0) is not working as I expected, platforms without records not returning 0, they are skipped.

Mysql Sql statement to Get Position of Student Ranked in order of marks scored

I need an sql that will give the position of the student ranked in order of marks scored in a specific examtyp e.g. CAT! only.the sql below gives the position of the student but does not distinguish the examtyp.it ranks without considering the examtyp.
res_id admNo stream examtyp termId marks grade points year
1 2129 0 CAT1 1 525 C 62 2013
2 4093 0 CAT1 1 569 B+ 69 2013
3 2129 0 CAT2 1 550 B+ 67 2013
4 4093 0 CAT2 1 556 B+ 68 2013
6 2129 0 FINAL 1 559 B+ 68 2013
7 2129 0 AVERAGE 1 545 B 66 2013
7 4093 0 FINAL 1 581 B+ 70 2013
8 4093 0 AVERAGE 1 569 B+ 69 2013
$sql = "SELECT 1 + (SELECT count(*) FROM $table a
WHERE a.total_marks > b.total_marks ) AS rank
FROM $table b WHERE admNo=? AND examCategory=? AND termId=? AND year=?
ORDER BY rank LIMIT 1";
$res = $this->db->query($sql, array($admNo, $examCategory, $term, $year));
This should work for you:
SELECT res_ID,
admNo,
stream,
examtyp,
termId,
grade,
points,
`year`,
Position
FROM ( SELECT #r:= CASE WHEN #e = examtyp THEN #r + CASE WHEN #p = points THEN 0 ELSE #i END ELSE 1 END Position,
#i:= CASE WHEN #p = points THEN #i + 1 ELSE 1 END incr,
#e:= Examtyp,
#p:= points,
res_ID,
admNo,
stream,
examtyp,
termId,
grade,
points,
`year`
FROM T,
(SELECT #e:= '') e,
(SELECT #r:= 0) r,
(SELECT #p:= 0) p,
(SELECT #i:= 0) i
ORDER BY examtyp, points
) T
WHERE T.admNo = 4093
AND T.Examtyp = 'CAT1'
It uses the same principle of using variables that has been suggested, however also partitions by examtyp, resetting the position to 0 for each new exam type, it also records the previous points to deal with ties, so if 3 people get the same mark they all get the same position.
Example on SQL Fiddle
Note in the bottom pane of the fiddle the results for AVERAGE are equal so both get position = 1
Try the Query
SET #rank=0;
select
#rank := #rank+1 AS rank
result_id,
marks_scored,
admNo,
Aggregate_points,
year
from tale_name
order by marks_scored DESC
Try this query
Query 1:
select
#rn:=if(#prv=examtyp, #rn+1, 1) as rId,
admNo,
#prv:=examtyp as exmtyp,
marks
from table1
join
(select #rn:=0,#prv:='') tmp
order by exmtyp, marks desc
SQL FIDDLE:
| RID | ADMNO | EXMTYP | MARKS |
---------------------------------
| 1 | 4093 | AVERAGE | 569 |
| 2 | 2129 | AVERAGE | 545 |
| 1 | 4093 | CAT1 | 569 |
| 2 | 2129 | CAT1 | 525 |
| 1 | 4093 | CAT2 | 556 |
| 2 | 2129 | CAT2 | 550 |
| 1 | 4093 | FINAL | 581 |
| 2 | 2129 | FINAL | 559 |
EDIT
Query 1:
select * from (
select
#rn:= #rn+1 as rId,
admNo,
examtyp,
marks
from table1
join
(select #rn:=0) tmp
where examtyp='CAT1'
order by examtyp, marks desc
) tmp where tmp.admNo=2129
SQL FIDDLE:
| RID | ADMNO | EXAMTYP | MARKS |
---------------------------------
| 2 | 2129 | CAT1 | 525 |
try this -
SELECT q1.rownum
FROM
(
SELECT *, #rownum:=#rownum + 1 AS rownum
FROM $table t, (SELECT #rownum:=0) r
WHERE examtyp = 'CAT1'
ORDER BY marks
) q1
WHERE q1.admNo=?
2) Since you modified the requirement to get equal ranks for same marks, u might need to do something like this -
SELECT q1.rownum
FROM
(
SELECT *, #rownum:=#rownum + 1 AS rownum
FROM
(SELECT DISTINCT marks FROM table1 t WHERE t.examtyp = 'CAT1' ORDER BY t.marks) q2,
(SELECT #rownum:=0) r
) q1,
table1 t2
WHERE
t2.examtyp = 'CAT1'
AND t2.marks=q1.marks
AND t2.admNo=?;
Above, you need to change examCategory at two places.
This is not the most optimized query..but it will do ur work.
3) as per your third requirement to get incremented count of next student, this might do the trick -
SELECT ROWNUM
FROM
(
SELECT q1.marks, min(q1.rownum) AS rownum
FROM
(
SELECT t1.marks, #rownum:=#rownum + 1 AS rownum
FROM
table1 t1,
(SELECT #rownum:=0) r
WHERE
t1.examtyp='CAT1'
ORDER BY t1.marks asc
) q1
GROUP BY q1.marks
) q2,
table1 t2
WHERE
t2.examtyp = 'CAT1'
AND t2.marks=q2.marks;
AND t2.admNo=?;

Mysql count and sum from two different tables

i have a problem with some querys in php and mysql:
I have 2 different tables with one field in common:
table 1
id | hits | num_g | cats | usr_id |active
1 | 10 | 11 | 1 | 53 | 1
2 | 13 | 16 | 3 | 53 | 1
1 | 10 | 22 | 1 | 22 | 1
1 | 10 | 21 | 3 | 22 | 1
1 | 2 | 6 | 2 | 11 | 1
1 | 11 | 1 | 1 | 11 | 1
table 2
id | usr_id | points
1 | 53 | 300
Now i use this statement to sum just the total from the table 1 every id count + 1 too
SELECT usr_id, COUNT( id ) + SUM( num_g + hits ) AS tot_h FROM table1 WHERE usr_id!='0' GROUP BY usr_id ASC LIMIT 0 , 15
and i get the total for each usr_id
usr_id| tot_h |
53 | 50
22 | 63
11 | 20
until here all is ok, now i have a second table with extra points (table2)
I try this:
SELECT usr_id, COUNT( id ) + SUM( num_g + hits ) + (SELECT points FROM table2 WHERE usr_id != '0' ) AS tot_h FROM table1 WHERE usr_id != '0' GROUP BY usr_id ASC LIMIT 0 , 15
but it seems to sum the 300 extra points to all users:
usr_id| tot_h |
53 | 350
22 | 363
11 | 320
Now how i can get the total like the first try but + the secon table in one statement? because now i have just one entry in the second table but i can be more there.
thanks for all the help.
hi thomas thanks for your reply, i think is in the right direction, but i'm getting weirds results, like
usr_id | tot_h
22 | NULL <== i think the null its because that usr_id as no value in the table2
53 | 1033
Its like the second user is getting all the the values. then i try this one:
SELECT table1.usr_id, COUNT( table1.id ) + SUM( table1.num_g + table1.hits + table2.points ) AS tot_h
FROM table1
LEFT JOIN table2 ON table2.usr_id = table1.usr_id
WHERE table1.usr_id != '0'
AND table2.usr_id = table1.usr_id
GROUP BY table1.usr_id ASC
Same result i just get the sum of all values and not by each user, i need something like this result:
usr_id | tot_h
53 | 53 <==== plus 300 points on table1
22 | 56 <==== plus 100 points on table2
/////////the result i need ////////////
usr_id | tot_h
53 | 353 <==== plus 300 points on table2
22 | 156 <==== plus 100 points on table2
I think the structure need to be something like this
Pseudo statements ;)
from table1 count all id to get the number of record where the usr_id are then sum hits + num_g and from table2 select the extra points where the usr_id are the same as table1 and get the result:
usr_id | tot_h
53 | 353
22 | 156
There is nothing in your subquery which calculates extra points to correlate it to the outer Table1. So, one solution is to add that correlation:
SELECT usr_id
, COUNT( id ) + SUM( num_g + hits )
+ (SELECT points
FROM table2
WHERE table2.usr_id = table1.usr_id ) AS tot_h
FROM table1
WHERE usr_id != '0'
GROUP BY usr_id ASC
LIMIT 0 , 15
Another solution would be to simply join to it directly:
SELECT table1.usr_id
, COUNT( table1.id )
+ SUM( table1.num_g + table1.hits + table2.points )
AS tot_h
FROM table1
Left Join table2
On table2.usr_id = table1.usr_id
WHERE table1.usr_id != '0'
GROUP BY table1.usr_id ASC
LIMIT 0 , 15
i think get the solution, i dont know if its the best but it works for me, if you know a way to optimize this i really apreciate it.
SELECT usr_id , COUNT( id ) + SUM( num_g + hits )as sumtot ,
(SELECT points FROM table2 WHERE usr_id = table1.usr_id ) AS tot_h
FROM table1
WHERE usr_id != '0'
GROUP BY usr_id ASC
with this i get something like this
usr_id |sumtot | tot_h
5 |557 | NULL
53 |2217 | 300
So then i just sum the result and show it in a while loop.
<?php
//some mysql here
//then the while loop
// and then the final sum
$final_result=$r_rank['tot_h']+$r_rank['sumtot'];
?>
Thanks a lot for your help thomas :)