MYSQL 1 to N JOIN - mysql

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;

Related

optmize a select query (if necessary creating index)

I have 2 tables:
chats (id, ..., chat_status_id) // (about 28k records)
chat_messages(id, chat_id, send_date, ...) // (about 1 million records)
I need to get chats of certain status with latest message.
This is the select I am using, but it's pretty slow (it works in the end):
SELECT c.*,
p1.*
FROM chats c
JOIN chat_messages p1
ON ( c.id = p1.chat_id )
LEFT OUTER JOIN chat_messages p2
ON ( c.id = p2.chat_id
AND ( p1.send_date < p2.send_date
OR ( p1.send_date = p2.send_date
AND p1.id < p2.id ) ) )
WHERE p2.id IS NULL
AND c.chat_status_id = 1
ORDER BY p1.send_date DESC
I do not know howto optimize it.
I would start with a few index updates. First, your WHERE clause is based on the CHATS table status, that should be first in your index order, but can have the ID as well AFTER to support the joins. Next, your messages table. Similarly, those are JOINed based on the CHAT ID, not its own unique table ID as the primary consideration, but does use the ID as the carry-along for conditional testing of p1.id < p2.id. Suggestions as follows.
Table Index
Chats ( Chat_Status_Id, id )
Chat_Messages ( Chat_id, id, send_date )
Give this a try:
SELECT c.*, p1.*
FROM chats c
JOIN chat_messages p1 ON ( c.id = p1.chat_id )
WHERE NOT EXISTS
(
SELECT 1
FROM chat_messages p2
WHERE c.id = p2.chat_id
AND ( p1.send_date < p2.send_date
OR ( p1.send_date = p2.send_date
AND p1.id < p2.id ) )
)
WHERE c.chat_status_id = 1
ORDER BY p1.send_date DESC
With
chats: INDEX(chat_status_id, id)
chat_messages: INDEX(chat_id, send_date, id)

Get a Combined Result in MySQL

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;

delete from database except that filter

I have a database where people via a command sends information and its stored in the database. The same person can have many reports per day and the next query shows only the latest:
SELECT
r1.id,
r1.nickname,
r1.fecha,
r1.bestia1,
r1.bestia2,
r1.bestia3,
r1.bestia4,
r1.bestia5
FROM
reporte AS r1
INNER JOIN
( SELECT
nickname,
MAX(fecha) AS max_date
FROM
reporte
GROUP BY
nickname ) AS latests_reports ON latests_reports.nickname = r1.nickname AND latests_reports.max_date = r1.fecha
ORDER BY
r1.fecha DESC
But now I want to delete all the records except the records returned from the previous query, how can I do it?
Ideally you'd do this as a set of steps, but it may be possible to single-step it by:
DELETE FROM reporte
WHERE id not in
(
SELECT a.id FROM(
SELECT r1.id
FROM reporte AS r1
INNER JOIN ( SELECT nickname, MAX(fecha) AS max_date
FROM reporte GROUP BY nickname ) AS latests_reports
ON latests_reports.nickname =
r1.nickname AND latests_reports.max_date = r1.fecha
) a
)
You need the a.id query in there otherwise MySQL will complain that it can't update the reporte table
As separate steps:
CREATE TABLE to_keep AS
SELECT r1.id
FROM reporte AS r1
INNER JOIN
( SELECT nickname, MAX(fecha) AS max_date FROM reporte GROUP BY nickname ) AS latests_reports
ON
latests_reports.nickname = r1.nickname AND
latests_reports.max_date = r1.fecha
DELETE r.* FROM reporte r LEFT JOIN to_keep k ON r.id = k.id WHERE k.id IS NULL
DROP TABLE to_keep
EDIT: I have updated the query from EXITS() to NOT EXISTS()
You want to delete all records except the records your query listed. This is your query :
:
DELETE FROM Reporte WHERE NOT EXISTS
(
SELECT 1 FROM
(
SELECT r1.id,
FROM reporte AS r1
INNER JOIN ( SELECT nickname, MAX(fecha) AS max_date FROM reporte GROUP BY nickname ) AS latests_reports
ON latests_reports.nickname = r1.nickname
AND latests_reports.max_date = r1.fecha
) r WHERE Reporte.Id= r.Id
)

MySQL: SELECT if non of group by members is equal to x

I have troubles getting proper data.
I have table structure like:
id INT(11) AI
order_id INT(11)
status varchar(45)
This table log status changes for orders.
So order_id's will have few statuses.
Now I need to select rows and group them by order_id, where order never had status (not even one status with given order_id) != 'example'
We don't show orders, where one of members had status = example
Sample data
1 12 ready
1 12 example
2 13 ready
2 13 sent
So I don't want order 12 to show at all, because one of it members have "example" status
I've tried grouping results, but it's not enough.
you can do it by simple join query :
select a.order_id
from ordrstatus as a left outer join (select orderid , count(*) as status from orderstatus where status = 'example' group by orderid) as b on a.orderid = b.orderid
where b.status = 0 or b.status is NUll
Join query always run faster then IN query . by using Join in query it will run only one time .
You can try like this...it will return all order id which never had status -example
Select
Order_id,
from TableName A where Not Exists(
Select id from TableName B where
status='example' and
a.Order_id=b.Order_id
)
group by Order_id
Not quite sure if you want the records for order which have had a status of example, or ones which have never had a status of example
To get a list of orders (with the status grouped up) which have had a status of example:-
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
INNER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
GROUP BY order_id
To get those which have NEVER had a status of exmaple
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
WHERE b.order_id IS NULL
GROUP BY order_id
EDIT
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a -- Statuses
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b -- Get any order id which has had a status of example (as a LEFT JOIN)
ON a.order_id = b.order_id
INNER JOIN
(
SELECT order_id, MAX(id) AS Latestid
FROM SomeTable
GROUP BY order_id
) c -- Get the latest status for each order (ie, max id)
ON a.order_id = c.order_id
LEFT OUTER JOIN
(
SELECT order_id, id
FROM SomeTable
WHERE status = 'example2'
) d -- Get the id of the order status of example2
ON a.order_id = d.order_id AND c.Latestid = d.id -- join on the same order id and that the record id matches the latest record id
WHERE b.order_id IS NULL -- reject those where a match was found on example for any status
AND d.order_id IS NULL -- reject those where a match was found on example2 for the latest status
GROUP BY order_id
try this
SELECT Order_ID FROM tbl_Orders
WHERE Status NOT IN ('example')
GROUP BY Order_ID
SELECT DISTINCT x.order_id
FROM order_status x
LEFT
JOIN order_status y
ON y.order_id = x.order_id
AND y.status = 'example'
WHERE y.id IS NULL;

sql select the product with most users

users:
uid int(11) - userid(primary key, auto_increment)
name varchar(255)
pass varchar(64)
created int(11)
projects:
pid int(11) .....
name varchar(150)
description varchar(255)
created int(11)
users_projects:
uid int(11) - user id
pid int(11) - product id
How can i select the project with the most ussers assigned to it?
sql query.
You could use something like this:
select p.pid,
p.name,
up.TotalUsers
from projects p
inner join
(
select pid, count(uid) TotalUsers
from users_projects
group by pid
) up
on p.pid = up.pid
order by TotalUsers Desc
-- limit 1
See SQL Fiddle with Demo
This will return the list of all projects and a count of the Total Users per project. If you want to return the project will the most users, then you will include the limit 1 that is commented out.
If you have more than one project that has the same number of users, then you would want to use something similar to this:
select p.pid,
p.name,
up.TotalUsers
from projects p
inner join
(
select pid, count(uid) TotalUsers
from users_projects
group by pid
) up
on p.pid = up.pid
where totalusers = (select count(*) Total
from users_projects
group by pid
order by total desc
limit 1)
See SQL Fiddle with Demo
Thanks #JW for the Fiddle
The following query will include multiple projects having the same number of users and happens to be the most number of users.
SELECT a.name userName, c.name ProjectName
FROM users a
INNER JOIN users_projects b
ON a.uid = b.uid
INNER JOIN projects c
ON b.pid = c.pid
INNER JOIN
(
SELECT pid, COUNT(*) totalCount
FROM users_projects
GROUP BY pid
HAVING COUNT(*) = (SELECT COUNT(*) x
FROM users_projects
GROUP BY pid
ORDER BY x DESC
LIMIT 1)
) d ON b.pid = d.pid
ORDER BY a.Name ASC
SQLFiddle Demo (with duplicate project)
SQLFiddle Demo
If you are looking for only one project, the following is the fastest way:
select project_id, count(*) as NumUsers
from user_projects
group by project_id
order by count(*) desc
limit 1
(I am assuming that "product" = "project").