How to code this SELECT statment? - mysql

Given this table :
mysql> describe activity;
+---------------------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------+-------------+------+-----+---------+-------+
| user_id | varchar(16) | NO | | NULL | |
| login_time | int(11) | NO | | NULL | |
| last_activity_time | int(11) | NO | | NULL | |
| last_activity_description | text | YES | | NULL | |
| logout_time | int(11) | NO | | NULL | |
+---------------------------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)
I want to select the most recent last_activity_time (standard Unix timestamp) for each user who is logged in (i.e has one or more rows where logout_time is not zer0).
I tried
SELECT user_id, login_time, MAX(last_activity_time)
FROM activity
WHERE logout_time="0";
...but that found only a single entry with two users logged in, probably because I am selecting for MAX(last_activity_time)
What I want is something like
SELECT all unique user_ids
SELECT each of those which has one or more entries where `logout_time` != 0
SELECT the maximum value of `logout_time` for each of those
all in one single SELECT statement. How can I do that?

SELECT user_id, MAX(logout_time)
FROM activity
WHERE logout_time <> "0"
GROUP BY user_id;

Related

MYSQL - output extra column based on a certain condition

At first, I want to apologize for providing such a weak title; I couldn't describe it in a better way.
Consider the following: We have three tables, one for users, one for records and one for ratings. The tables are quite self-explanatory but the schema for database is as following:
+---------------------+
| Tables_in_relations |
+---------------------+
| records |
| ratings |
| users |
+---------------------+
The schema for records table is as following:
+----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------------------+------+-----+---------+----------------+
| id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(256) | NO | | NULL | |
| year | int(4) | NO | | NULL | |
+----------+----------------------+------+-----+---------+----------------+
The schema for users table is as following:
+----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------------------+------+-----+---------+----------------+
| id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| email | varchar(256) | NO | | NULL | |
| name | varchar(256) | NO | | NULL | |
| password | varchar(256) | NO | | NULL | |
+----------+----------------------+------+-----+---------+----------------+
ratings table is, obvoiusly, where the ratings are stored among with the record_id and user_id and works as a relation table.
It's schema is as following:
+----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------------------+------+-----+---------+----------------+
| id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| record_id| smallint(5) unsigned | NO | MUL | NULL | |
| user_id | smallint(5) unsigned | NO | MUL | NULL | |
| rating | int(1) | NO | | NULL | |
+----------+----------------------+------+-----+---------+----------------+
Now, In my application, I have a search function that fetches records based on a certain keyword. The output should also include the average rating of a certain record and a total amount of ratings per record. This can be accomplished by following query:
SELECT re.id, re.title, re.year, ROUND(avg(ra.rating)) as avg_rate,
COUNT(ra.record_id) as total_times_rated
FROM records re
LEFT JOIN ratings ra ON ra.record_id = re.id
GROUP BY re.id;
which will give me the following output:
+----+------------------------+------+----------+-------------------+
| id | title | year | avg_rate | total_times_rated |
+----+------------------------+------+----------+-------------------+
| 1 | Test Record 1 | 2008 | 3 | 4 |
| 2 | Test Record 2 | 2012 | 2 | 4 |
| 3 | Test Record 3 | 2003 | 3 | 4 |
| 4 | Test Record 4 | 2012 | 3 | 3 |
| 5 | Test Record 5 | 2003 | 2 | 3 |
| 6 | Test Record 6 | 2006 | 2 | 3 |
+----+------------------------+------+----------+-------------------+
Question:
Now, here comes the tricky part, at least for me. Within my app, you can search records whether signed in or not and if signed in, I'd also like to include the user's own rating value in the above query.
I know that I can run a conditional to check whether user is signed in or not by reading the session value and execute a corresponding query based on that. I just don't know how to include that individual rating value of a certain user to the above query.
You can add user's rating in the result by adding a SELECT query in columns:
SELECT re.id, re.title, re.year, ROUND(avg(ra.rating)) as avg_rate,
COUNT(ra.record_id) as total_times_rated,
(SELECT rating FROM ratings WHERE user_id = ? AND record_id = re.id) as user_rating
FROM records re
LEFT JOIN ratings ra ON ra.record_id = re.id
GROUP BY re.id;
We can get the user_id from session and pass it to this query in order to generate user_rating column in the result.
Assuming user can rate a record multiple times, I have used SUM. If not, we can remove it from the query.
Update
If you don't want GROUP BY to consider that value then you can wrap the existing query into another query and add a column to it, e.g.:
SELECT a.id, a.title, a.year, a.avg_rate, a.total_times_rated,
(SELECT rating FROM ratings WHERE user_id = ? AND record_id = a.id) as user_rating
FROM (SELECT re.id as id, re.title as title, re.year as year, ROUND(avg(ra.rating)) as avg_rate,
COUNT(ra.record_id) as total_times_rated
FROM records re
LEFT JOIN ratings ra ON ra.record_id = re.id
GROUP BY re.id) a;

mysql average latest 5 rows

I have table:
describe tests;
+-----------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-----------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| line_id | int(11) | NO | | NULL | |
| test_time | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| alarm_id | int(11) | YES | | NULL | |
| result | int(11) | NO | | NULL | |
+-----------+-----------+------+-----+-------------------+-----------------------------+
And I execute query:
SELECT avg(result) FROM tests WHERE line_id = 4 ORDER BY test_time LIMIT 5;
which I want to generate average of 5 latest results.
Still something is not ok, because query generates average of all table data.
What can be wrong?
If you want the last five rows, then you need to order by the time column in descending order:
select avg(result)
from (select result
from tests
where line_id = 4
order by test_time desc
limit 5
) t
the guy before submitted something link that
for my it works
select avg( id ) from ( select id from rand limit 5) as id;
Only one result set will be returned because of the AVG function.

MySQL merge results into table from count of 2 other tables, matching ids

I've got 3 tables: model, model_views, and model_views2. In an effort to have one column per row to hold aggregated views, I've done a migration to make the model look something like this, with a new column for the views:
+---------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | | NULL | |
| [...] | | | | | |
| views | int(20) | YES | | 0 | |
+---------------+---------------+------+-----+---------+----------------+
This is what the columns for model_views and model_views2 look like:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user_id | smallint(5) | NO | MUL | NULL | |
| model_id | smallint(5) | NO | MUL | NULL | |
| time | int(10) unsigned | NO | | NULL | |
| ip_address | varchar(16) | NO | MUL | NULL | |
+------------+------------------+------+-----+---------+----------------+
model_views and model_views2 are gargantuan, both totalling in the tens of millions of rows each. Each row is representative of one view, and this is a terrible mess for performance. So far, I've got this MySQL command to fetch a count of all the rows representing single views in both of these tables, sorted by model_id added up:
SELECT model_id, SUM(c) FROM (
SELECT model_views.model_id, COUNT(*) AS c FROM model_views
GROUP BY model_views.model_id
UNION ALL
SELECT model_views2.model_id, COUNT(*) AS c FROM model_views2
GROUP BY model_views2.model_id)
AS foo GROUP BY model_id
So that I get a nice big table with the following:
+----------+--------+
| model_id | SUM(c) |
+----------+--------+
| 1 | 1451 |
| [...] | |
+----------+--------+
What would be the safest route for pulling off commands from here on in to merge the values of SUM(c) into the column model.views, matched by the model.id to model_ids that I get out of the above SQL query? I want to only fill the rows for models that still exist - There is probably model_views referring to rows in the model table which have been deleted.
You can just use UPDATE with a JOIN on your subquery:
UPDATE model
JOIN (
SELECT model_views.model_id, COUNT(*) AS c
FROM model_views
GROUP BY model_views.model_id
UNION ALL
SELECT model_views2.model_id, COUNT(*) AS c
FROM model_views2
GROUP BY model_views2.model_id) toupdate ON model.id = toupdate.model_id
SET model.views = toupdate.c

mysql query sub selecting group by

I have a table described below
mysql> describe payments;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(128) | NO | | NULL | |
| email | varchar(128) | NO | | NULL | |
| txn_id | varchar(19) | NO | | NULL | |
| payment_status | varchar(20) | NO | | NULL | |
| auth | varchar(40) | NO | | NULL | |
| expired_at | datetime | YES | | NULL | |
+----------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)
It is possible that I could have 2 entries such as:
name: chris|expires at: 2012-01-01|email: me#chrismuench.com
name: chris|expires at: 2014-01-01|email: me#chrismuench.com
I want to do a query that I find all expired users based on downloads. But there could be multiple entires for the same email address. In the above case this person should NOT show up in the expired list.
It seems like I want to do a GROUP BY email but somehow filter out expiration if it is > NOW()
It would seem that for each unique user you want the maximum date and to check whether that is before NOW() right? So:
SELECT name, MAX(expired_at) as latest_expired FROM payments WHERE lastest_expired < NOW() GROUP BY name;
Here is what you would have to do. I haven't used datetime functions in a bit so you'll have to double check it for correctness. I just assumed NOW() was a function.
Find the set of unexpired users, then subtract it from the entire set of users.
select * from table where email not in (select email from table where expired_at > NOW());

Mysql selecting unique values

I am really stuck with an sql query... I hope someone can help shed some light for me.
Here is what my table looks like
mysql> show fields from france_data;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| email | varchar(45) | YES | | NULL | |
| name | varchar(45) | YES | | NULL | |
| lastname | varchar(45) | YES | | NULL | |
| quality | varchar(45) | YES | | NULL | |
| country | varchar(45) | YES | | NULL | |
| state | varchar(45) | YES | | NULL | |
| year | varchar(45) | YES | | NULL | |
| owner | varchar(45) | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
Here's the catch, I have duplicate data in my table, I would like to pull out all data from this table, non-duplicated based on the email.
I ran a simple count like this:
mysql> select count(*) from france_data;
and this is the result set:
+----------+
| count(*) |
+----------+
| 2405259 |
+----------+
1 row in set (0.01 sec)
Now I tried to run a count like this:
mysql> select count(*) from france_data group by email;
Just to see how many unique records I have. Unfortunately this times out.
Does any one know how I can do a count of unique rows and select of the same type?
Please try this
SELECT COUNT(DISTINCT email) FROM france_data
If you're looking at unique rows based on email, a simple
select count(distinct email) from france_data
should do the trick.
If you're also looking to see each email's duplicate count, try this:
select email, count(*) as cnt from france_data group by email order by cnt desc;
Please try this
SELECT count(DISTINCT email) FROM france_data ORDER BY email;