Seemingly simple MYSQL query - mysql

Here is what my tables look like:
CREATE TABLE Author(
authorID INT PRIMARY KEY,
name VARCHAR(30)
);
CREATE TABLE book(
ISBN INT PRIMARY KEY,
title VARCHAR(30),
authorID INT,
inventory INT,
paperBack BOOLEAN,
fiction BOOLEAN,
FOREIGN KEY (authorID) REFERENCES Author(authorID)
);
I need to find out which author has written the most books.
I am working with a mixture of the following. I guess I am having trouble piecing it all together...
SELECT authorID, count(*)
from book
group by authorID;
I am not sure how to get the single row that has the highest count and then only get the authorID of that row. Once I have that authorID, I know how to get the name.

Try this:
select a.name,count(*)
from author a
join book b on b.authorID=a.authorID
group by a.Name
order by 2 desc
limit 1

If all you want is the ID of the author, then use ORDER BY and LIMIT 1 as others have pointed out. However, if you're going to want other fields from Author, or if you are interested in multiple authors (second highest count, lowest count, etc), then you should consider joining to a derived table, like this:
SELECT Author.*, d1.book_count
FROM Author
JOIN (SELECT authorID, count(authorID) as book_count FROM book GROUP BY authorID) d1
ON Author.authorID = d1.authorID
ORDER BY
d1.book_count
Would give a result set like this:
+----------------------------------+
| AuthorID | Name | book_count |
+----------------------------------+
| 1 | bob | 20 |
| 9001 | sam | 18 |
...

You don't need to use a subquery, you can just do something like this:
SELECT authorID FROM book GROUP BY authorID ORDER BY COUNT(*) DESC LIMIT 1
this should give you the authorID of the author with the most books.

Related

How to fill (join with other table) null values if any from another table?

I have two similar tables, they have the same columns, but different data, both matching different criterions. I want to join Table A to Table B, where some of the values are null.
I tried to look up similar questions but they are not describing my case as far as i can tell.
As an example:
Table A looks like
| id | name | age | gender |
1 Jhon 2 Male
2 Will null null
Table B looks like
| id | name | age | gender |
1 Jhon null null
2 Will 3 Male
What i would like to make is like
| id | name | age | gender |
1 Jhon 2 Male
2 Will 3 Male
I was trying to left join it, but the result is not as expected. My thought maybe i need to inner join it, then left join maybe, but it is a bit blurry yet.
I'm kinda new to joins, so every thought is appreciated.
Thanks in advance.
You can try to use UNION ALL in the subquery to do MAX
SELECT id ,name,MAX(age) age ,MAX(gender) gender
FROM (
SELECT id ,name , age , gender
FROM A
UNION ALL
SELECT id ,name , age , gender
TABLE B
) t1
GROUP BY id ,name
If your A and B tables schema are the same I would suggest you use only one table and use a flag to split those two part.
Did you try this?
select id, name,
coalesce(a.age, b.age) as age,
coalesce(a.gender, b.gender) as gender
from a join
b
using (id, name);
The issue is less the type of join then in how you combine the values from the two tables in the select.

SQL- Top count(column1) w.r.t Distinct column2

I have a table with three columns,
| User_id (INT) | CountryCode (VARCHAR) | channel_accessed (VARCHAR) |
There is no primary key over here, so repetition is possible for all columns.
I want to write a SQL query that returns Top countries name & there corresponding count w.r.t unique User_id
Tried following this Using group by on multiple columns but this has not helped me much.
sample data :
| User_id (INT) | CountryCode (VARCHAR) | channel_accessed (VARCHAR) |
1 US ARY
2 CA ARY
3 CA MTV
2 CA HUMTV
4 US Tensports
5 US Star Sports
2 CA PTV
2 CA QTV
2 CA NATGEO
Expected Result : US, because it has more unique users.
Try this:
select CountryCode
from yourtable
group by CountryCode
order by count(distinct User_id) desc
limit 1
SQLFiddle Demo
If the channel_accessed column doesn't matter then you could try
SELECT CountryCode, MAX(user_count)
FROM (SELECT CountryCode,
COUNT(DISTINCT(user_id)) as user_count
FROM table_name
GROUP BY CountryCode)

mysql show all other results from company

I have a question. This is my database structure
**company**
id | name
---------
1, Test
2, demo
**address**
id | name
---------
1, test1
2, test2
3, bla6
**address_company**
id | address_id | company_id
1, 1, 1
2, 2, 1
3, 3, 2
My query is this:
SELECT company.name, address.name FROM company
INNER JOIN address_company on address_company.company_id = company.id
INNER JOIN address on address.id = address_company.address_id
This works. But I need to filter results.
So when people click address (frontend): test1, it only needs to show company: Test
I can do this:
WHERE address.name = "test1"
This also works but I need to filter further so what I need is
WHERE address.name = "test1" AND address.name = "test2"
But this doesn't work, it doesn't show results. I can only filter on 1 address and I need to filter on more addresses.
Hope you guys can understand me and can help me.
THANKS!
The below strategy leans on the unique key(address_id,company_id) making sure there are no duplicates at that combo-level
Schema
create table company
( id int auto_increment primary key,
name varchar(100) not null
);
insert company(name) values ('Test'),('demo');
create table address
( id int auto_increment primary key,
name varchar(100) not null
);
insert address(name) values ('test1'),('test2'),('bla6');
create table address_company
( id int auto_increment primary key,
address_id int not null,
company_id int not null,
unique key(address_id,company_id) -- no dupes allowed ! I am banking on this below
);
insert address_company(address_id,company_id) values (1,1),(2,1),(3,2);
The queries
select company_id,count(*) theCount from address_company
where address_id in (1,2)
group by company_id having theCount>1;
+------------+----------+
| company_id | theCount |
+------------+----------+
| 1 | 2 |
+------------+----------+
select company_id,count(*) theCount from address_company
where address_id in (select id from address where name in ('test1','test2'))
group by company_id having theCount>1;
+------------+----------+
| company_id | theCount |
+------------+----------+
| 1 | 2 |
+------------+----------+
So if the group by / having returns greater than 1 for the count, where I literally went after name1 and name2, then I know that row qualifies. And that row of course then has name1 and name2.
Back to the unique key part: This assures we aren't tricked in having a company with the same address twice. Which first off doesn't make sense, and also that would mess up this strategy.
Obviously the schema needs some index help, and FK's wouldn't break anyone's heart. But this is just a strawman.
Use OR instead of and, or use the in() structure:
WHERE address.name = 'test1' OR address.name = 'test2'
WHERE address.name IN('test1', 'test2' )
Note: I hope that the below join condition was just typed incorrectly in the question:
INNER JOIN address on address.id = address_company.id

SQL - Counting from 2 or more Columns

I am somehow new to SQL, and I have a Election Application. I have done 80% of it and now stuck at counting votes from 2 or more Columns.
Example Table:
|**Senator_1** | **Senator_2** | **Senator_3**|
----------------------------------------------
George | Carl | Anthony
Carl | Tony | Stark
Anthony | George | Tony
Anthony | George | Stark
I would like to have this kind of result.
|**Candidate_Name** | **Vote_Count**|
-------------------------------------
George | 3
Anthony | 3
Carl | 2
Stark | 2
Tony | 2
I really don't have any idea of what query I am going to use. Any ideas of solving this?
By the way, for the confusion and all the arguments that started here, I am going to explain:
I wanted to be straight to my problem that's why I just posted a sample table. I have a table for the Voters, Candidates and the Votes. All tables have its ID and such, so I'm pretty sure it's normalized.
The main issue that you have is your table is not normalized. I would strongly advise that you fix your current table structure. A possible new table structure would be:
/* Table for unique voters */
CREATE TABLE voters (
id INT UNSIGNED NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL
) ENGINE=InnoDB;
/* Table for unique candidates */
CREATE TABLE candidates (
id INT UNSIGNED NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL
) ENGINE=InnoDB;
/* Many-to-many table storing votes by voters for candidates */
CREATE TABLE votes (
voter_id INT UNSIGNED NOT NULL,
candidate_id INT UNSIGNED NOT NULL,
PRIMARY KEY (voter_id, candidate_id),
CONSTRAINT FOREIGN KEY (voter_id) REFERENCES voters (id),
CONSTRAINT FOREIGN KEY (candidate_id) REFERENCES candidates (id)
) ENGINE=InnoDB;
/* Populate data */
INSERT INTO voters (name)
VALUES ('Voter 1'), ('Voter 2'), ('Voter 3'), ('Voter 4');
INSERT INTO candidates (name)
VALUES ('George'), ('Carl'), ('Anthony'), ('Tony'), ('Stark');
INSERT INTO votes (voter_id, candidate_id)
VALUES (1,1), (1,2), (1,3),
(2,2), (2,4), (2,5),
(3,3), (3,1), (3,4),
(4,3), (4,1), (4,5);
Then you could easily get a result by joining the two tables:
/* query showing all voters and the candidates they voted for */
SELECT voters.name, candidates.name
FROM votes
INNER JOIN voters on votes.voter_id = voters.id
INNER JOIN candidates ON votes.candidate_id = candidates.id;
/* Finally, a query showing votes per candidate */
SELECT candidates.name, COUNT(*) AS votes
FROM votes
INNER JOIN candidates ON votes.candidate_id = candidates.id
GROUP BY candidates.id;
See SQL Fiddle with Demo
However, if you cannot alter the design of the table, then you can get the result by unpivoting the data that you have in multiple columns. You can use a UNION ALL to unpivot the multiple columns into rows to get the count:
select name, count(*) TotalVotes
from
(
select senator_1 name
from yt
union all
select senator_2 name
from yt
union all
select senator_3 name
from yt
) d
group by name
order by totalVotes desc;
See SQL Fiddle with Demo
I think you are looking to count the total no. of occurrence of each name in different columns. Based on this, I think something like below might help -
select senator, sum(cnt) as 'count' from (
select senator_1 as 'senator', count(1) 'cnt' from election_table group by senator_1
union all
select senator_2 as 'senator', count(1) 'cnt' from election_table group by senator_2
union all
select senator_3 as 'senator', count(1) 'cnt' from election_table group by senator_3
) x group by x.senator

Stored procedure counting trouble

I have a table [users] that I wish to count the number of each occurrence of Movie_ID and update the record in a different table called [total]. So for Movie_ID=81212 it would send the value 2 to my [total] table.
like below:
------------------------------------
| [users] | [total]
+---------+---------+ +---------+-------------+
|Movie_ID |Player_ID| |Movie_ID | Player_Count|
+---------+---------+ +---------+-------------+
|81212 |P3912 | | 81212 | 2 |
+---------+---------+ +---------+-------------+
|12821 |P4851 | | 12821 | 1 |
+---------+---------+ +---------+-------------+
|81212 |P5121 |
+---------+---------+
(movie_ID + player_ID form composite key
so Movie_ID does not need to be unique)
So i'm trying to accomplish this with a stored procedure, this is what I have so far: I'm not sure how to code the part where it loops through every entry in the [users] table in order to find each occurrence of movie_id and sums it up.
DELIMITER //
CREATE PROCEDURE `movie_total` (OUT movie_count int(5))
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE movie_count int(5);
SELECT count(movie_id) AS movie_count FROM users
foreach unique row in Users ;
IF (SELECT COUNT(*) FROM users WHERE movie_id) > 0
THEN
INSERT INTO total (:movie_id, :Player_Count) VALUES (movie_id, movie_count);
END //
To update this field you can use a query like this -
UPDATE
total t
JOIN (SELECT Movie_ID, COUNT(*) cnt FROM users GROUP BY Movie_ID) m
ON t.Movie_ID = m.Movie_ID
SET
t.Player_Count = cnt
BUT: Do you really need a total table? You always can get this information using SELECT query; and the information in the total table may be out of date.
I think you can do this without a loop:
update total set total.Player_Count = (select COUNT(Movie_ID) from users where total.Movie_ID=users.Movie_ID group by (Movie_ID));