MySQL DELETE statement with a join, HAVING and GROUP BY - mysql

I have a table of items and another of reports. Each report has a foreign key linking to an item being reported.
I am trying to delete all items displayed in this query:
SELECT items.id, title, SUM(weight) AS total_weight, SUM(weight)*10/views AS score
FROM items, reports
WHERE items.id = reports.item_id
GROUP BY items.id
HAVING score >= 50;
Trying something like this:
DELETE items
FROM (SELECT items.id, title, SUM(weight) AS total_weight, SUM(weight)*10/views AS score
FROM items, reports
WHERE items.id = reports.item_id
GROUP BY items.id
HAVING score >= 50)
AS T;
Gives me this error message:
ERROR 1109 (42S02): Unknown table 'items' in MULTI DELETE

In MySQL, you have to be careful about the subqueries. I think the following works:
DELETE FROM items
WHERE id IN (select *
from (SELECT items.id
FROM items join reports
on items.id = reports.item_id
GROUP BY items.id
HAVING SUM(weight)*10/views >= 50
)
)
It tricks the compiler into accepting the query by using an additional subquery. I also fixed your join syntax.
The following, though, rewrites the query into a more common syntax, using a correlated subquery:
delete from items
where exists (select r.item_id
from reports r
where r.item_id = items.item_id
group by r.item_id
having SUM(r.weight)*10/items.views >= 50
)
This is guessing that weight and views come from reports. Otherwise, you need to put theitems` alias in front instead.

DELETE FROM items
WHERE
id IN (
SELECT
items.id
FROM items, reports
WHERE items.id = reports.item_id
GROUP BY items.id
HAVING SUM(weight)*10/views >= 50)

I believe your delete statement is wrong. It should be delete from tablename where [condition].
DELETE FROM items
WHERE
id IN (
Select T.id from (SELECT items.id, title, SUM(weight) AS total_weight, SUM(weight)*10/views AS score
FROM items, reports
WHERE items.id = reports.item_id
GROUP BY items.id
HAVING score >= 50) T)

Try this:
DELETE FROM items
WHERE id IN (SELECT id
FROM (SELECT i.id itemId, (SUM(weight) * 10 / views) score
FROM items i INNER JOIN reports r ON i.id = r.item_id
GROUP BY itemId HAVING score >= 50
) AS A
);

Related

SQL query to check if value doesn't exist in another table

I have a SQL query which does most of what I need it to do but I'm running into a problem.
There are 3 tables in total. entries, entry_meta and votes.
I need to get an entire row from entries when competition_id = 420 in the entry_meta table and the ID either doesn't exist in votes or it does exist but the user_id column value isn't 1.
Here's the query I'm using:
SELECT entries.* FROM entries
INNER JOIN entry_meta ON (entries.ID = entry_meta.entry_id)
WHERE 1=1
AND ( ( entry_meta.meta_key = 'competition_id' AND CAST(entry_meta.meta_value AS CHAR) = '420') )
GROUP BY entries.ID
ORDER BY entries.submission_date DESC
LIMIT 0, 25;
The votes table has 4 columns. vote_id, entry_id, user_id, value.
One option I was thinking of was to SELECT entry_id FROM votes WHERE user_id = 1 and include it in an AND clause in my query. Is this acceptable/efficient?
E.g.
AND entries.ID NOT IN (SELECT entry_id FROM votes WHERE user_id = 1)
A left join with an appropriate where clause might be useful:
SELECT
entries.*
FROM
entries
INNER JOIN entry_meta ON (entries.ID = entry_meta.entry_id)
LEFT JOIN votes ON entries.ID = votes.entry_id
WHERE 1=1
AND (
entry_meta.meta_key = 'competition_id'
AND CAST(entry_meta.meta_value AS CHAR) = '420')
AND votes.entry_id IS NULL -- This will remove any entry with votes
)
GROUP BY entries.ID
ORDER BY entries.submission_date DESC
Here's an implementation of Andrew's suggestion to use exists / not exists.
select
e.*
from
entries e
join entry_meta em on e.ID = em.entry_id
where
em.meta_key = 'competition_id'
and cast(em.meta_value as char) = '420'
and (
not exists (
select 1
from votes v
where
v.entry_id = e.ID
)
or exists (
select 1
from votes v
where
v.entry_id = e.ID
and v.user_id != 1
)
)
group by e.ID
order by e.submission_date desc
limit 0, 25;
Note: it's generally not a good idea to put a function inside a where clause (due to performance reasons), but since you're also joining on IDs you should be OK.
Also, The left join suggestion by Barranka may cause the query to return more rows than your are expecting (assuming that there is a 1:many relationship between entries and votes).

can we have a query of "10 posts from every category in one request"?

We have a list of posts that belong a category.
can we have a query of "10 posts from every category in one request" ?
Or
We have to query 10 posts for every category separately ?
thx
A fudge involving using GROUP_CONCAT to put all the post ids for a category together (you can add an order clause to this if you want), then substring_index to get the first 10 posts.
This is then joined back to the original table using FIND_IN_SET
SELECT a.*
FROM some_table a
INNER JOIN
(
SELECT category_id, SUBSTRING_INDEX(GROUP_CONCAT(post_id), ',', 10) AS posts
FROM some_table
GROUP BY catgegory_id
) sub0
ON a.category_id = sub0.category_id
AND FIND_IN_SET(a.post_id, sub0.posts)
Or using variables:-
SELECT a.*
FROM some_table a
INNER JOIN
(
SELECT category_id, post_id, #cnt:=IF(#category_id=category_id, #cnt + 1, 1) AS cnt, #category_id:=category_id
FROM
(
SELECT category_id, post_id
FROM some_table
ORDER BY category_id, post_id
) sub0
CROSS JOIN (SELECT #cnt:=0, #category_id:=0) sub1
) sub2
ON a.category_id = sub2.category_id
AND a.post_id = sub2.post_id
AND cnt <= 10
This pseudo code should point you in the right direction.
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN (SELECT * FROM T WHERE T.CATEGORY = T1.CATEGORY ORDER BY CATEGORY DESC LIMIT 10)

MySQL Joining tables and pulling information to check

I am trying to return a descending list of item types sold within a specified date range.
I understand how to get the amount of Item types and count them, but am stuck with how to link to the other tables. I will put my working code along with psuedo of how I think it needs to be done, any help would be greatly appreciated.
SELECT Item_Type, COUNT(*) as theAmount
FROM myDB.Item
//LINK order_ID to item
//WHERE order_ID includes Item_Type
//Link to session
//Find Ses_Date that order was made
GROUP BY Item_Type
ORDER BY theAmount desc
LIMIT 10
I have three tables I need to pull data from, the fields needed are shown:
Item
PK: Item_ID
Row: Item_Type
FK: Order_ID
Order
PK: Order_ID
FK: Session_ID
Session
PK: Ses_ID
Row: Ses_date
SELECT Item_Type, COUNT(*) as theAmount, Session_date
FROM Item
left outer join Order
on Item.Order_id = Order.Order_id
left outer join Session
on Order.Session_id = Session.Session_id
GROUP BY Item_Type
ORDER BY theAmount desc
LIMIT 10
The above query will give you the results.
SELECT i.Item_Type, COUNT(i.Item_Type) as theAmount
FROM Item AS i
JOIN Order AS o ON o.order_ID = i.Order_ID
JOIN Session AS s ON s.Ses_ID = o.Session_ID
WHERE s.Ses_date between #date1 AND #date2
GROUP BY i.Item_Type
ORDER BY theAmount DESC
LIMIT 10
you need to use LEFT JOIN in this case so if you have no matching records on the other table, the value for Item_Type would be zero.
SELECT a.Item_Type, COUNT(c.Ses_ID) theAmount
FROM Item a
LEFT JOIN `Order` b
ON a.Order_ID = b.Order_ID
LEFT JOIN `Session` c
ON b.Session_ID = c.Ses_ID
GROUP BY a.Item_Type
-- ORDER BY theAmount DESC
-- LIMIT 10

Order by most of items with Join

I've a 'items' table with the following structure:
id, person_id, active
and 'people' table with id and name columns.
I want to order by most active items and join the people.name column to sql.
I tried to do something like that, but it's not working:
SELECT people.id, COUNT(*) as items_count
FROM items, people
WHERE items.active = true AND people.id = items.person_id
GROUP BY items.person_id
ORDER BY items_count DESC
Use Joins this will match the condition. Order by items_count will give you most active user
Select people.id, COUNT(items.active) as items_count
FROM items
LEFT JOIN people on people.id = items.person_id
WHERE items.active = true
GROUP BY people.id
ORDER BY items_count DESC
select * from (SELECT people.id, COUNT(*) as items_count
FROM items
left outer join people on people.id = items.person_id
where items.active = true
GROUP BY items.person_id) as A
ORDER BY A.items_count DESC

MYSQL select statement - 5 results from each user

I'm trying to do a select statement and it works except that it's not limiting the number of results for each user (U.id) to 5.
SELECT F.id,F.created,U.username,U.fullname,U.id,I.id,I.cached_image
FROM favorites AS F
INNER JOIN users AS U
ON F.faver_profile_id = U.id
INNER JOIN items AS I
ON F.notice_id = I.id
WHERE faver_profile_id IN ('.$users.')
GROUP BY I.id HAVING COUNT(U.id) <= 5
ORDER BY F.faver_profile_id, F.created DESC
I'm grouping by I.id to eliminate duplicates. From my research it looks like you can only use HAVING COUNT if your also grouping by that column, but I cannot group by U.id or I'd lose results rows.
Instead of HAVING, can you slap a LIMIT 5 in there?
Edit: OP cannot LIMIT entire query,
and, AFAIK, MySQL does not support LIMIT in subqueries,
so you can create a temporary table with your five (5) user ids:
create table temp_table ( id INT );
insert into temp_table (id) SELECT U.id FROM users U LIMIT 5;
SELECT F.id,F.created,U.username,U.fullname,U.id,I.id,I.cached_image
FROM favorites AS F
INNER JOIN temp_table AS Ut
ON F.faver_profile_id = Ut.id
INNER JOIN items AS I
ON F.notice_id = I.id
WHERE faver_profile_id IN ('.$users.')
GROUP BY I.id
ORDER BY F.faver_profile_id, F.created DESC;
drop table temp_Table;
Let us know how that works.