SQL Occurrence Comparison? - mysql

**Above is a picture of this particular table*
I need to write a query for a database that lists the name of a department for the department that controls the most projects.
In my database, departments are identified by dnums.
So my question is, how can I write something that checks for the greatest occurrence of a Dnum in SQL? Because that's how I will identify the department that controls the most projects.
I've tried several different queries, but none of them work properly.
Could anyone explain a method that could compare occurrences?

You know already how to count per department:
select dnum, count(*) from project group by dnum;
In SQL Server it is easy to select the dnum(s) with the maximum occurrences; you order by count descending and take the top row(s) using TOP() WITH TIES.
select top(1) with ties dnum from project group by dnum order by count(*) desc;
(In standard SQL that would be order by count(*) desc fetch 1 row with ties).
In standard SQL (and SQL Server) you also have the option of ranking your records per count:
select dnum
from (select dnum, rank() over (order by count(*) desc) as rnk from project) ranked
where rnk = 1;
MySQL doesn't give you any of these options, lacking both a WITH TIES clause and analytic functions such as RANK.
So in MySQL you would not find the departments with the maximum count in one step, but only the maximum count alone first. You would get the according department(s) only in a second step.
Two approaches here:
select count(*) from project group by dnum order by count(*) desc limit 1;
or
select max(cnt) from (select count(*) as cnt from project group by dnum) counted;
Then join the counted departments again:
select p.dnum
from
(
select count(*) as cnt
from project
group by dnum
order by count(*) desc limit 1
) m
(
select dnum, count(*) as cnt
from project
group by dnum
) p on p.cnt = m.cnt;
The last step is the same in both DBMS:
select dname
from departments
where dnumber in (select dnum ...);
(Or join the departments table instead, so you can show both name and count.)

You can use the COUNT function:
SELECT dnum, COUNT(*)
FROM project
GROUP BY dnum
ORDER BY COUNT(*)
If you need the department_name, you'll have to join to your department table (assuming you have one). It could look something like this:
SELECT d.dnum, d.name, COUNT(p.pnumber)
FROM department d
INNER JOIN projects p ON d.dnum = p.dnum
GROUP BY d.dnum, d.name
ORDER BY COUNT(p.pnumber)

Related

Invalid use of group function for mysql

I am getting this error but I don't understand why my query doesn't work. Can someone please give me a hand?
The question is this:
SELECT activity
FROM Friends
GROUP BY activity
HAVING COUNT(activity) > MIN(COUNT(activity))
AND COUNT(activity) < MAX(COUNT(activity))
My idea is that as long as the count of the activity is larger than the activity that has the minimum count and less than the activity that has the maximum count, it should be returned. But I am having "Invalid use of group function" error which I don't understand. One possible thing that I could think of is that the parts that I am comparing with the COUNT(activity) have to be a number that is selected from the table instead of a part that has "MIN" or "MAX". But I don't understand why as they both look like the same number to me.
If your version of MySql is less than 8.0, the following will work:
select activity, count(*) as cnt from Friends group by activity
having cnt not in (
select max(cnt) as cnt from (
select activity, count(*) as cnt from Friends group by activity
) sq1
union
select min(cnt) as cnt from (
select activity, count(*) as cnt from Friends group by activity
) sq2
);
activity
cnt
Singing
2
View on DB Fiddle
Since the above SQL references the same select 3 times, namely select activity, count(*) as cnt from Friends group by activity, you might consider creating a quasi-temporary table (but there is the overhead in creating such a table to consider -- for the number of rows you actually presented, this would run more slowly):
create table t as select activity, count(*) as cnt from Friends group by activity;
select activity from t where cnt not in (
select max(cnt) as cnt from t
union
select min(cnt) as cnt from t
);
drop table t;
activity
Singing
View on DB Fiddle
If you just want data from friends table then you can use the analytical function (My sql 8.0 or higher) as follows:
select activity from
(SELECT activity, dense_rank() over (order by count(*)) as rn_asc,
dense_rank() over (order by count(*) desc) as rn_desc
FROM Friends
GROUP BY activity)
where rn_asc <> 1 and rn_dsc <> 1

Trying to figure out how to find the max count

Which team has the most number of members on their roster?
Okay so below is the code that I have input currently. It returns all the teams as well as how many people are on each team. I am not sure how to code it to only display the team with the most members as when I try to use a max function and a count function I get an error.
SELECT Team_Name, COUNT(Member.Student_ID_Num)
FROM Teams
JOIN Member ON Teams.Team_Number = Member.Team_Number
GROUP BY Team_Name
you can try below - using limit and order by desc
Select Team_Name, count(Member.Student_ID_Num) as cnt
from Teams join Member on Teams.Team_Number = Member.Team_Number
group by Team_Name
order by cnt desc
limit 1
If you are using MySQL 8+, then the ROW_NUMBER function comes in handy here:
SELECT Team_Name, cnt
FROM
(
SELECT t.Team_Name, COUNT(*) AS cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rn
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name
) t
WHERE rn = 1;
If you instead want all ties for the highest count, should two or more teams be tied, then replace ROW_NUMBER with RANK.
If you have to do this the old fashioned way, without LIMIT or ROW_NUMBER, then get ready for a really ugly query:
SELECT
t.Team_Name,
COUNT(*) AS cnt
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name
HAVING COUNT(*) = (SELECT MAX(cnt) FROM (SELECT COUNT(*) AS cnt
FROM Teams t
INNER JOIN Member m
ON t.Team_Number = m.Team_Number
GROUP BY t.Team_Name) t );
In order to get the team with the highest member count, we need no join (unless we also want to show the member count). One thing to keep in mind that there may be multiple teams sharing the same maximum member count.
The old fashioned way in standard SQL, before there was any limit clause (FETCH FIRST ROWS in standard SQL) was this:
Count members per team number.
Get the maximum count.
Get the team number(s) with this maximum count.
Get the team(s) for these team numbers.
The query:
select *
from teams
where team_number in
(
select team_number
from member
group by team_number
having count(*) =
(
select max(cnt) as max_cnt
from
(
select count(*) as cnt
from member
group by team_number
) counted
)
);
As of MySQL 8 we would rather use a wnindow function, however:
select *
from teams
where (team_number, 1) in
(
select team_number, rank() over (order by count(*) desc)
from member
group by team_number
);

mysql/postgres window function limit result without subquery

Is it possible to limit the result of a window function, with partitioning, without a subquery? This code is in postgres/mysql. I'm looking for solution in mysql and postgres.
For example: let's say the join is irrelevant to the point of the question.
select acct.name, we.channel, count(*) as cnt,
max(count(*)) over (partition by name order by count(*) desc) as max_cnt
from web_events we join accounts acct
on we.account_id=acct.id
group by acct.name, we.channel
order by name, max_cnt desc;
The result of this query gives:
I only want to show the first line of each of the window's partition.
For example: lines with cnt: [3M,19],[Abbott Labortories,20]
I tried the following that doesn't work (added limit 1 to the window function):
select acct.name, we.channel, count(*) as cnt,
max(count(*)) over (partition by name order by count(*) desc limit 1) as max_cnt
from web_events we join accounts acct
on we.account_id=acct.id
group by acct.name, we.channel
order by name, max_cnt desc;
I only want to show the first line of each of the window's partition. For example: lines with cnt: [3M,19],[Abbott Labortories,20]
You don't actually need a window function here, since the first row's max_cnt will always equal cnt. Instead use DISTINCT ON in combination with the GROUP BY.
From the postgresql documentation
SELECT DISTINCT ON ( expression [, ...] ) keeps only the first row of each set of rows where the given expressions evaluate to equal. The DISTINCT ON expressions are interpreted using the same rules as for ORDER BY (see above). Note that the “first row” of each set is unpredictable unless ORDER BY is used to ensure that the desired row appears first
SELECT DISTINCT ON(acct.name)
acct.name
, we.channel
, COUNT(*) cnt
FROM web_events we
JOIN accounts acct
ON we.account_id=acct.id
GROUP BY 1, 2
ORDER BY name, cnt DESC;
Here's a quick demo in sqlfiddle. http://sqlfiddle.com/#!17/57694/8
1 way I always messed up when I first started using DISTINCT ON is to ensure that the order of expressions in the ORDER BY clause starts with the expressions in the DISTINCT ON. In the above example the ORDER BY starts with acct.name
If there is a tie for first position, the first row that meets the criteria will be returned. This is non-deterministic. It is possible to specify additional expressions in the ORDER BY to affect which rows are returned in this setting.
example:
ORDER BY name, cnt DESC, channel = 'direct'
will return the row containing facebook, if for a given account, both facebook and direct yield the same cnt.
However, note that with this approach, it is not possible to return all the rows that are tied for first position, i.e. both rows containing facebook & direct (without using a subquery).
DISTINCT ON may be combined in the same statement with GROUP BYs (above example) and WINDOW FUNCTIONS (example below). The DISTINCT ON clause is logically evaluated just before the LIMIT.
For instance, the following query (however pointless) shows off the combination of DISTINCT ON with WINDOW FUNCTION. It will return a distinct row per max_cnt
SELECT DISTINCT ON(mxcnt)
acct.name
, we.channel
, COUNT(*) cnt
, MAX(COUNT(*)) OVER (PARTITION BY acct.name) mxcnt
FROM web_events we
JOIN accounts acct
ON we.account_id=acct.id
GROUP BY 1, 2
ORDER BY mxcnt, cnt DESC;
Use a subquery. If you want exactly one row (even if there are ties), then use row_number():
select name, channel, cnt
from (select acct.name, we.channel, count(*) as cnt,
row_number() over (partition by acct.name order by count(*) desc) as seqnum
from web_events we join
accounts acct
on we.account_id = acct.id
group by acct.name, we.channel
) wea
order by name;
You can use rank() if you want multiple rows for an account, in the event of ties.

Find the most popular in mysql

My table is
f1(drinker,shop)
The table has a list of drinkers and the shops which they visit.I need to find the most popular shop.I know I can do a simple group by of shops and order it in a descending way and limit the results to 1 but my doubt is what if two or more shop have the same number of drinkers in that case my query fails.I can't use limit 2 or 3 because I want a general working query and not a one specific to the data.I am running out of ideas.
Note:
Please don't show this way:
select shop from f1 group by shop order by count(*) desc limit 1
In that case, you have to compare the counts. Not so pretty with a group by query:
select shop
from f1
group by shop
having count(*) = (select max(cnt)
from (select count(*) as cnt
from f1
group by shop
) s
);
You could also do this as a subquery:
select shop
from f1 join
(select count(*) as cnt
from f1
group by shop
order by count(*) desc
limit 1
) f1max
group by shop
having count(*) = max(f1max.cnt);

SQL Query - How to find out how which employees worked at more than one store

I have a relationship table such that it has
employeeID | storeID
What would be the query to find out which employees worked at more than one store?
SELECT employeeID WHERE ???
And possibly also list each different stores just once per employee...
Use group by and having, as in:
select employeeID, count(*) from table group by employeeID having count(distinct storeID) > 1
This will give you the employees working at more than one store. Use this as a subquery to list the stores for each such employee.
you can try -
select distinct employeeID,StoreID from table1
where storeID in
(
select storeID from table1 group by storeID having count(distinct employeeID) >1
)
cor storing count and showing store ID also in one query you can use following query..
select a.employeeID,a.storeID,b.cnt
from table1 a,
(select employeeID,count(*) cnt
from table1
group by employeeID
having count(distinct storeID) >1) b
where a.employeID=b.employeeid