Sort Data in a Range MySQL - mysql

I have a table like below
SUBJECT - MARKS - SEMESTER
MATH - 50 - 1
SCIENCE - 60 - 1
ENGLISH - 70 - 1
MATH - 60 - 2
SCIENCE - 80 - 2
ENGLISH - 90 - 2
I want to produce a output like below. The problem is, even there is no data between 0-10 range I want 0 in all three columns. I am unable to achieve using "group by" and "sum". Do any of you have any idea
RANGE MATH SCIENCE ENGLISH
0-10 0 0 0
10-20 0 0 0
20-30 0 0 0
30-40 0 0 0
40-50 0 0 0
50-60 1 0 0
60-70 1 1 0
70-80 0 0 1
80-90 0 1 0
90-100 0 0 1

You can do this, but you need to define the ranges, either as a reference table or in the query. The rest is conditional aggregation:
select r.range,
sum(subject = 'MATH' and t.marks is not null) as Math,
sum(subject = 'SCIENCE' and t.marks is not null) as Science,
sum(subject = 'English' and t.marks is not null) as English
from ((select 0 as mins, 9.99 as maxs, '0-10' as range) union all
(select 10 as mins, 19.99 as maxs, '10-20' as range) union all
. . .
(select 90 as mins, 100 as maxs, '90-100' as range)
) left join
table t
on t.marks between r.mins and r.maxs
group by r.range
order by min(r.mins);

Related

Getting n x n matrix of counts of records based on the categorical values of multiple fields using SQL

I want to create simple n x n matrix where n is the every possible unique values of every categorical fields in my data, and the values in the matrix contains a count of records having a pair of two categorical values.
This would be more clearer with the following sample data (replication of my actual data)
user_id
gender
tier
age_group
20+ such fields
aaaa
male
tier1
15-24
...
bbbb
male
tier2
25-34
...
cccc
female
tier1
15-24
...
dddd
female
tier3
35-44
...
eeee
other
null
35-44
...
ffff
male
tier2
45+
...
...
...
...
...
...
In my actual table there are more than 20 categorical fields (such as Marital_status, Income_band, Education_level, Zone, etc.) and more than 500k records (user_id). Each fields can take 2 to 10 fixed categorical values.
The output I want is like following - [counts of records which satisfy any two criteria], for example no. of male user living in tier2 cities are 2. so value 2 at the intersection of male x tier2. And so on...
n x n
male
female
others
tier1
tier2
tier3
null
15-24
25-34
35-44
45+
total
male
3
0
0
1
2
0
0
1
1
0
1
3
female
0
2
0
1
0
1
0
1
0
1
0
2
others
0
0
1
0
0
0
1
0
0
1
0
1
tier1
1
1
0
2
0
0
0
2
0
0
0
2
tier2
2
0
0
tier3
0
1
0
null
0
0
1
15-24
1
1
0
25-34
1
0
0
35-44
0
1
1
45+
1
0
0
total
3
2
1
My use case would be getting the overlap between two categorical fields in terms of % of other.
I am not sure how easy or complex it is to get using SQL. I searched for relevant questions but surprisingly I couldn’t find any solution. Hope you can help me on it for SQL. Let me know if any clarification you need for the problem.
You have to find the sum of duplicate data then u can show accordingly in table.
Here is the query
SELECT
SUM(gender = 'male') as male,
SUM(gender = 'female') as female,
SUM(gender = 'others') as others,
SUM(tier = 'tier1') as tier1,
SUM(tier = 'tier2') as tier2,
SUM(tier = 'tier3') as tier3,
SUM(tier = 'null') as null,
SUM(age_group = '15-24') as age_15_24,
SUM(age_group = '25-34') as age_25_34,
SUM(age_group = '35-44') as age_35_44,
SUM(age_group = '45+') as age_45,
FROM users
You just have to replace users with your table name.

How to use count in sql based on a IF condition

From this table
groupId
flag
flagValue
1
0
500
2
0
100
1
1
10
2
1
50
3
0
100
1
1
200
3
1
1000
2
1
50
I need this result
groupId
flag1
flag0
valFlag1
valFlag0
totalFlags
1
2
1
210
500
3
2
2
1
100
100
3
3
1
1
1000
100
2
where
flag1 is number of times flag is 1 for a particular group
flag0 is number of times flag is 0 for a particular group
valFlag1 is sum of flagVal when flag is 1
valFlag0 is sum of flagVal when flag is 0
totalFlags is sum of total flags associated with a group
I am stuck as to how to actually count values based on an IF condition.
Anyhelp is appreciated. Thanks.
I have used a table named group_table with your values
Try using this:
SELECT
g.`groupId`,
SUM(g.`flag`=1 ) AS flag1,
SUM(g.`flag`=0) AS flag0,
SUM(CASE WHEN g.`flag`=1 THEN g.`flagValue` ELSE 0 END) AS valFalg1,
SUM(CASE WHEN g.`flag`=0 THEN g.`flagValue` ELSE 0 END) AS valFalg0,
COUNT(*) AS totalFlags
FROM
`group_table` g
GROUP BY g.`groupId`
If you have to use the IF,
SELECT
g.`groupId`,
IF(g.`flag`=1,1,0 ) AS flag1,
IF(g.`flag`=0,1,0) AS flag0,
SUM(IF(g.`flag`=1,g.`flagValue`,0 )) AS valFalg1,
SUM(IF(g.`flag`=0,g.`flagValue`,0 )) AS valFalg0,
COUNT(*) AS totalFlags
FROM
`group_table` g
GROUP BY g.`groupId`, flag1, flag0
They'll produce the same result

MySQL SELECT record immediately prior to range and select range

I have a MySQL table of states for three things, a,b and c
id a b c time
--------------------------
1 0 1 1 78
2 1 1 0 89
3 1 0 0 105
4 0 0 0 107
5 1 0 1 122
6 0 0 1 134
7 0 1 0 167
8 1 1 1 168
9 0 1 0 177
10 0 0 0 180
As an example, the bounds of time are chosen by the user as time>100
AND time<170
But I need to know the value of ‘a’ immediately prior to the 1st returned record. (where id=2)
I’m trying to find the most efficient way of creating this query, without resorting to 2 separate queries.
SELECT a, time FROM states WHERE time<100 order by time DESC limit 1
AND
SELECT a, time FROM states WHERE time>100 AND time<170 ORDER BY time ASC
To return a result set of ...
a time
1 89
1 105
0 107
1 122
0 134
0 167
0 168
Any advice would be gratefully received, thanks!
One method uses LEAD():
SELECT a, time
FROM (SELECT s.*, LEAD(time) OVER (ORDER BY time) as next_time
FROM states s
) s
WHERE next_time > 100 AND time < 170;
You can also use:
select s.*
from states s
where s.time >= (select s2.time from states s2 where s2.time <= 100 order by s2.time desc limit 1) and
s.time < 170;
This, alas, doesn't work when the subquery returns no values. That can be fixed, but it complicates the query.
However, your solution is actually fine (with union all):
(SELECT a, time
FROM states
WHERE time <= 100
ORDER BY time DESC
LIMIT 1
) UNION ALL
(SELECT a, time
FROM states
WHERE time > 100 AND time < 170
)
ORDER BY time ASC;
From a performance perspective, this should be okay if you have an index on time. This also readily handles the problem when there are no values 100 or less.

MySQL - How to Group by values and display in different colulmn on the same row

I have source table that I would like to sum quantities based on a specific value and display the sum for each value while grouping by an id.
animal_id oh co gender
10 5 1 M
20 10 5 F
10 15 2 F
30 5 0 C
10 5 4 M
20 10 0 F
I need an output of
animal_id Moh Mco Foh Fco Coh Cco
10 10 5 15 2 0 0
20 0 0 20 5 0 0
30 0 0 0 0 5 0
Each column will display the sum of each gender and each row will be grouped by the animal_id.
I tried to use the CASE like is MSSQL but it didn't sum per each gender.
Thinking PIVOT but I'm not very familiar with it.
I'm at a loss...
Try following:
SELECT animal_id,
SUM(IF(gender='M', oh, 0)) AS Moh, SUM(IF(gender='M', co, 0)) AS Mco,
SUM(IF(gender='F', oh, 0)) AS Foh, SUM(IF(gender='F', co, 0)) AS Fco,
SUM(IF(gender='C', oh, 0)) AS Coh, SUM(IF(gender='C', co, 0)) AS Cco
FROM anim_table
GROUP BY animal_id;
So key concept is to combine SUM() with IF()

Count numbers without Zero

I have a query who works correctly, but where I want to make a change.
The query counts all notes for id_service = 89.
The notes are numbers between 0 and 3 not more.
My query counts all those notes to get an Average.
All works fine.
The change what I want is, that he counts only the notes between 1 and 3 (not 0).
I don't know how I can do that.
Example:
This query count how much time I get the number 3 with all conditions.
Here the query:
SELECT round( avg( AVG =3 ) * count( AVG ) ) AS New
, sma_famille.famille
FROM (
SELECT ROUND( SUM( note ) / count( note ) ) AS AVG
, sma_famille.famille
, sma_agents.nom
FROM sma_notes
INNER JOIN sma_famille
ON sma_famille.id_service =89
INNER JOIN sma_agents
ON sma_notes.id_agent = sma_agents.id_agent
INNER JOIN sma_service_activite
ON sma_service_activite.id_activite = sma_notes.id_activite
AND sma_service_activite.id_famille = sma_famille.id_famille
AND sma_service_activite.id_service = sma_famille.id_service
GROUP BY sma_famille.famille, sma_agents.nom
) AS FN
LEFT JOIN sma_famille
ON sma_famille.id_service =89
AND FN.famille = sma_famille.famille
GROUP BY FN.famille
An example:
In Bio I can give 2 notes per persons like Bio Part1 and Bio Part2.
In my example I have two persons.
I give in Bio Part1 the note "3" for both and in Bio Part2 i don't give a note, so that there are the note "0"!
Here the result of my query:
That is what I get:
Note Math English Bio
1 0 0 0
2 0 0 2
3 0 0 0
That is what I want:
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
Without the note "0" I must get an average of 100% for the note "3".
Now i get:
Note Math English Bio
1 0
2 0
3 2
And not
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
How can i get the "0" in the resultset
Anybody an idea?
One way to get a count of values > 0 would be:
count(case when note > 0 then note end)
(Where note is 0, the case evaluates as null which is not counted.)
Although the simplest way might be
sum(sign(note))