I have a table called book_author with schema (book_id: int, author: string).
I am trying to obtain the books with the second highest number of authors.
That said, if the table was something like this:
book_id | author
_____________________
1 | John
2 | Anna
3 | Dan
1 | Robert
2 | Kim
1 | Oscar
3 | Bill
The return would be something like this. Book_id = 1 is not in the table because it has the max.
book_id | NUM_AUTHORS
2 | 2
3 | 2
My initial attempt was as follows, but this simply returns the book ids with the Maximum number of authors, not second to largest... Any way I could modify this?
SELECT book_id, COUNT(*) AS NUM_AUTHORS FROM book_author GROUP BY book_id
HAVING COUNT(*) =
(SELECT MAX(c) FROM
(SELECT COUNT(book_id) AS c
FROM book_author
GROUP BY book_id));
You can do this with ordering the counts in descending order and getting the 2nd row (with offset and limit)
select book_id,count(*)
from book_author
group by book_id
having count(*) = (select distinct cnt
from (select count(*) as cnt
from book_author
group by book_id
) t
order by cnt desc
limit 1 offset 1
)
Related
I have a table like this (simplified version):
+------+-------+-----+--------------+-----+
| id | name | age | company.name | ...
+------+-------+-----+--------------------+
| 1 | Adam | 21 | Google | ...
| 3 | Peter | 20 | Apple | ...
| 2 | Bob | 20 | Microsoft | ...
| 9 | Alice | 18 | Google | ...
+------+-------+-----+--------------------+
I need groups data with counting rows by any one column. And I need to get first row in each group. User select which column will be used to group.
If user select column age to group then results:
+------+------------+-------+
| id | group_name | count |
+------+------------+-------+
| 9 | 18 | 1 |
+------+------------+-------+
| 2 | 20 | 2 |
+------+------------+-------+
| 1 | 21 | 1 |
+------+------------+-------+
Column to group may be numeric or string.
Currently I does it by this query:
SELECT id, group_name, users_name, count(id) as count FROM (
SELECT persons.id as id, company.type as group_name, users.name as users_name
FROM persons
LEFT JOIN company on company.id = persons.company_id
LEFT JOIN position on position.id=persons.position_id
...
LEFT JOIN source on source.id=persons.source_id
WHERE ...
ORDER BY if(company.type = '' or company.type is null,1,0) ASC,
company.type ASC, IF(persons.status = '' or persons.status is null,1,0) ASC,
persons.status ASC, persons.id
) t1 GROUP BY group_name
but with new version mysql this SQL stoped works I think that order is ignored in sub-select.
I know that similar topics was wroted, but proposed solutions not working with my query. I have to join many tables, add multiple conditions and use cascade order and then select first row from each group. I will be very happy if solution will be optimised for performace.
---- EDIT ----
Proposed solution:
SQL select only rows with max value on a column
which suggest to use MAX() and GROUP BY not working well. For two reason
If grouped column include string, then query return not first row, but last row in each group.
If my dataset has a cascade order, I can not use MAX in a few columns at the same time.
I created sqlfiddle which include exact example.
http://sqlfiddle.com/#!9/23225d/11/0
-- EXAMPLE 1 - Group by string
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.name ASC, company.id ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.name, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.name
ORDER BY company.name ASC, persons.id ASC;
-- The results will be:
-- |ID | NAME | COUNT|
-- |1 | Google | 2 |
-- |3 | Microsoft| 3 |
-- EXAMPLE 2 - Cascade order
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.type ASC, persons.status ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.type, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.type
ORDER BY company.type ASC, persons.status ASC;
-- The results will be:
-- |ID | NAME| COUNT|
-- |3 | 1 | 2 |
-- |2 | 2 | 3 |
Just change MAX() to MIN() to get the first row instead of the last row in each group.
To get the extreme values of cascading columns, see SQL : Using GROUP BY and MAX on multiple columns. Use that in the subquery part of the query to get the row containing those extremes, as in SQL select only rows with max value on a column.
So the form of the full query is:
SELECT t1.id, t1.grouped_column, t2.count
FROM yourTable AS t
JOIN (SELECT t3.grouped_column, t3.order_column1, MIN(t4.order_column2) AS order_column2, SUM(t3.count) AS count
FROM (SELECT grouped_column, MIN(order_column1) AS order_column1, COUNT(*) AS count
FROM yourTable
GROUP BY grouped_column) AS t3
JOIN yourTable AS t4
ON t3.grouped_column = t4.grouped_column AND t3.order_column1 = t4.order_column1
GROUP BY t4.grouped_column, t4.order_column1) AS t2
ON t1.grouped_column = t2.grouped_column AND t1.ordered_column1 = t2.order_column1 AND t1.order_column2 = t2.order_column2
Since you want to operate on a join, I suggest you define a view that uses the join. Then you can use that view in place of yourTable in the above query.
I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2016-01-01
2 | 1 | 2016-01-02
3 | 2 | 2016-02-01
4 | 2 | 2016-03-01
5 | 3 | 2016-04-01
I want to get the results like this
Id count of Id max(dateof)
2 | 2 | 2016-01-02
4 | 2 | 2016-03-01
5 | 1 | 2016-04-01
My try
SELECT id,tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof ,
count(id) cnt_of_id -- How to get this count value in the result
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
This is an aggregation query, but you don't seem to want the column being aggregated. That is ok (although you cannot distinguish the rk that defines each row):
select count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
In other words, your subquery is pretty much all you need.
If you have a reasonable amount of data, you can use a MySQL trick:
select substring_index(group_concat(id order by dateof desc), ',', 1) as id
count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
Note: this is limited by the maximum intermediate size for group_concat(). This parameter can be changed and it is typically large enough for this type of query on a moderately sized table.
You obviously want one result row per fk, so group by it. Then you want the max ID, the row count and the max date for each fk:
select
max(id) as max_id,
count(*) as cnt,
max(date_of) as max_date_of
from tbl
group by fk;
I have a table that looks like this:
Categories:
cId | Name | Parent
----+-------------------------+-------
1 | Parent One | NULL
2 | Child of 1st Parent | 1
3 | Parent Two | NULL
4 | Child of 1st Parent | 1
5 | Child of 2nd Parent | 2
The table does not represent a heirarchy: Every item is either a child or a parent, but not both.
And one table like this:
Posts:
pId | Name | cID
----+-------------------------+-------
1 | Post 1 | 1
2 | Post 2 | 2
3 | Post 3 | 2
4 | Post 4 | 3
I'd like to run a query on it that returns this:
cId | Count
---+---------
1 | 3
2 | 2
3 | 1
4 | 0
5 | 0
Count is the number of posts connected to the category.
All categories should be returned.
Parent categories should have the count of the category + child categories sum. (this is one of the things I'm having problem with)
Child categories should have the category sum.
How should I do this?
From your expected results, it looks like you don't care about grandchildren and lower, in which case, this should work. To get the correct parent count, I'm checking for Parent IS NULL or Count(children) > 0, in which case, I'm adding 1:
SELECT c.cId, CASE WHEN C.Parent IS NULL OR COUNT(C2.cId) > 0 THEN 1 ELSE 0 END +
COUNT(C2.cId) TotalCount
FROM Categories C
LEFT JOIN Categories C2 on c.cId = c2.Parent
GROUP BY c.cId
Here is some sample fiddle: http://www.sqlfiddle.com/#!2/b899f/1
And the results:
CID TOTALCOUNT
1 3
2 2
3 1
4 0
5 0
---EDIT---
From reading your comments, it looks like you want something like this:
SELECT c.cId,
COUNT(DISTINCT P.pId) + COUNT(DISTINCT P2.pId) TotalCount
FROM Categories C
LEFT JOIN Posts P ON C.CId = P.CId
LEFT JOIN Categories C2 on c.cId = c2.Parent
LEFT JOIN Posts P2 ON C2.CId = P2.CId
GROUP BY c.cId
http://www.sqlfiddle.com/#!2/eb0d2/3
This is general hint. I do not know if analytic functions and partitioning available in MySQL but you can partition your output by categories then count and sum up within categories. Do some research about analytic functions and partition by clause. General example of what I meant - output is partitioned by deptno and ordered. Also, max hiredate determined within partition - replace max with count, sum etc... in your case:
SELECT * FROM
(
SELECT deptno
, empno
, ename
, sal
, RANK() OVER (PARTITION BY deptno ORDER BY sal desc) rnk
, ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal desc) rno
, MAX(hiredate) OVER (PARTITION BY deptno ORDER BY deptno) max_hire_date
FROM emp_test
ORDER BY deptno
)
--WHERE rnk = 1
ORDER BY deptno, sal desc
/
DEPTNO EMPNO ENAME SAL RNK RNO MAX_HIRE_DATE
--------------------------------------------------------------------
10 7839 KING 5000 1 1 1/23/1982
10 7782 CLARK 2450 2 2 1/23/1982
10 7934 MILLER 1300 3 3 1/23/1982
20 7788 SCOTT 3000 1 1 1/28/2013
20 7902 FORD 3000 1 2 1/28/2013
20 7566 JONES 2975 3 3 1/28/2013
If I have a table and data like this:
ID | Name | Group
1 Apple A
2 Boy A
3 Cat B
4 Dog C
5 Elep C
6 Fish C
and I wish to order it according to the total of Group from smallest to largest value, such as :
A - 2 records , B - 1 record , C - 3 records , so it will become:
3 Cat B
1 Apple A
2 Boy A
4 Dog C
5 Elep C
6 Fish C
I tried
$sql = "SELECT ID,Name FROM table ORDER BY COUNT(Group)";
but it just returns one result for me.
Are there any hints? Thank you.
You need to aggregate the data first, this can be done using the GROUP BY clause:
SELECT Group, COUNT(*)
FROM table
GROUP BY Group
ORDER BY COUNT(*) DESC
The DESC keyword allows you to show the highest count first, ORDER BY by default orders in ascending order which would show the lowest count first.
...none of the other answers seem to do what the asker asked.
For table named 'things' with column 'group':
SELECT
things.*, counter.count
FROM
things
LEFT JOIN (
SELECT
things.group, count(things.group) as count
FROM
things
GROUP BY
things.group
) counter ON counter.group = things.group
ORDER BY
counter.count ASC;
which gives:
id | name | group | count
---------------------------
3 | Cat | B | 1
1 | Apple | A | 2
2 | Boy | A | 2
4 | Dog | C | 3
5 | Elep | C | 3
6 | Fish | C | 3
SELECT group, COUNT(*) FROM table GROUP BY group ORDER BY group
or to order by the count
SELECT group, COUNT(*) AS count FROM table GROUP BY group ORDER BY count DESC
Try :
SELECT count(*),group FROM table GROUP BY group ORDER BY group
to order by count descending do
SELECT count(*),group FROM table GROUP BY group ORDER BY count(*) DESC
This will group the results by the group column returning the group and the count and will return the order in group order
SELECT * FROM table
group by `Group`
ORDER BY COUNT(Group)
Try using below Query:
SELECT
GROUP,
COUNT(*) AS Total_Count
FROM
TABLE
GROUP BY
GROUP
ORDER BY
Total_Count DESC
Below gives me opposite of what you have. (Notice Group column)
SELECT
*
FROM
myTable
GROUP BY
Group_value,
ID
ORDER BY
count(Group_value)
Let me know if this is fine with you...
I am trying to get what you want too...
Q. List the name of each show, and the number of different times it has been held.
List the show which has been held most often first.
event_id show_id event_name judge_id
0101 01 Dressage 01
0102 01 Jumping 02
0103 01 Led in 01
0201 02 Led in 02
0301 03 Led in 01
0401 04 Dressage 04
0501 05 Dressage 01
0502 05 Flag and Pole 02
Ans:
select event_name, count(show_id) as held_times from event
group by event_name
order by count(show_id) desc
i have table structure like this
sn | person_id | image_name |
1 | 1 | abc1.jpb
2 | 1 | aa11.jpg
3 | 11 | dsv.jpg
4 | 11 | dssd.jpg
5 | 11 | sdf.jpg
I need distinct person_id newest row as following
2 | 1 | aa11.jjpb
5 | 11 | sdf.jpg
IT is possible ?
SELECT * FROM yourtable GROUP BY person_id ORDER BY sn DESC
Essentially you want to select all records from your table. Then it is grouped by the person_id (limiting the result to 1 per person id)... Ordering by SN decending means that it will return the most recent (highest) sn
Update: (and verified)
SELECT * FROM (SELECT * FROM stackoverflow ORDER BY sn DESC) a GROUP BY person_id ORDER BY sn
SELECT * FROM table GROUP BY person_id HAVING MAX(sn)
EDIT
SELECT f.*
FROM (
SELECT person_id, MAX(sn) as maxval
FROM table GROUP BY person_id
) AS x INNER JOIN table AS f
ON f.person_id = x.person_id AND f.sn = x.maxval;
where table is your table name.
SELECT * FROM table a WHERE a.`id` = ( SELECT MAX(`id`) FROM table b WHERE b.`person_id` = a.`person_id` );
What you are doing inside the parenthesis is selecting the max id for the rows that have that distinct person_id. So for each unique person_id you are getting the most recent entry.