I've the(simplified) following model:
Book
id
name
BookCategory
book_id
category_id
rank
Category
id
name
With a given category id, I'd like to get the books having that category as the highest ranked one.
I'll give an example to be more clear about it:
Book
id name
--- -------
1 On Writing
2 Zen teachings
3 Siddharta
BookCategory
book_id category_id rank
--- ------- -----
1 2 34.32
1 5 24.23
1 9 54.65
2 5 27.33
2 9 28.32
3 2 30.43
3 5 27.87
Category
id name
--- -------
2 Writing
5 Spiritual
9 Buddism
The result for category_id = 2 would be the book with id = 3.
This is the query I'm running:
SELECT book."name" AS bookname
FROM bookcategory AS bookcat
LEFT JOIN book ON bookcat."book_id" = book."id"
LEFT JOIN category cat ON bookcat."category_id" = cat."id"
WHERE cat."id" = 2
ORDER BY bookcat."rank"
This is not the right way to do it because it doesn't select the max rank of each book. I've yet to find a proper solution.
Note: I'm using the postgresql 9.1 version.
Edit:
DB Schema (taken from martin's SQL Fiddle answer):
create table Book (
id int,
name varchar(16)
);
insert into Book values(1, 'On Writing');
insert into Book values(2, 'Zen teachings');
insert into Book values(3, 'Siddharta');
create table BookCategory (
book_id int,
category_id int,
rank real
);
insert into BookCategory values(1,2,34.32);
insert into BookCategory values(1,5,24.23);
insert into BookCategory values(1,9,54.65);
insert into BookCategory values(2,5,27.33);
insert into BookCategory values(2,9,28.32);
insert into BookCategory values(3,2,30.43);
insert into BookCategory values(3,5,27.87);
create table Category (
id int,
name varchar(16)
);
insert into Category values(2, 'Writing');
insert into Category values(5,'Spiritual');
insert into Category values(9, 'Buddism');
add another column to calculate rank:
dense_rank() OVER (PARTITION BY book."name" ORDER BY bookcat."rank"
s ASC) AS rank
To set up:
CREATE TABLE Book
(
id int PRIMARY KEY,
name text not null
);
CREATE TABLE Category
(
id int PRIMARY KEY,
name text not null
);
CREATE TABLE BookCategory
(
book_id int,
category_id int,
rank numeric not null,
primary key (book_id, category_id)
);
INSERT INTO Book VALUES
(1, 'On Writing'),
(2, 'Zen teachings'),
(3, 'Siddharta');
INSERT INTO Category VALUES
(2, 'Writing'),
(5, 'Spiritual'),
(9, 'Buddism');
INSERT INTO BookCategory VALUES
(1, 2, 34.32),
(1, 5, 24.23),
(1, 9, 54.65),
(2, 5, 27.33),
(2, 9, 28.32),
(3, 2, 30.43),
(3, 5, 27.87);
The solution:
SELECT Book.name
FROM (
SELECT DISTINCT ON (book_id)
*
FROM BookCategory
ORDER BY book_id, rank DESC
) t
JOIN Book ON Book.id = t.book_id
WHERE t.category_id = 2
ORDER BY t.rank;
Logically, the subquery in the FROM clause generates a relation with the highest ranking category for each book, from which you then select the books in that category and order them by the ranking in that category.
Results:
name ----------- Siddharta (1 row)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With