PHP MySQL Active Record criteria sum - php

I have two tables, users and visits. I want to select all users who have 'active' in the visits table's status field, with a count of all visit records regardless of active/inactive status.
This query would only give me a count of 'active' visit records
SELECT
users.user_id,
COUNT(visits.id)
FROM users u
JOIN visits v ON v.user_id=u.user_id
WHERE visits.status='active'
I was thinking of a subquery, or a php loop but worried about performance if the users table grows. If subquery's the best solution please post the active record code.

A sub-query will be more efficient than looping in the code. I'd suggest this query:
select u.user_id, ucount
from users u
join (
select user_id, COUNT(id) as ucount
from visits
group by user_id
) all
ON u.user_id = all.user_id
join visits v
on v.user_id=u.user_id
where v.status='active'
And if you need only user_id and no users.* field, then you don't have to join to users table (inspired by answer from arunmoezhi):
select user_id,count(*) as visit_count
from visits v
join (
select user_id from visits
where status ='active'
) act
on v.user_id=act.user_id
group by v.user_id;
Edited for formatting.

select user_id,count(*) as visit_count
from visits
where user_id in(
select user_id from visits
where status ='active'
)
group by user_id;

Related

Joining 3 tables and matching null entries

I have 3 tables, users, news, news_viewed. I'm trying to join these 3 tables and find a list of news each user has not viewed.
TABLE users
userid
username
status
TABLE news
newsid
title
post_time
TABLE news_viewed
nvid
username
newsid
Looking to find a list from users that have not read news (found in news_viewed)
I've tried many different joins, including left joins and inners and outers but cannot get the results I need.
$_30daysago = strtotime('-30 days');
SELECT * FROM
(
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
UNION
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable
I need the required results to include the users.username, news.newsid and news.title.
Any help would be appreciated, thank you!
This is a good spot to use the LEFT JOIN antipattern:
SELECT u.username, n.newsid, n.title
FROM users u
INNER JOIN news n ON n.post_time > ?
LEFT JOIN news_viewed nv
ON n.newsid = nv.newsid
AND nv.username = u.username
WHERE
u.status = 'active'
AND nv.nvid IS NULL
This query generates a cartesian product of users and recent news (ie having a post time greater than the parameter indicated by ?), and returns the users/news tuples for which the left join on news_viewed did not succeed (hence the antipattern).
Note: it is unclear what column to use in the join; column name news_viewed (username) tend to indicate that it relates to users(username), whereas the primary key of users seems to be userid. Fix your column names or fix your relationship.
Ellaborating on #GMB's answer
Your query:
$_30daysago = strtotime('-30 days');
SELECT * FROM
(
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
UNION
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
) as JoinedTable
is saying:
get all active users with the news they have read (inner join)
SELECT users.username, news_id
FROM users inner join news_viewed ON
users.username = news_viewed.username and users.status='active'
and add all the news with the users that have read them in the last 30 days (inner join again)
SELECT news_viewed.username, post_time
FROM news_viewed inner join news ON
news_viewed.newsid = news.newsid and news.post_time>'$_30daysago'
That is actually bringing up all the tuples from news_viewed minus the ones where the user is not active AND the new is over 30 days old.
however, given the usage of inner join, you're bringing a lot of duplicate records
1.- The results from the first query where the new is less than 30 days old
2.- The results from the second query where the user is active
since you're using UNION and not UNION ALL, you are implicitly asking for a SELECT DISTINCT, but the fields are different (it makes no sense to display newsid and then post_time in the same field)
plus, you have a typo in the field name, which is not news_id
You have to look at it from the other way around. The potential combinations amount for a scenario where every user has read every new. So you get that universe as a basis (number of users times number of news) and then
1- remove inactive users
2- remove news older than 30 days
3- remove tuples that are unrelated in the news_viewed table
SELECT users.username, news.newsid
FROM users
JOIN news
ON users.status='active' -- removes inactive users
AND news.post_time>'$_30daysago' -- removes older news
LEFT JOIN
news_viewed nv USING (username, newsid)
WHERE nv.nvid IS NULL -- removes unrelated entries

PHP mysqli. ORDER SELECT after one table but SELECT data from another table?

I have 2 tables.
visitors:
pageID
visitorID
last_visit
users:
userID(this ID and the pageID and visitorID in visitors is bundled to the same exact user)
username
age
I want to select last 5 visitors of the current logged in user (ORDER BY last_visit LIMIT 5 in the table visits)
I want to select the data from table users of those 5 users.(username, age, etc.)
How do i do this?
Use a JOIN with a subquery that gets the last 5 visitors.
SELECT u.*
FROM users AS u
JOIN (SELECT visitorId
FROM visitors AS v
WHERE v.pageID = $currentUserID
ORDER BY last_visit
LIMIT 5) AS v1
ON u.userID = v1.visitorID

MySQL, combining DISTINCT and JOIN

I have a table of users, called ..wait for it... "users".. and then another table which records users' activity called ...you'll never guess... "activity"..
I have an HTML table which shows the users in the users table, but I want to order the table by the users' last activity.
Here's my query.
SELECT *
FROM `users`
JOIN `activity`
ON `activity`.`user_id` = `users`.`id`
ORDER BY `activity`.`timestamp`
LIMIT 25
The problem here is that it shows multiple rows for each user since there are multiple records for each user in the activity table.
How can I alter the query to only show one record for each user and order them by the last activity in the activity table.?
I did experiment with using the "DISTINCT" keyword but no luck :/
"one record for each user and order them by the last activity in the activity table.?"
Couly try this? Assuming you need both user info and activity info.
SELECT users.*, a.*
FROM users INNER JOIN (
SELECT user_id, MAX(timestamp) max_timestamp
FROM activity
GROUP BY user_id
) x ON users.user_id = x.user_id
INNER JOIN activity a ON x.user_id = a.user_id AND a.timestamp = x.max_timestamp
ORDER BY a.timestamp;
inner sub-query finds max_timestamp per user_id and outer JOIN finds activity has max_timestamp and user_id
This is not SQL solution, but what I would do is keep lastActivity column in Users table and update it when needed. Then sorting, filtering by this column would be very easy and efficient.

Inner, outer or full join on three tables in MySQL database on linux with apache

I want to have some help creating my query to get information from three different tables sharing information in common.
My first table is:
auctions
id title description user_id(who posted it)
My second table is:
bids
id user_id bid auction_id owner_id
My third table is:
users
id username X XX XXX XXXX
...and my SQL is as follows however it's not returning any results:
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
WHERE auctions.user_id=".$_SESSION['userid']."
INNER JOIN users ON auction_bids.user_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']."
What I need is to capture the auction's title, username who bidded on the auction and the bid. the auction has to have a bid and posted by the user who owns the $_SESSION['userid'].
Any help is appreciated.
You have two different 'where' statements, which may just need combining;
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
INNER JOIN users ON auction_bids.user_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']." AND auctions.user_id=".$_SESSION['userid']."
However, I'm not sure this is really what you want, as it will return only records where the specific user both 'owns' the item AND has bidded on it (both based on the userid session), rather than displaying all records from different people who have bidded on an item 'owned' by the user.
Something like: ?
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title FROM auction_bids, auctions
INNER JOIN users ON auction_bids.user_id = users.id,
WHERE auction.owner_id = ".$_SESSION['userid']."
Hope this points you in the right direction!
you have 2 where clauses, that is incorrect. I have revised your query based on your requirements.
SELECT auction_bids.user_id AS applicant, auction_bids.*, auctions.title
FROM auction_bids, auctions
INNER JOIN users ON auction_bids.owner_id = users.id
WHERE auction_bids.owner_id = ".$_SESSION['userid']."
AND auctions.user_id=auctions_bids.owner_id

In MySQL how do I count messages from a user?

I have tables as below.
Table Messages
message_id
parent_id
forum_id
user_id
Table Users
user_id
username
pass
How to query a user, and display the count of their messages with their username?
My query:
select count(subject), user_id from messages group by user_id;
The problem is that it only displays user_id and count of messages but no username. How do I make it display the name of the user?
You need to join the users table into your query:
select count(*), username
from messages, users
where users.user_id = messages.user_id
group by users.user_id;
SELECT username, COUNT(*) FROM messages M
JOIN users U USING (user_id)
GROUP BY U.user_id
This takes care of a scenario where two different user_id have the same username in the users table. Also this query is MySQL specific and may not work with other RDBMS.
I think this is the query you want.
$query = "SELECT u.username, COUNT(*) as total_count FROM messages m INNER JOIN users u ON m.user_id = u.user_id GROUP BY U.user_id"
Here total_count gives the total count of messages grouped by user id to avoid joining of same usernames.
Use below. I believe it will work.
SELECT messages.user_id, users.username, count(*)
FROM messages, users
WHERE messages.user_id = users.user_id
Good Luck!!!

Categories