I have 5 tables in Mysql and i need a combined result from all the tables
The tables are as follows
1)library_book_relation
CREATE TABLE db.library_book_relation (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
library_id int ,
book_item_id int ,
seller_price decimal(10,2) ,
pack_quantity decimal(10,2) ,
is_discontinue tinyint ,
total_quantity decimal(10,2) ,
) ;
2)book_item
CREATE TABLE db.`book_item` (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
book_id int ,
size_unit_id tinyint ,
size decimal(10,2) ,
is_active boolean ,
is_deleted boolean ,
) ;
3) book
CREATE TABLE db.book(
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
name varchar(100) ,
author_id int ,
description varchar(256) ,
is_active boolean ,
is_deleted boolean ,
) ;
4) author
CREATE TABLE db.author(
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
name varchar(100) ,
image varchar(256) ,
description varchar(256) ,
is_active tinyint ,
is_deleted tinyint ,
) ;
5)size_unit
CREATE TABLE db.size_unit (
id tinyint NOT NULL PRIMARY KEY,
name varchar(100) ,
is_active tinyint ,
) ;
i want a combined result where i get
author id
author name
count of books for the author in library
i got all 3 in different queries but dont know how to combine the result
to get author id and name
select id,name FROM author where id IN( select e.id from author e inner join book r on e.id = r.author_id where r.id IN( select q.id from book q inner join book_item w on q.id = w.book_id where w.id IN( Select s.id from book_item s inner join library_book_relation d on s.id=d.book_item_id where d.library_id = 1 )))
to get count of books for author in library
(Select COUNT(*) FROM book where author_id IN ( select e.id from author e inner join book r on e.id = r.author_id where r.id IN( select q.id from book q inner join book_item w on q.id = w.book_id where w.id IN( Select s.id from book_item s inner join library_book_relation d on s.id=d.book_item_id where d.library_id = 1 ))) GROUP BY author_id)
The easiest way is to join all tables, group by author and library and count:
select b.author_id, a.name, lbr.library_id, count(*)
from book b
join book_item bi on bi.book_id = b.id
join library_book_relation lbr on lbr.book_item_id = bi.id
join author a on a.id = b.author_id
group by b.author_id, a.name, lbr.library_id
order by b.author_id, a.name, lbr.library_id;
Per SQL standard you only need to group by b.author_id, lbr.library_id, because you want one result row per author and library. MySQL should be able to do this, so you can remove a.name from the group by clause. (There is only one name per author ID.)
You can also count before joining with the authors table. It is considered good style to aggregate before joining and can help a lot (or be even necessary) in more complicated queries.
select c.author_id, a.name, c.library_id, c.books
from
(
select b.author_id, lbr.library_id, count(*) as books
from book b
join book_item bi on bi.book_id = b.id
join library_book_relation lbr on lbr.book_item_id = bi.id
group by b.author_id, lbr.library_id
) c
join author a on a.id = c.author_id
order by c.author_id, a.name, c.library_id;
Above queries do not count zeros, though. They just show how often to find authors in the libraries. Here is how to count how many books author #1 has in library #123:
select a.id, a.name, coalesce(b.books, 0)
from author a
left join
(
select b.author_id, count(*) as books
from book b
join book_item bi on bi.book_id = b.id
join library_book_relation lbr on lbr.book_item_id = bi.id
where lbr.library_id = 123
group by b.author_id
) c on c.author_id = a.id
where a.id = 1;
Or with the subquery in the select clause:
select
a.id,
a.name,
(
select count(*)
from book b
join book_item bi on bi.book_id = b.id
join library_book_relation lbr on lbr.book_item_id = bi.id
where lbr.library_id = 123
and b.author_id = a.id
) as books
from author a
where a.id = 1;
At last a query to get all authors and all libraries including zeros:
select a.id as author_id, a.name, l.id as library_id, coalesce(c.books, 0)
from author a
cross join library l
left join
(
select b.author_id, lbr.library_id, count(*) as books
from book b
join book_item bi on bi.book_id = b.id
join library_book_relation lbr on lbr.book_item_id = bi.id
group by b.author_id, lbr.library_id
) c on c.author_id = a.id and c.library_id = l.id
order by a.id, l.id;
Related
I have the following query:
SELECT *
FROM (
SELECT
m.id AS id,
reference_id,
title,
created_by,
publish_up,
state
FROM z_news_master m
LEFT JOIN z_news_english c ON m.id = c.reference_id
WHERE c.created_by = 17152
ORDER by c.id DESC
) AS A
UNION
SELECT * FROM (
SELECT
m.id AS id,
reference_id,
title,
created_by,
publish_up,
state
FROM z_news_master m
LEFT JOIN z_news_spanish c ON m.id = c.reference_id
WHERE c.created_by = 17152
ORDER by c.id DESC
) AS B
GROUP BY id
Basically, I have 3 tables (z_news_master, z_news_english, z_news_spanish), to store News in Spanish or English languages. The z_news_master table contains the generic news information, the z_news_english and z_news_spanish contain the news in its respective language.
I need to get a list of the news, if the news is in both language tables it should return only one (not duplicated), the code above does the work, but if there is a new in English and Spanish, the record gets duplicated.
I'd also like to know why the GROUP BY id and the GROUP BY reference_id don't work?
Use a NOT EXISTS subquery to remove a fallback language row if a corresponding row for the prefered language exists. Assuming the prefered language is "english", the query would be:
SELECT
m.id AS id,
reference_id,
title,
created_by,
publish_up,
state
FROM z_news_master m
JOIN z_news_english c ON m.id = c.reference_id
WHERE c.created_by = 17152
UNION ALL
SELECT
m.id AS id,
reference_id,
title,
created_by,
publish_up,
state
FROM z_news_master m
JOIN z_news_spanish c ON m.id = c.reference_id
WHERE c.created_by = 17152
AND NOT EXISTS (
SELECT *
FROM z_news_english e
WHERE e.reference_id = m.id
AND e.created_by = c.created_by
)
ORDER by id DESC
Note that there is no need for GROUP BY. And a LEFT JOIN doesn't make sense because you have a WHERE condition on a column from the right table (which would convert the LEFT JOIN to INNER JOIN).
You can do this without using a union. The statement below gets all rows from the master table, and join any existing rows from both the spanish and english tables.
If a row exists in the spanish table, it uses the values from that table. If a row exists in the english table, and not the spanish table, it uses the values from that table.
If no matching row exists in either the english or spanish table, it returns columns from the master table.
You can alter the priorities by changing the order of the WHEN's.
SELECT
CASE
WHEN NOT s.id IS NULL THEN s.id
WHEN NOT e.id IS NULL THEN e.id
ELSE m.id AS `id`,
CASE
WHEN NOT s.reference_id IS NULL THEN s.reference_id
WHEN NOT e.reference_id IS NULL THEN e.reference_id
ELSE m.reference_id AS `reference_id`,
CASE
WHEN NOT s.title IS NULL THEN s.title
WHEN NOT e.title IS NULL THEN e.title
ELSE m.title AS `title`,
CASE
WHEN NOT s.created_by IS NULL THEN s.created_by
WHEN NOT e.created_by IS NULL THEN e.created_by
ELSE m.created_by AS `created_by`,
CASE
WHEN NOT s.publish_up IS NULL THEN s.publish_up
WHEN NOT e.publish_up IS NULL THEN e.publish_up
ELSE m.publish_up AS `publish_up`,
CASE
WHEN NOT s.state IS NULL THEN s.state
WHEN NOT e.state IS NULL THEN e.state
ELSE m.state AS `state`
FROM z_news_master m
LEFT JOIN z_news_spanish s ON m.id = s.reference_id
LEFT JOIN z_news_english e ON m.id = e.reference_id
WHERE m.created_by = 17152
ORDER by m.id DESC
GROUP BY m.id
EDIT
Per Paul Spiegel's comment here's an even shorter version:
SELECT
COALESCE(s.id, e.id, m.id) AS `id`,
COALESCE(s.reference_id, e.reference_id, m.reference_id) AS `reference_id`,
COALESCE(s.title, e.title, m.title) AS `title`,
COALESCE(s.created_by, e.created_by, m.created_by) AS `created_by`,
COALESCE(s.publish_up, e.publish_up, m.publish_up) AS `publish_up`,
COALESCE(s.state, e.state, m.state) AS `state`
FROM z_news_master m
LEFT JOIN z_news_spanish s ON m.id = s.reference_id
LEFT JOIN z_news_english e ON m.id = e.reference_id
WHERE m.created_by = 17152
ORDER by m.id DESC
GROUP BY m.id
I am relatively new to using group by and aggregate functions in SQL, I have the following tables:
CREATE TABLE `artists` (`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR( 100 ) NOT NULL );
CREATE TABLE `genres` (`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR( 100 ) NOT NULL);
CREATE TABLE `songs` (`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR( 100 ) NOT NULL, `artist_id` INT NOT NULL );
CREATE TABLE `songs_genres` (`song_id` INT NOT NULL, `genre_id` INT NOT NULL );
I am looking to return artists that have songs in multiple genres. Any ideas most welcome!
I have this so far to link everything together but cant quite work out the grouping / aggregation required:
select a.name as name, g.name as genre
from artists a inner join songs s on a.id = s.artist_id
inner join songs_genres sg on s.id = sg.song_id
inner join genres g on g.id = sg.genre_id
Thanks in advance.
This might help. It will find how many genres have multiple artists:
SELECT COUNT(a.Name) AS "No. of Artists",
g.Name AS "Genre"
FROM Artists a
INNER JOIN songs s on a.id = s.artist_id
INNER JOIN songs_genres sg on s.id = sg.song_id
INNER JOIN genres g on g.id= sg.genre_id
GROUP BY g.Name
This will give you the number of genres per artist:
SELECT COUNT(g.Name) AS "No. of Genres",
a.Name AS "Artist"
FROM Artists a
INNER JOIN songs s on a.id = s.artist_id
INNER JOIN songs_genres sg on s.id = sg.song_id
INNER JOIN genres g on g.id= sg.genre_id
GROUP BY a.Name
Adding a HAVING clause will allow you to narrow your results to those Artists/Genres with more than any number:
SELECT COUNT(g.Name) AS "No. of Genres",
a.Name AS "Artist"
FROM Artists a
INNER JOIN songs s on a.id = s.artist_id
INNER JOIN songs_genres sg on s.id = sg.song_id
INNER JOIN genres g on g.id= sg.genre_id
GROUP BY a.Name
HAVING Count(g.Name) > 1 -- or 2, or 10. This will skip over single-genre artists
What you're doing in these queries is counting how many times a given value shows up when grouping by another value.
You can check group by documentation http://www.w3schools.com/sql/sql_groupby.asp, bu I think you can do the following
select a.name as name, g.name as genre
from artists a inner join songs s on a.id = s.artist_id
inner join songs_genres sg on s.id = sg.song_id
inner join genres g on g.id = sg.genre_id
group by name, genre desc
I hope this helps, also you can check this threat Using group by on multiple columns
I have the following tables:
tasks ( id int PRIMARY KEY auto_increment,
description text,
created datetime,
modified datetime);
commentaries( id int PRIMARY KEY auto_increment,
task_id int,
user_id int,
description text,
created datetime );
users ( id int PRIMARY KEY auto_increment,
name varchar(300),
created datetime );
It's One-To-Many relationship (1-N).
I'm building an inbox, to list the last N tasks, and to show the last inserted commentary for each task?
EDIT1:
The query:
SELECT T.id, T.description, C.description, T.created
FROM tasks T LEFT JOIN commentaries C ON T.id = C.task_id AND C.id IN (
SELECT max(C.id) FROM tasks Task
LEFT JOIN commentaries C ON C.task_id = Task.id
WHERE Task.id in (select id from tasks WHERE user_id = 1 )
GROUP BY Task.id)
ORDER BY T.id DESC
LIMIT 10;
EDIT2
SQL Fiddle Link: http://sqlfiddle.com/#!2/1555c7/1
You can get the time of the last inserted commentary by using:
select task_id, max(created) as maxc
from commentaries
group by task_id;
You can get the last inserted commentary by using this in a join:
select t.*, c.*
from tasks t join
commentaries c
on c.task_id = t.id join
(select task_id, max(created) as maxc
from commentaries
group by task_id
) cmax
on cmax.task_id = c.task_id and cmax.maxc = c.created;
SELECT T.*, C.*
FROM tasks T
JOIN Commentaries C
ON T.id = C. task_id
GROUP BY T.id
ORDER BY C.task_id ASC
LIMIT 1
select * from task,com where task.id=com.task_id order by com.da desc limit 1;
I have 3 tables Products,ProductHas,Props. Needless to say each product has more than one prop which is kept in the ProductHas table. I'm trying to find the Product B which is closest to Product A interms of similarities of their props.
Current structure of the tables look like this.
+----------+----------+-----------+
|Products |ProductHas|Props |
+----------+----------+-----------+
|product_id|product_id|prop_id |
+----------+----------+-----------+
| name | prop_id |description|
+----------+----------+-----------+
Another option
SELECT A.Name, B.Name, COUNT(*)
FROM (
SELECT p.name, pp.description
FROM Products p
INNER JOIN ProductHas ph ON ph.product_id = p.product_id
INNER JOIN Props pp ON pp.prop_id = ph.prop_id
) AS A INNER JOIN (
SELECT p.name, pp.description
FROM Products p
INNER JOIN ProductHas ph ON ph.product_id = p.product_id
INNER JOIN Props pp ON pp.prop_id = ph.prop_id
) AS B ON B.description = A.Description
WHERE A.Name = 'A'
GROUP BY
A.name, B.Name
ORDER BY
COUNT(*) DESC
Try:
select h1.product_id, count(h0.prop_id) count_matches, count(*) total_props
from ProductHas h1
left join ProductHas h0
on h0.product_id = ? and h0.prop_id = h1.prop_id and h0.product_id <> h1.product_id
group by h1.product_id
order by 2 desc
limit 1
you could try a fulltext index on the props table, i
CREATE TABLE Props(
prop_id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
description TEXT,
FULLTEXT (description)
) ENGINE=MyISAM;
(I don't know anything about the size of the description but if you know its limit then you should put it like description VARCHAR(200))
SELECT *
FROM Props prod_a_props,
Props prod_b_props,
ProductHas prod_a_rel
WHERE prod_a_rel.product_id = :your_product_A_id
AND prod_a_props.prop_id = prod_a_rel.prop_id
AND MATCH (prod_b_props.description) AGAINST (prod_a_props.description);
Try something like this:
SELECT B.product_id
FROM Products B
INNER JOIN
ProductHas HB
INNER JOIN
ProductHas HA
INNER JOIN
Products A
ON HA.product_id = A.product_id
ON HA.prop_id = HB.prop_id
AND HA.product_id != HB.product_id
ON B.product_id = HB.product_id
WHERE A.product_id = xxx
GROUP BY B.product_id
ORDER BY COUNT(A.product_id) DESC
LIMIT 1
I'm trying to build a query that will order results by date and time, but the date and time upon which I want to base the ordering is contained in two tables. Additionally, I'd like to group the data by loc_id for rows having the same date.
For example, if I had this data:
Notes:
- parent_id links to the id in TABLE A.
- there are two other tables USER and LOC which are used to get the user name and location name.
- comments are optional in TABLE A, but will always exist in TABLE B.
I've found a lot of info on this site that's close to what I need, but nothing that's quite helped me solve this issue.
Thanks.
SELECT a.`date`
, a.`time`
, l.loc_name AS l_name
, a.rating
, a.comment
, u.user_name
FROM table_a AS a
LEFT JOIN loc AS l
ON l.loc_id = a.loc_id
LEFT JOIN `user` AS u
ON u.user_id = a.user_id
UNION ALL
SELECT b.`date`
, b.`time`
, l.loc_name AS l_name
, NULL AS rating
, b.comment
, u.user_name
FROM table_b AS b
LEFT JOIN table_a AS a
ON a.id = b.parent_id
LEFT JOIN loc AS l
ON l.loc_id = a.loc_id
LEFT JOIN `user` AS u
ON u.user_id = b.user_id
ORDER BY `date`
, l_name
, `time`
Something like this?
(Assumed your users table was called users and your locations table locations.)
SELECT a.date, a.time, l.loc_name, a.rating, b.comment, u.user_name
FROM table_a a
JOIN locations l ON l.loc_id = a.loc_id
LEFT JOIN table_b b ON b.parent_id = a.id
RIGHT JOIN users u ON a.user_id = u.user_id
GROUP BY a.loc_id
ORDER BY a.date DESC, a.time DESC