mysql query sub selecting group by - mysql

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());

Related

I want an other table to be updated when I create an entry

I want start_date and start_time copied into latest_time and latest_date, while adding a new entry into my logbook. But I want dependency on logbook.logbook_index_id = logbook_index.id for all entries too.
mysql> describe logbook;
+-------------------------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------------------+-----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| logbook_index_id | int(10) unsigned | NO | | NULL | |
| start_date | date | NO | | NULL | |
| start_time | time | NO | | NULL | |
mysql> describe logbook_index;
+--------------------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| first_date | date | NO | | NULL | |
| first_time | time | NO | | NULL | |
| latest_date | date | NO | | NULL | |
| latest_time | time | NO | | NULL | |
+--------------------+----------------------+------+-----+---------+----------------+
atm I got this far ...
create trigger update_dates after insert on logbook
for each row update logbook_index
set latest_date = start_date where logbook_index.id = logbook_index_id;
I do it mostly wrong I bet. How does this work correctly and how do I get the time copied too ?
If I understood your question correctly:
For this I would suggest using a trigger
You can put an AFTER INSERT trigger on the table that you insert, inside the trigger you can put the update to the other table.
In order to access variables from the newly insert record, you need to do the following:
UPDATE logbook_index
SET latest_date = NEW.start_date
WHERE logbook_index.id = NEW.logbook_index_id;
Notice the keyword NEW that is used to access the newly insert record.
If you were using an AFTER UPDATE trigger, you could access the old values by using OLD
What you're searching for is a Trigger, a procedure that's automatically invoked in response to an event, in your case the insertion of a row in the logbook table.

How to get the corresponding id of a max field in mysql

I am still new to mysql commands and I learned how to get the max value of a column and count of a column. My database is named db and my table (named "foo") looks like this:
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | varchar(64) | NO | PRI | NULL | |
| expires | datetime | YES | MUL | NULL | |
| extra | text | YES | | NULL | |
| valid | tinyint(1) | NO | | NULL | |
| trust_id | varchar(64) | YES | MUL | NULL | |
| user_id | varchar(64) | YES | MUL | NULL | |
+----------+-------------+------+-----+---------+-------+
Basically, I want to know if there is a way to get the max date of the expires field and then print out the corresponding fields like user_id and id. So far this is the command I currently use to get the max date from expires in the command line
mysql -u root db -e "select max(id) from foo;"
this alone will give me:
+---------------------+
| max(expires) |
+---------------------+
| 2016-08-05 17:54:44 |
+---------------------+
What I would like to do is get this back:
+---------------------+--------+--------+
| max(expires) | id | user_id|
+---------------------+--------+--------+
| 2016-08-05 17:54:44 |cd97eb4 | 2bf2cec|
+---------------------+--------+--------+
So then I would have the max expiration date along with the id and user id for that expiration date.
Any insight is greatly appreciated!
Use order by and limit:
select f.*
from foo f
order by f.expires desc
limit 1;
I think this is the simplest way. And with an index on foo(expires), it should have the best performance as well.
If you just want the id and user_id corresponding to that (max) date, then something like this should do
select expires, id, user_id
from foo
where expires in
(select max(expires)
from foo)

Why does this query return an intermediate record?

I ran a somewhat nonsense query on MySQL, but because its output is the same each time, I'm wondering if someone can help me understand the underlying algorithm.
Here's the table Orders on which we'll execute the query (database taken from here, just in case someone's interested):
+----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| orderNumber | int(11) | NO | PRI | NULL | |
| orderDate | date | NO | | NULL | |
| requiredDate | date | NO | | NULL | |
| shippedDate | date | YES | | NULL | |
| status | varchar(15) | NO | | NULL | |
| comments | text | YES | | NULL | |
| customerNumber | int(11) | NO | MUL | NULL | |
+----------------+-------------+------+-----+---------+-------+
There are 326 records for now, with the largest orderNumber being 10425.
Now here's the query I ran (basically removed GROUP BY from a sensible query):
mysql> select count(1), orderNumber, status from orders;
+----------+-------------+---------+
| count(1) | orderNumber | status |
+----------+-------------+---------+
| 326 | 10100 | Shipped |
+----------+-------------+---------+
1 row in set (0.00 sec)
So I'm asking for the total number of rows, along with status and orderNumber, which can be just about anything under the given circumstances. But the query always returns orderNumber 10100, even if I log out and run it again.
Is there a predictable answer for this?
There's no predictable answer for which you should use in your design. In general, the DB will return the values of the first row that matches the query. If you want predictability, you should apply an aggregate to every column (e.g. using MIN or MAX to always get smallest/largest value)

How to code this SELECT statment?

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;

Mysql update all rows based on select from another table

I have two tabels;
mysql> describe ipinfo.ip_group_country;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| ip_start | bigint(20) | NO | PRI | NULL | |
| ip_cidr | varchar(20) | NO | | NULL | |
| country_code | varchar(2) | NO | MUL | NULL | |
| country_name | varchar(64) | NO | | NULL | |
+--------------+-------------+------+-----+---------+-------+
mysql> describe logs.logs;
+----------------------+------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| ts | timestamp | NO | | CURRENT_TIMESTAMP | |
| REMOTE_ADDR | tinytext | NO | | NULL | |
| COUNTRY_CODE | char(2) | NO | | NULL | |
+----------------------+------------+------+-----+---------------------+----------------+
I can select country code using ip address from first table:
mysql> SELECT country_code FROM ipinfo.`ip_group_country` where `ip_start` <= INET_ATON('74.125.45.100') order by ip_start desc limit 1;
+--------------+
| country_code |
+--------------+
| US |
+--------------+
In logs.logs, I have all the REMOTE_ADDR (ip address) set, but all COUNTRY_CODE entries are empty. Now, I want to populate COUNTRY_CODE appropriately using the ipinfo table. How can I do this?
thanks!
Try
UPDATE logs.logs
SET COUNTRY_CODE = (
SELECT country_code
FROM ipinfo.ip_group_country
WHERE ipinfo.ip_start <= INET_ATON(logs.REMOTE_ADDR)
LIMIT 1
)
WHERE COUNTRY_CODE IS NULL
If it fails saying the column types must match, you'll have to alter your logs.logs table so that the REMOTE_ADDR column is the same type (varchar(20)) as the ip_cidr table.
In a single-table update you use update t1 set c1=x where y.
In a multi-table update you use update t1, t2 set t1.c1=t2.c2 where t1.c3=t2.c4
Here's the relevant documentation http://dev.mysql.com/doc/refman/5.0/en/update.html
What you're looking for is something along the lines of (editted) update logs.logs as l, ipinfo.ip_group_country as c set l.COUNTRY_CODE=c.country_code where c.ip_start <= INET_ATON(l.REMOTE_ADDR) order by c.ip_start asc
Edit: you're right, the max() in the original answer I provided could not work. The query above should, although it will likely be less efficient than something like the approach in the answer provided below.