Sql multiple max value in different dimensions - mysql
I am trying to find a max number of the value but the thing is my code choose only max value even though values are equal so how can I find max value and if there are same make a two rows
Here is the example:
(Ps; data is fake just for example question please let me if something is wrong)
select class.name as className,
school.schoolName as schoolName,
count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, schoolName
order by schoolName
and result is:
className | SchoolNAme | TotalNum
-----------|------------|----------
Math | A | 9
Bio | A | 2
History | A | 7
Music | A | 9
Math | B | 22
English | B | 8
Music | B | 1
History | B | 2
Geo | B | 2
Bio | B | 3
Math | C | 15
Geo | C | 2
and I found each school max number like this:
select ClassName,
SchoolName,
max(totalNumber) as Total
from
(
select class.name as className,
school.schoolName as schoolName,
count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, schoolName
order by schoolName
) t1
group by schoolName
and this is the result I have:
className | SchoolNAme | Total
-----------|------------|----------
Math | A | 9
Math | B | 22
Math | C | 15
but the problem I have as we can see two total number is 9 for school A
so how can I show all max number if they are equal.
this is what I want;
className | SchoolNAme | Total
-----------|------------|----------
Math | A | 9
Bio | A | 9
Math | B | 22
Math | C | 15
Thanks
Max will show 1 row per group, as you are grouping by school name and there are 3 schools you will get 3 groups and therefore the 3 rows returned.
School A class Bio appears from your data to have only 2 rows (count counts the rows per group) so Bio A 9 will never be returned, so the result wanted will never be obtainable. I guess you really expect Music A 9 instead along with Math A 9.
The School table is actually redundant in this case (based upon interpretation of the join and the ensuing data i.e. count is count the number of joined rows and thus the number of class rows).
As such i believe the following produces the results that you are after :-
WITH
cte1 AS
(SELECT name,schoolName, count() AS cnt FROM class
GROUP BY name,schoolName),
cte2 AS
(SELECT name, schoolName, max(cnt) AS m FROM cte1
GROUP BY schoolname)
-- SELECT * FROM cte2
SELECT cte1.name, cte1.schoolName, cte1.cnt FROM cte1
WHERE cte1.cnt = (SELECT m FROM cte2 WHERE cte2.schoolname = cte1.schoolname)
ORDER BY schoolName, name
;
The above was tested using the following :-
DROP TABLE IF EXISTS school;
DROP TABLE IF EXISTS class;
CREATE TABLE IF NOT EXISTS school (schoolName TEXT, quantity INTEGER);
CREATE TABLE IF NOT EXISTS class (name TEXT, schoolName);
INSERT INTO school VALUES ('A',1),('B',1),('C',1);
INSERT INTO class VALUES
('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),
('Bio','A'),('Bio','A'),
('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),
('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),
('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),
('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),
('Music','B'),
('History','B'),('History','B'),
('Geo','B'),('Geo','B'),
('Bio','B'),('Bio','B'),('Bio','B'),
('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),
('Geo','C'),('Geo','C')
;
select class.name as className, school.schoolName as schoolName, count() as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, school.schoolName
order by schoolName, totalNumber DESC
;
select ClassName, SchoolName, max(totalNumber) as Total
from
(
select class.name as className, school.schoolName as schoolName, count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname,school.schoolname
order by schoolName
)
group by schoolName;
WITH
cte1 AS
(SELECT name,schoolName, count() AS cnt FROM class
GROUP BY name,schoolName),
cte2 AS
(SELECT name, schoolName, max(cnt) AS m FROM cte1
GROUP BY schoolname)
-- SELECT * FROM cte2
SELECT cte1.name, cte1.schoolName, cte1.cnt FROM cte1
WHERE cte1.cnt = (SELECT m FROM cte2 WHERE cte2.schoolname = cte1.schoolname)
ORDER BY schoolName, name
;
Results
First Query
The first query is your query, the results match the results in the question :-
Second Query
Again this matches the unwanted/wrong results from your question :-
Third Query
This I believe matches what you require :-
Related
Finding users with at least one of every item
For example, I have the following table called, Information user_id | item ------------------------- 45 | camera 36 | smartphone 23 | camera 1 | glucose monitor 3 | smartwatch 2 | smartphone 7 | smartphone 2 | camera 2 | glucose monitor 2 | smartwatch How can I check which user_id has at least one of every item? The following items will not be static and may be different everytime. However in this example there are 4 unique items: camera, smartphone, smartwatch, glucose monitor Expected Result: Because user_id : 2 has at least one of every item, the result will be: user_id 2 Here is what I attempted at so far, however if the list of items changes from 4 unique items to 3 unique items, I don't think it works anymore. SELECT * FROM Information GROUP BY Information.user_id having count(DISTINCT item) >= 4
One approach would be to aggregate by user_id, and then assert that the distinct item_id count matches the total distinct item_id count from the entire table. SELECT user_id FROM Information GROUP BY user_id HAVING COUNT(DISTINCT item_id) = (SELECT COUNT(DISTINCT item_id) FROM Information);
You can try to use self-join by count and total count SELECT t1.user_id FROM ( SELECT user_id,COUNT(DISTINCT item) cnt FROM T GROUP BY user_id ) t1 JOIN (SELECT COUNT(DISTINCT item) cnt FROM T) t2 WHERE t1.cnt = t2.cnt or exists Query 1: SELECT t1.user_id FROM ( SELECT user_id,COUNT(DISTINCT item) cnt FROM T GROUP BY user_id ) t1 WHERE exists( SELECT 1 FROM T tt HAVING COUNT(DISTINCT tt.item) = t1.cnt ) Results: | user_id | |---------| | 2 |
One more way of solving this problem is by using CTE and dense_rank function. This also gives better performance on MySQL. The Dense_Rank function ranks every item among users. I count the number of distinct items and say pick the users who have the maximum number of distinct items. With Main as ( Select user_id ,item ,Dense_Rank () over ( Partition by user_id Order by item ) as Dense_item From information ) Select user_id From Main Where Dense_item = ( Select Count(Distinct item) from information);
Group by names desc - get last entered values for a grouped name
I have an table like that: id | name | v (lvl) 11 | Jane | 6 12 | John | 5 13 | Jane | 6 14 | John | 5 15 | Jane | 7 16 | Jane | 5 In my autocomplete form now id like to group the names but get the last value (value with biggest id). In the example above would be Jane | 5 I tried with combinations like distinct, group by, order by. But im always get Jane | 6 or grouped like this and reversed: Jane | 6 Jane | 7 Jane | 5 I would need something like this: SELECT name,lvl FROM ( SELECT DISTINCT name, lvl FROM pora WHERE name LIKE 'Jane' ORDER BY lvl DESC ) GROUP BY name EDIT: I won't get the highest lvl, i want get the lvl of the highest id, grouped by name. Thats all. My example above would be the best explanation what i like to get. In the inner query i change the order to DESC for all and in the outer i group it by names. But i get an error for this. EDIT 2 I finally did at my own. The correct solution (i was already close): SELECT a.name, a.lvl FROM ( SELECT DISTINCT name, lvl FROM pora WHERE name LIKE 'Jane' ORDER BY id DESC )as a GROUP BY name
LIKE without % is just = SELECT * FROM yourTable WHERE name = 'Jane' ORDER BY id DESC LIMIT 1 But because you mention autocomplete functionality you should use: WHERE name LIKE 'Jane%'
To have the latest, you need to have a field dateAdded which stores the date you ran the insert command. Following which, you use MAX(dateAdded) to get the latest ID (since, as you mentioned, it may decrease as well) UPDATE: if ID doesn't decrease, you can always use MAX(ID) SELECT MAX(id), v from tablename where name = 'Jane' UPDATE: This has been tested: SELECT ID, v from tableName where ID = (SELECT MAX(ID) as ID from tableName where name like '%Jane%')
Try the following query (h/t #lamak) WITH CTE AS ( SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY [id] DESC) FROM poro ) SELECT * FROM CTE WHERE RN = 1
mysql "and" logic within result set
Say I have a data set like the following: table foo id | employeeType | employeeID ------------------------- 1 | Developer | 1 2 | Developer | 2 3 | Developer | 3 4 | Manager | 1 5 | Manager | 4 6 | Manager | 5 7 | CEO | 1 8 | CEO | 6 and I wanted to run a query that would return all the employeeids (along with the employeeTypes) where there is a common employee id between all employeeTypes (that's the 'and' logic. ONly employeeIDs that have all employeeTypes will return. employeeType = Developer and employeeType=Manager and employeeType=CEO). For the data above the example output would be result table id | employeeType | employeeID ------------------------- 1 | Developer | 1 4 | Manager | 1 7 | CEO | 1 I was able to do this when I only had only TWO employeeTypes by self joining the table like this. select * from foo as fooOne join foo as fooTwo on fooOne.employeeID = fooTwo.employeeID AND fooOne.employeeType <> fooTwo.employeeType that query returns a result set with values from fooTwo when the 'and' logic matches, but again, only for two types of employees. My real use case scenario dictates that I need to be able to handle a variable number of employeeTypes (3, 4, 5, etc...) Any thoughts on this would be greatly appreciated.
This should return the rows that you want: SELECT foo.* FROM foo WHERE employeeID IN ( SELECT employeeID FROM foo GROUP BY employeeID HAVING COUNT(DISTINCT employeeType) = (SELECT COUNT(DISTINCT employeeType) FROM foo) ) Please see a fiddle here. The inner query will return the number of distinct employee types: (SELECT COUNT(DISTINCT employeeType) FROM foo) The middle query will return all the employee IDs that have the maximum number of employee types: SELECT employeeID FROM foo GROUP BY employeeID HAVING COUNT(DISTINCT employeeType) = (SELECT COUNT(DISTINCT employeeType) FROM foo) and the outer query will return the whole rows.
You can try a subquery to make it dynamic SELECT employeeID, employeeType FROM foo WHERE employeeID IN ( SELECT employeeID FROM foo GROUP BY employeeID HAVING COUNT(DISTINCT employeeType) = (SELECT COUNT(DISTINCT employeeType) FROM foo) )
I agree that this might be looked down as a very inefficient/hacky way of doing things, but this should still get the job done. And frankly, I can't see any other way out of this. SELECT * FROM ( SELECT EMPLOYEE_ID, GROUP_CONCAT(DISTINCT EmployeeType ORDER BY EmployeeType) AS Roles FROM EMPLOYEES GROUP BY EMPLOYEE_ID ) EMPLOYEE_ROLES WHERE EMPLOYEE_ROLES.Roles = 'CEO,Developer,Manager'; Note that the comma separated list of roles provided in the end is in the alphabetical order.
Count number of distinct rows for multiple values
Let's consider this table specifing how many times a person bought a property. +--------+----------+ | user | property | +--------+----------+ | john | car | | john | car | | john | house | | peter | car | | peter | car | | amanda | house | | amanda | house | +--------+----------+ I need to know how many times a car was bought once, how many times a house was bought once, etc. Something like this: +----------+---+---+ | property | 1 | 2 | +----------+---+---+ | cars | 4 | 2 | | house | 3 | 1 | +----------+---+---+ How many times a car was bought? Four, two for peter and two for john. How many times a car was bought twice? Two, for the same guys. How many times a house was bought? Three, two for amanda and once for john. How many times a house was bought twice? Only once, for amanda Is this possible to do this only using SQL queries? I don't care about performance or hackish ways. There are more than two frequencies. There's a fixed set of time a person can buy a property (5) so it's not problem to specify the columns manually in the query. I mean there's not problem doing something like: SELECT /* ... */ AS 1, /* ... */ AS 2, /* ... */, AS 3 /* ... */
SELECT DISTINCT #pr := prop, (SELECT COUNT(1) FROM tbl WHERE prop = #pr LIMIT 1), (SELECT COUNT(1) FROM (SELECT *, COUNT(*) cnt FROM tbl GROUP BY usr, prop HAVING cnt = 2) as tmp WHERE `tmp`.prop = #pr LIMIT 1) FROM tbl; Yes, it is not the best method; but hey, you get the answers as desired. Also, it'll generate the results for any kind of property in your table. The fiddle link lies here. P.S.: 60 tries O_O
I am here since you posted the question. Good one... Here is a way to do it exactly as you asked for, with just groups and counts. The trick is that I concatenate the user and property columns to produce a unique "id" for each, if we could call it that. It should work independently of the count of purchases. SELECT C.`property`, COUNT(C.`property`), D.`pcount` from `purchases` C LEFT JOIN( SELECT A.`property`, B.`pcount` FROM `purchases` A LEFT JOIN ( SELECT `property`, CONCAT(`user`, `property`) as conc, COUNT(CONCAT(`user`, `property`)) as pcount FROM `purchases` GROUP BY CONCAT(`user`, `property`) ) B ON A.`property` = B.`property` GROUP BY B.pcount ) D ON C.`property` = D.`property` GROUP BY C.`property`
SQL Fiddle MySQL 5.5.30 Schema Setup: CREATE TABLE Table1 (`user` varchar(6), `property` varchar(5)) ; INSERT INTO Table1 (`user`, `property`) VALUES ('john', 'car'), ('john', 'car'), ('john', 'house'), ('peter', 'car'), ('peter', 'car'), ('amanda', 'house'), ('amanda', 'house') ; Query 1: select t.property, t.total, c1.cnt as c1, c2.cnt as c2, c3.cnt as c3 from (select t.property , count(t.property) as total from Table1 t group by t.property ) as t left join ( select property, count(*) as cnt from ( select property, user, count(*) as cnt from table1 group by property, user having count(*) = 1 ) as i1 group by property ) as c1 on t.property = c1.property left join ( select property, count(*) as cnt from ( select property, user, count(*) as cnt from table1 group by property, user having count(*) = 2 ) as i2 group by property ) as c2 on t.property = c2.property left join ( select property, count(*) as cnt from ( select property, user, count(*) as cnt from table1 group by property, user having count(*) = 3 ) as i3 group by property ) as c3 on t.property = c3.property Results: | PROPERTY | TOTAL | C1 | C2 | C3 | ------------------------------------------- | car | 4 | (null) | 2 | (null) | | house | 3 | 1 | 1 | (null) |
You may try following. SELECT COUNT(TABLE1.PROPERTY) AS COUNT, PROPERTY.USER FROM TABLE1 INNER JOIN (SELECT DISTINCT PROPERTY, USER FROM TABLE1) AS PROPERTY ON PROPERTY.PROPERTY = TABLE1.PROPERTY AND PROPERTY.USER = TABLE1.USER GROUP BY TABLE1.USER, PROPERTY.PROPERTRY tested similar in MySQL
try this SELECT property , count(property) as bought_total , count(distinct(user)) bought_per_user FROM Table1 GROUP BY property the output will be like that PROPERTY | BOUGHT_TOTAL | BOUGHT_PER_USER ________________________________________________________ car | 4 | 2 house | 3 | 2 DEMO SQL FIDDLE HERE
You should be able to do this with sub-selects. SELECT property, user, COUNT(*) FROM purchases GROUP BY property, user; will return you the full set of grouped data that you want. You then need to look at the different frequencies: SELECT property, freq, COUNT(*) FROM (SELECT property, user, COUNT(*) freq FROM purchases GROUP BY property, user) AS foo GROUP BY property, freq; It's not quite in the format that you illustrated but it returns the data
I hope this can help u.....let us create one table first: create table prop(user varchar(max),property varchar(max)) insert into prop values('john','car'),insert into prop values('john','car'), insert into prop values('john','house'),insert into prop values('peter','car'), insert into prop values('peter','car'),insert into prop values('amanda','house'), insert into prop values('amanda','house') 1)how many times car was bought? ANS: select count(property) from prop where property = 'car' (4) 2)How many times a car was bought twice? ANS: select user,COUNT(property) from prop where property = 'car' group by user having COUNT(property) = 2 2-john 2-peter 3)How many times a house was bought? ANS: select COUNT(property) from prop where property = 'house' (3) 4)How many times a house was bought twice? ANS: select user,COUNT(property) from prop where property='house' group by user having COUNT(property)< =2 2-amanda 1-john
Query to Segment Results Based on Equal Sets of Column Value
I'd like to construct a single query (or as few as possible) to group a data set. So given a number of buckets, I'd like to return results based on a specific column. So given a column called score which is a double which contains: 90.00 91.00 94.00 96.00 98.00 99.00 I'd like to be able to use a GROUP BY clause with a function like: SELECT MIN(score), MAX(score), SUM(score) FROM table GROUP BY BUCKETS(score, 3) Ideally this would return 3 rows (grouping the results into 3 buckets with as close to equal count in each group as is possible): 90.00, 91.00, 181.00 94.00, 96.00, 190.00 98.00, 99.00, 197.00 Is there some function that would do this? I'd like to avoid returning all the rows and figuring out the bucket segments myself. Dave
create table test ( id int not null auto_increment primary key, val decimal(4,2) ) engine = myisam; insert into test (val) values (90.00), (91.00), (94.00), (96.00), (98.00), (99.00); select min(val) as lower,max(val) as higher,sum(val) as total from ( select id,val,#row:=#row+1 as row from test,(select #row:=0) as r order by id ) as t group by ceil(row/2) +-------+--------+--------+ | lower | higher | total | +-------+--------+--------+ | 90.00 | 91.00 | 181.00 | | 94.00 | 96.00 | 190.00 | | 98.00 | 99.00 | 197.00 | +-------+--------+--------+ 3 rows in set (0.00 sec) Unluckily mysql doesn't have analytical function like rownum(), so you have to use some variable to emulate it. Once you do it, you can simply use ceil() function in order to group every tot rows as you like. Hope that it helps despite my english. set #r = (select count(*) from test); select min(val) as lower,max(val) as higher,sum(val) as total from ( select id,val,#row:=#row+1 as row from test,(select #row:=0) as r order by id ) as t group by ceil(row/ceil(#r/3)) or, with a single query select min(val) as lower,max(val) as higher,sum(val) as total from ( select id,val,#row:=#row+1 as row,tot from test,(select count(*) as tot from test) as t2,(select #row:=0) as r order by id ) as t group by ceil(row/ceil(tot/3))