Select results from table1 based on entries on table2 - php

I have 2 tables;
banner_views (id, b_id, b_date)- this record a banner view every time it gets displayed
banners_dynamic (id, status, static_iname, static_keywords, static_url, static_alt, static_type, static_image, b_views, b_clicks) - stores the banner data
I would like to select 3 banners_dynamic results which have had the least views in the last 7 days.
I did put somethign together (see below) but I realised it was grabbing the total views for all banner rather than uniquely by id.
SELECT *,
(SELECT COUNT(*) FROM banner_views v WHERE v.b_date >= DATE(NOW()) - INTERVAL 7 DAY) as post_count
FROM banners_dynamic b
WHERE static_keywords LIKE '%test%' AND b.status='1' AND b.static_type='1'
ORDER BY post_count ASC LIMIT 3
Can anyone point me in the correct direction?

You must join both banners_dynamic table and your subquery with corresponding banner IDs:
SELECT
b.*, p.b_count
FROM
banners_dynamic b
INNER JOIN (
SELECT
b_id,
COUNT(*) AS b_count
FROM
banner_views v
WHERE
v.b_date >= DATE(NOW() - INTERVAL 7 DAY)
GROUP BY
b_id
) p on p.b_id = b.id
WHERE
b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
ORDER BY
p.b_count ASC
LIMIT 3
UPDATE: You can do it even without subquery:
SELECT
b.*, COUNT(v.b_id) AS b_count
FROM
banners_dynamic b
INNER JOIN banner_views v ON v.b_id = b.id
WHERE
v.b_date >= DATE_ADD(NOW(), INTERVAL - 7 DAY)
AND b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
GROUP BY
v.b_id
ORDER BY
b_count ASC
LIMIT 3;
If you want to include banners without any views (count=0) then you must do a LEFT JOIN:
SELECT
b.*, COUNT(v.b_id) AS b_count
FROM
banners_dynamic b
LEFT JOIN banner_views v ON v.b_id = b.id
AND v.b_date >= DATE_ADD(NOW(), INTERVAL - 7 DAY)
WHERE
b.static_keywords LIKE '%test%'
AND b.`status` = '1'
AND b.static_type = '1'
GROUP BY
v.b_id
ORDER BY
b_count ASC
LIMIT 3;

Related

MySQLi Query 2 tables based on count form one table over 30 days

I am having a trouble with a query.
I need to collect 2 companies from trade_directory where they have status 1 and match 3 categories.
I want to select the 2 companies on the least view first basis counted from company_views within the last 30 days.
SELECT
b.id, b.v_date, b.c_id COUNT(b.c_id) AS v_count
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
FROM
company_views b
LEFT JOIN trade_directory v ON v.id = b.c_id
WHERE
v.cat1 = 'cat'
AND v.cat2 = 'rat'
AND v.cat3 = 'hat'
AND v.status = '1'
GROUP BY
b.c_id
ORDER BY
v_count ASC
LIMIT 2
Thanks
Chris
You're using AND operator in SELECT instead of WHERE
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
can your try this code
SELECT
b.id, b.v_date, b.c_id COUNT(b.c_id) AS v_count
FROM
company_views b
LEFT JOIN trade_directory v ON v.id = b.c_id
WHERE
v.cat1 = 'cat'
AND v.cat2 = 'rat'
AND v.cat3 = 'hat'
AND v.status = '1'
AND b.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
GROUP BY
b.c_id
ORDER BY
v_count ASC
LIMIT 2
I changed my approach (using different tables but here it what is working but it is really really slow)
SELECT o.*, COUNT(e.c_id) AS b_count
FROM cat3_entries o
LEFT JOIN company_views e ON e.c_id = o.c_id
AND e.v_date >= DATE_ADD(NOW(), INTERVAL - 30 DAY)
WHERE
o.c3_id = '81'
GROUP BY
o.c_id
ORDER BY
b_count ASC
LIMIT
3;
If I could resolve the speed issue this one would be great.

count clause to output all records in database - mysql

I been trying different combination, but I cant seems to get this to work. I have inner join tables, I want to count the number of QA ISSUE found in the records and also output those records with only QA ISSUE, How would I do that?
SELECT d.department, m.mo_number, m.part_number, c.category,
COUNT(CASE WHEN c.category = 'QA ISSUE' THEN category END) as qa_issue,
SUM(CASE WHEN c.category = 'QA ISSUE' THEN time_spent END) as time_spent
FROM master as m
INNER JOIN category as c ON c.cat_id = m.cat_id
INNER JOIN department as d ON d.dept_id = m.dept_id
WHERE m.date_created >= DATE_SUB(now(), INTERVAL 50 DAY) AND
d.department = 'Electronics'
GROUP BY m.mo_number
ORDER BY 1
To filter results by aggregates you use the HAVING clause which occurs after the GROUP BY clause. Note this is not a substitute for the WHERE clause (which chooses the rows to be aggregated).
SELECT
d.department
, m.mo_number
, m.part_number
, c.category
, COUNT(*) AS qa_issue
, SUM(time_spent) AS time_spent
FROM master AS m
INNER JOIN category AS c ON c.cat_id = m.cat_id
INNER JOIN department AS d ON d.dept_id = m.dept_id
WHERE m.date_created >= DATE_SUB(now(), INTERVAL 50 DAY)
AND d.department = 'Electronics'
AND c.category = 'QA ISSUE'
GROUP BY
d.department
, m.mo_number
, m.part_number
, c.category
HAVING COUNT(*) = 1
ORDER BY
d.department
I have also added a condition to the where clause and added all non-aggregated columns into the GROUP BY clause - which I recommend you do always.

get user rank according to the like number

I have 3 tables as below in MySQL:
user: id username ...
post: id user_id title ....
user_post_like: id post_id user_id like_date (every like in a separate row)
Every user can send and like posts.
Now I wanna get the specified user daily rank according to the number of likes(from user_post_like table) that the user's posts got in the last 24 hours using an SQL command.
Maybe using count command for counting the users that have more likes than the specified user.
select u.username, count(l.id) as likes
from user_post_like l
join `user` u on u.od = l.user_id
where now() - interval 24 hour >= l.like_date
group by u.id, u.username
order by count(l.id) desc
and if you need a rank number too, you can do
select u.username, count(l.id) as likes, #rank := #rank + 1 as rank
from user_post_like l
join `user` u on u.od = l.user_id
cross join (select #rank := 0) r
where now() - interval 24 hour >= l.like_date
group by u.id, u.username
order by count(l.id) desc

MySQL Subquery Optimization - where not in(subquery)

I'm trying to optimize the following query. I'm thinking an outer join would do the trick, but I can't wrap my mind around how to put it together.
// ---------------------------------
// Simplified representation of data
// ---------------------------------
create table views (
user_id,
article_id
)
create table article_attributes (
article_id,
article_attribute_id
)
create table articles (
id,
title,
date
)
Views table has tens of millions of records.
Articles table has a couple hundred thousand.
I'm trying to match all articles with a certain attribute associated with it, and that have not been viewed by a user.
What I have tried, but doesn't scale well:
select a.title, a.sid as article_id, a.total_views as times_read, a.date
from articles a
join article_attributes att on att.article_id = a.sid
where a.sid not in(
select v.article_id
from views v
join article_attributes att on att.article_id = v.article_id
where user_id = 132385
and att.article_attribute_id = 10
group by v.article_id
)
and att.article_attribute_id = 10
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
order by total_views desc
limit 5
This works fine, but gets significantly slower the more articles the user has viewed. Any ideas or suggestions would be appreciated.
SELECT a.title, a.sid AS article_id, a.total_views AS times_read, a.date
FROM articles a
JOIN article_attributes att
ON a.id = att.article_id AND att.article_attribute_id = 10
LEFT JOIN views v
ON a.id = v.article_id AND v.user_id = 132385
WHERE v.user_id IS NULL
The first join gets you only the articles with the given attribute.
The second join takes the first join's result and returns rows with the user_id and all the remaining rows from first result that don't have the user_id.(Basically ALL articles with attribute 132385 with the user_id being either 10 or NULL)
Then all we want is that result where user_id is NULL
Try to avoid nested queries and let the engine do it's job. Note you can tag on your other filters (DATE, ORDER BY) on the end.
Try this query
select a.title, a.sid as article_id, a.total_views as times_read, a.date
from
articles a
left join
views v
on
a.sid = v.article_id AND v.article_id is null
join
article_attributes att
on
att.article_id = v.article_id AND v.user_id = 132385 AND att.article_attribute_id = 10
where
a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
order by
total_views desc limit 5
Create necessary index for articles table (total_views, sid, date)
view table (article_id, user_id)
article_attributes table (article_id, article_attribute_id)
Hope this helps.
Instead of using the subquery as a where condition, I suggest using it in a join. Also, I suggest you not to use group by in your subquery, but select distinct:
select
a.title, a.sid as article_id, a.total_views as times_read, a.date
from
(articles a
inner join article_attributes att on a.sid = att.article_id)
left join (
select distinct
v.article_id
from views v
inner join article_attributes att on v.article_id = att.article_id
where
user_id = 132385
and att.article_atribute_id = 10
) as b on a.sid = b.article_id
where
b.article_id is null
and att.article_attribute_id = 10
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
Hope this helps
EXISTS should work better than IN:
SELECT a.title,
a.sid AS article_id,
a.total_views AS times_read,
a.date
FROM articles a
JOIN article_attributes att ON att.article_id = a.sid
WHERE NOT EXISTS (SELECT 0
FROM views v
JOIN article_attributes att ON att.article_id = v.article_id
WHERE user_id = 132385
AND att.article_attribute_id = 10
AND v.article_id = a.sid )
AND att.article_attribute_id = 10
AND a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
ORDER BY total_views DESC LIMIT 5

Joining Three Tables

I'm trying to take data from three different tables and output it using as few queries and as little PHP code as possible.
Listed below are the tables I have and the columns in each (only listing relevant columns).
exp_members (A)
columns: member_id, group_id
exp_brandrelations (B)
columns: member_id, brand_id
exp_du_mktgmats (C)
columns: du_id, brand_id, date
I want to loop through the members who belong to group_id='5' (from A), determine which brands are assigned to each member (from B), and get a list of du_ids (from C) that correspond to each member, that have been INSERTed in the last 24 hours.
So far, I can get a list of members in group 5:
SELECT member_id, brand_id FROM exp_brandrelations
WHERE member_id IN (SELECT member_id FROM exp_members where group_id = 5)
And I can get a list of du_ids from the last 24 hours:
SELECT du_id FROM exp_du_mktgmats
WHERE date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
But I'm not sure how best to tie it all together.
This should do it!
SELECT m.member_id, b.brand_id, d.du_id FROM exp_members m, exp_brandrelations b, exp_du_mktgmats d WHERE m.group_id = '5' AND m.member_id = b.member_id AND b.brand_id = d.brand_id AND d.date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
SELECT du_id FROM exp_members m, exp_brandrelations r, exp_du_mktgmats a
WHERE a.brand_id=r.brand_id AND r.member_id=m.member_id
AND date >= DATE_SUB(NOW(), INTERVAL 1 DAY)
AND m.group_id='5'
SELECT
c.du_id
FROM
exp_du_mktgmats c
LEFT JOIN
exp_brandrelations b
ON
c.brand_id = b.brand_id
LEFT JOIN
exp_members a
ON
b.member_id = a.member_id
WHERE
a.group_id = 5
AND
c.date >= DATE_SUB(NOW(), INTERVAL 1 DAY);

Categories