3

I have a Book(60,000 books) and author(37,000 authors) tables in my Database. A book may be written by more than one author. So book table has comma separated author_ids in author_id column in book table Book Table

book_master_id      book_name       author_id
1                   Book 1          22,23
2                   Book 2          23
3                   Book 3          24
4                   Book 4          23,24

Author Table

author_id   author_name 
22          Jim 
23          Roger   
24          Andrew  

Now if I want result as author name with the descending order of count of written book. i.e

Roger(3)
Andrew(2)
Jim(1)

What should be the Mysql query??? Please tell me steps. Also consider the execution time as minimum Thank you

Rajan Rawal
  • 6,171
  • 6
  • 40
  • 62
  • 6
    Read this **Is-storing-a-comma-separated-list-in-a-database-column-really-that-bad?**: http://stackoverflow.com/questions/3653462/is-storing-a-comma-separated-list-in-a-database-column-really-that-bad The short answer is **Yes, it's really that bad** – ypercubeᵀᴹ Oct 19 '11 at 10:30
  • Then, normalize your table structure. – ypercubeᵀᴹ Oct 19 '11 at 10:31
  • This time you will know why such database design is not good but that bad. – xdazz Oct 19 '11 at 10:33

5 Answers5

5

You should normalize your database tables, as already mentioned by several other people. The result database structure should look like:

BOOK_MASTER
book_master_id    book_name
1                 Book 1
2                 Book 2
3                 Book 3
4                 Book 4

AUTHOR
author_id         author_name 
22                Jim 
23                Roger   
24                Andrew 

BOOK_AUTHOR
book_master_id    author_id
1                 22
1                 23
2                 23
3                 24
4                 23
4                 24

You should ofcourse set the correct constraints on your database schema, but this will give you an idea.

With this database structure you can use the following query to get the result you want:

SELECT
    a.author_name,
    COUNT(*) as number_of_books
FROM
    author a
    JOIN book_author ba ON a.id = ba.author_id
GROUP BY
    a.author_name
ORDER BY number_of_books DESC

Writing a script that moves the author_id comma separated lists of you current books table into the new author_book table should be fairly trivial, something in the line of the following would probably work:

<?php
$query = "SELECT book_master_id, author_id FROM books";
$result = mysql_query($query);

while ($row = mysql_fetch_row($result)) {
    $values = array();
    foreach (explode(',', $row['author_id']) as $authorId) {
        $values[] = "(" . $row['book_master_id'] . ", " . $authorId . ")";
    }

    $query = "INSERT IGNORE INTO book_author (book_master_id, author_id) VALUES ";
    $query .= implode(', ', $values);

    mysql_query($query);
}

Note that I did not test this code, and you should first try it out on a test database to see if it actually does what it is supposed to do. Furthermore, if you have a lot of data, this script can take some time to execute. And finally, maybe there is an SQL query that can do this, but this was the first solution that came to mind.

Jan-Henk
  • 4,864
  • 1
  • 24
  • 38
  • If you need some help on how to transform you existing database structure into this proposed structure I can help. I see you are replying to answers that will work for you existing database structure, but these are all fairly complex solutions that will perform very bad. – Jan-Henk Oct 19 '11 at 12:05
  • Sure. I would be thankful if you would help me. I know comma separated values are bad, but I just wanted to work around it. So yes tell me sir, What should I do? – Rajan Rawal Oct 19 '11 at 17:51
  • @RajanRawal I added a piece of PHP code (since you tagged your question with PHP) to my answer. You should first create the book_author table I proposed, and use the PHP code to populate the newly created table. – Jan-Henk Oct 19 '11 at 18:18
  • Thank you @Jan-Henk. If I have further queries about Data structure and PHP Can i contact you for help? – Rajan Rawal Oct 20 '11 at 08:58
1

If you really want minimal execution times, I guess you also want to have a good table structure. And your comma separated list most definitly isn't. You should instead add a third table which chains together your book table and the authors table. It could be something like this:

book_master_id   author_id
1                22
1                23
2                23
3                24
4                23
4                24

That way you can easily calculate the books per author and do many other queries that would be unreasonably complicated and slow otherwise.

Jan-Henk
  • 4,864
  • 1
  • 24
  • 38
florian h
  • 1,162
  • 1
  • 10
  • 24
1

As ypercube pointed out, comma separated string values of IDs is VERY bad, and will kill your performance no matter... and Jan-Henk well pointed out a correct structure to handle as well as the query... However, if you can't change the structure (which is strongly recommended), you have to extend the offering from Sashi.

If looking for a "like" comparison on a comma separated list, you would get conflicts on numbers like

Author ID being found in a string of Authors of "123,223,323,423,1235,36235" yet none of them are author 23. You would have to wrap your authors with commas at the beginning and end to ensure you are looking EXPLICITLY for ",23," being found in the string.

select auther_name,count(*)
     from authors au
    inner join books bo 
    on concat( ",", au.author_id, "," ))
         like CONCAT('%,', bo.authors,',%')
    group by author_name order by count(*) desc;
DRapp
  • 47,638
  • 12
  • 72
  • 142
  • MySQL has `FIND_IN_SET()` which is useful in these cases and probably faster than `CONCAT + LIKE` – ypercubeᵀᴹ Oct 19 '11 at 11:23
  • @ypercube, thanks for that insight... believe it or not, I don't actually work in MySQL environment enough to know all the special funcitons. – DRapp Oct 19 '11 at 11:37
0

Hope this will solve ur problem:::

 select auther_name,count(*)
     from authors au
    left join books bo 
    on au.author_id like CONCAT('%', bo.author_id,'%')
    group by author_name order by count(*) desc;
Sashi Kant
  • 13,277
  • 9
  • 44
  • 71
  • Thank you Shahi. But your ans gives the result only for the authors who has written single book means book with single author. – Rajan Rawal Oct 19 '11 at 11:39
  • Try this then::: select auther_name,count(*) from authors au inner join books bo on au.author_id like CONCAT('%', bo.author_id,'%') group by author_name order by count(*) desc; – Sashi Kant Oct 19 '11 at 11:42
  • Error Code : 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') from author_master au inner join book_master bo on au.author_id like CONCAT('%' at line 1 – Rajan Rawal Oct 19 '11 at 11:53
  • `select au.author_name, count(*) from author_master au left outer join book_master bo on (au.author_id like CONCAT('%,',bo.author_id,',%') || au.author_id like CONCAT(bo.author_id,',%') || au.author_id like CONCAT('%,', bo.author_id) || au.author_id like (bo.author_id)) group by au.author_name order by count(*) desc;` – Rajan Rawal Oct 19 '11 at 12:46
0
SELECT  author_name + ' ( '+
cast((

 select count(author_id) from book 
 where author_id like '%'+cast(author.author_id as varchar(10))+'%')as nvarchar(50) ) +')'
FROM author 

its working in ms sql server 2005 so you can change if there is any need for mysql

rahularyansharma
  • 11,156
  • 18
  • 79
  • 135
  • Please help: Error Code : 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '+cast(author_master.author_id as varchar(10))+'%')as nvarchar(50) ) +')' FROM a' at line 1 – Rajan Rawal Oct 19 '11 at 11:45
  • rajan i dont have mysql so i already suggest you to change if is there any need from syntax view so please check the manual of mysql – rahularyansharma Oct 19 '11 at 11:59