MySql - Insert with IF and JOIN - php

I'm trying to add bonus points for users every 2 days. But I'm not sure how to join the users table and get the user_bonus value which indicates the last time a user received a bonus. Can this be done in one query?
Insert query:
INSERT INTO points_plus (user_id, points) VALUES (?,?);
This would be the 2 day interval which needs to be checked:
user_bonus <= NOW() - INTERVAL 2 DAY
users table(part of it)
+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| weekly_mail | datetime | YES | | NULL | |
| user_bonus | datetime | YES | | NULL | |
| join_date | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+
points_plus table
+----+---------+--------+---------------------+
| id | user_id | points | date |
+----+---------+--------+---------------------+
| 18 | 13 | 300 | 2015-10-07 12:06:39 |
| 43 | 13 | 99 | 2015-10-18 14:16:50 |
| 45 | 13 | 103 | 2015-10-19 05:49:49 |
+----+---------+--------+---------------------+

You can try this.
INSERT INTO points_plus (user_id, points)
SELECT id, $points
FROM users
WHERE user_bonus <= NOW() - INTERVAL 2 DAY

Related

Splitting date and time then transfer them to their new respective columns

I have a website which have a date and time combined in the start of meeting and end of meeting.
Meeting_start
2019-04-26 11:39:15
I already created new columns for date and time. How can I split the existing date and time then transfer them to their new respective columns?
Ps. I'm looking for SQL codes.
You can simply use the date and time functions available in mySQL
set #d='2019-04-26 11:39:15';
select date(#d),time(#d);
Will output
+------------+----------+
| date(#d) | time(#d) |
+------------+----------+
| 2019-04-26 | 11:39:15 |
+------------+----------+
Assuming that the existing column is called meeting_start and a table called TABLE you could then do
update `TABLE` set `date_col`=date( `meeting_start` ), `time_col`=time( `meeting_start` );
A simple table schema to illustrate how to update the new fields
mysql> describe `so_datetime_fudge`;
+--------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| meeting_start | datetime | YES | | NULL | |
| meeting_start_date | date | YES | | NULL | |
| meeting_start_time | time | YES | | NULL | |
+--------------------+------------------+------+-----+---------+----------------+
A snapshot of the table with 3 rows of dummy data
mysql> select * from so_datetime_fudge;
+----+-----------------------+---------------------+--------------------+--------------------+
| id | name | meeting_start | meeting_start_date | meeting_start_time |
+----+-----------------------+---------------------+--------------------+--------------------+
| 1 | The First of Too Many | 2019-05-27 16:48:30 | NULL | NULL |
| 2 | Holy Cow Batman | 2019-05-27 16:54:56 | NULL | NULL |
| 3 | The End is Nigh | 2019-05-27 16:55:22 | NULL | NULL |
+----+-----------------------+---------------------+--------------------+--------------------+
Then running this query:
update `so_datetime_fudge` set `meeting_start_date`=date( `meeting_start` ), `meeting_start_time`=time( `meeting_start` );
Running the same SELECT query after the update
mysql> select * from so_datetime_fudge;
+----+-----------------------+---------------------+--------------------+--------------------+
| id | name | meeting_start | meeting_start_date | meeting_start_time |
+----+-----------------------+---------------------+--------------------+--------------------+
| 1 | The First of Too Many | 2019-05-27 16:48:30 | 2019-05-27 | 16:48:30 |
| 2 | Holy Cow Batman | 2019-05-27 16:54:56 | 2019-05-27 | 16:54:56 |
| 3 | The End is Nigh | 2019-05-27 16:55:22 | 2019-05-27 | 16:55:22 |
+----+-----------------------+---------------------+--------------------+--------------------+
But, if you create a VIEW of the table you can structure this how you like and the underlying data remains unchanged
CREATE VIEW `vw_datetime_fudge` AS select
`id`,
`name`,
date( `meeting_start` ) as `meeting_start_date`,
time( `meeting_start` ) as `meeting_start_time`
from `so_datetime_fudge`;
mysql> select * from `vw_datetime_fudge`;
+----+-----------------------+--------------------+--------------------+
| id | name | meeting_start_date | meeting_start_time |
+----+-----------------------+--------------------+--------------------+
| 1 | The First of Too Many | 2019-05-27 | 16:48:30 |
| 2 | Holy Cow Batman | 2019-05-27 | 16:54:56 |
| 3 | The End is Nigh | 2019-05-27 | 16:55:22 |
+----+-----------------------+--------------------+--------------------+
<?php
$subject = 'Meeting_start 2019-04-26 11:39:15';
$pattern = '/(\d\d\d\d\-\d\d\-\d\d).*(\d\d\:\d\d\:\d\d)/U';
preg_match_all($pattern,$subject,$matches);
$date = $matches[1][0];
$time = $matches[2][0];
echo $date . '<br>' . PHP_EOL;
echo $time . '<br>' . PHP_EOL;
gives result
2019-04-26<br>
11:39:15<br>
You can use DateTime::format method
$date = new DateTime('2019-04-26 11:39:15');
$timeString = $date->format('H:i:s');
$dateString = $date->format('Y-m-d');

Need to speed up MySQL query, it's at 3+ seconds with only 4,000 records, has 3 joins

Here's the situation:
I have a few tables (described below) that track residents in an apartment building, their unit number, and when they were "seen" last (we have lots of older people with health issues, so it's important to check on them every 2 days or so; sometimes they die here and that's how we know how to check on them).
The contstraints for a "check" are that they have to have been seen in the past 48 hours; if not, the query should pull their record up. Here are the table definitions I'm using:
The "people" table, where resident info is stored:
MariaDB [olin2]> describe people;
+-------------+-------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| fname | varchar(32) | NO | MUL | NULL | |
| lname | varchar(32) | NO | | NULL | |
| dob | date | YES | | NULL | |
| license_no | varchar(24) | NO | | NULL | |
| date_added | timestamp | NO | | CURRENT_TIMESTAMP | |
| status | varchar(8) | NO | | Allow | |
| license_exp | date | YES | | NULL | |
+-------------+-------------+------+-----+-------------------+----------------+
The "units" table, where unit numbers are stored (people switch units so I didn't want them in the "people" table):
MariaDB [olin2]> describe units;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| number | varchar(3) | NO | MUL | NULL | |
| resident | int(11) | NO | | NULL | |
| type | varchar(16) | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+
and the "wellness" table, where the "checks" are stored (the resident's id number, when they were seen and by whom, etc.):
MariaDB [olin2]> describe wellness;
+--------------+-------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+-------------------+----------------+
| wellness_id | int(11) | NO | PRI | NULL | auto_increment |
| people_id | int(11) | NO | | NULL | |
| time_checked | timestamp | NO | | CURRENT_TIMESTAMP | |
| check_type | varchar(1) | NO | | NULL | |
| username | varchar(16) | NO | | NULL | |
| return_date | timestamp | YES | | NULL | |
+--------------+-------------+------+-----+-------------------+----------------+
The "return_date" field in the "wellness" table is for when a resident leaves for more than 2 days, then they won't be included in the results when they are displayed (they actually will be included in the query results, but I use PHP to filter those out).
Here's the query I have been using... It worked well for a few weeks, but as there were more and more records added it's been getting noticably slower (right now its 3.5 seconds to return the results):
select p.id, w.time_checked, w.username, w.return_date
from people p
left join units u on p.id = u.resident
left join wellness w on p.id = w.people_id
left join wellness as w2 on w.people_id = w2.people_id
and w.time_checked < w2.time_checked
where w2.people_id is null
and w.time_checked < (now() - interval 48 hour)
order by u.number
I know my problem is the joins, but I don't know how to get the results I need without them and/or how to optimize this query to speed it up... Here's a sample of results (if needed):
+----+---------------------+----------+---------------------+
| id | time_checked | username | return_date |
+----+---------------------+----------+---------------------+
| 8 | 2013-12-01 11:00:13 | tluce | 0000-00-00 00:00:00 |
+----+---------------------+----------+---------------------+
1 row in set (3.44 sec)
So, in this result set, resident 8 hasn't been seen for 3 days... the result is correct but the 3.44 sec isn't acceptable for my users to have to wait.
Any ideas on how I can improve this?
EDIT (More Info):
I realize updating the wellness entry for each person would be easier and quicker to access; however I like to have these data on-hand because I generate graphs from it to show A) when we most often see a particular resident and B) which staff members check on people the most often (aka - who's doing their job and who's not)
I DO use indexes, and here's the results of an EXPLAIN on my query:
+------+-------------+-------+--------+---------------+---------+---------+----------------- -+------+--------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------+---------+---------+----------------- -+------+--------------------------------------------------------------------+
| 1 | SIMPLE | u | ALL | NULL | NULL | NULL | NULL | 107 | Using temporary; Using filesort |
| 1 | SIMPLE | p | eq_ref | PRIMARY,idx | PRIMARY | 4 | olin2.u.resident | 1 | Using where |
| 1 | SIMPLE | w | ALL | NULL | NULL | NULL | NULL | 7074 | Using where; Using join buffer (flat, BNL join) |
| 1 | SIMPLE | w2 | ALL | NULL | NULL | NULL | NULL | 7074 | Using where; Not exists; Using join buffer (incremental, BNL join) |
+------+-------------+-------+--------+---------------+---------+---------+----------------- -+------+--------------------------------------------------------------------+
The indexes in the people table: id, fname, lname, license_no
The wellness table: wellness_id
The units table: id, number
Under possible keys each field that has NULL means that index was not used. so you can add index to the fields you use to join. for example units.residents. you can do the same thing to wellness.people_id –
You have too many joins for this result. The only reason it seems like you have the "people" table in there is to attach to "units" but you have flags in both "units" and "wellness" that you can join off of. I also don't believe the second join of the wellness table is necessary.
This is all you should need:
Select W.people_id, w.time_checked, w.username, w.return_date
FROM units u
left join wellness w on u.resident = w.people_id
where w.time_checked < (now() - interval 48 hour)
order by u.number
You may want to also look at indexing your people_id fields in tables where it is not the primary key. That will help speed up any queries you try and run off these tables.

How to combine this two MySQL query using Group By

I have this table that i use to query by grouping via station_id.
+------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+-------+
| id | varchar(50) | NO | PRI | NULL | |
| station_id | tinyint(3) | NO | | NULL | |
| game_type_id | smallint(1) | NO | MUL | NULL | |
| price | decimal(10,2) | YES | | 0.00 | |
| date_created | datetime | YES | MUL | NULL | |
| bet_no1 | tinyint(2) | YES | | 0 | |
| bet_no2 | tinyint(2) | YES | | 0 | |
+------------------+---------------+------+-----+---------+-------+
Here is the query i use to display it on a table using GROUP BY station_id
SELECT station_id,
COUNT(*) as bet_counts,
FORMAT(SUM(price),2) as gross
FROM bets
WHERE bet_void=0
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id
The query will give me.
+------------+------------+-------+
| station_id | bet_counts | gross |
+------------+------------+-------+
| 1 | 14 | 16.00 |
| 2 | 5 | 5.00 |
| 7 | 11 | 11.00 |
+------------+------------+-------+
But i also have another query that counts each specific bets( game_type_id ) from each station_id. I usually query this inside the a looping statement.
SELECT COUNT(*) as count
FROM bets
WHERE game_type_id = 1
AND station_id = {station_id from first query}
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
My question is, how can i make this in one query and still use the GROUP BY station_id and also get the count of bets on each game_type_id? Something like this result.
+------------+------------+-------+-------------------------+-------------------------+
| station_id | bet_counts | gross | count_of_game_type_id_1 | count_of_game_type_id_2 |
+------------+------------+-------+-------------------------+-------------------------+
| 1 | 14 | 16.00 | 10 | 4 |
| 2 | 5 | 5.00 | 3 | 2 |
| 7 | 11 | 11.00 | 11 | 0 |
+------------+------------+-------+-------------------------+-------------------------+
You can do this by joining the results together. However, the logic in the two queries is very similar, so you can combine them into a single aggregation query:
SELECT station_id,sum(case when bet_void = 0 then 1 else 0 end) as bet_counts,
FORMAT(SUM(case when bet_void = 0 then price else 0 end),2) as gross,
sum(case when game_type_id = 1 then 1 else 0 end) as count
FROM bets b
where date_created >= '2013-02-12 00:00:00' AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id

strange behavior of query

I have the table below:
+-------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+-------------------+----------------+
| category_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(64) | NO | | NULL | |
| description | text | NO | | NULL | |
| created_on | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_on | timestamp | YES | | NULL | |
+-------------+------------------+------+-----+-------------------+----------------+
I needed to delete some records according to the date created and executed a set of queries:
mysql> select created_on from categories limit 10;
+---------------------+
| created_on |
+---------------------+
| 2013-01-14 09:26:21 |
| 2012-08-15 11:18:38 |
| 2012-09-06 06:44:46 |
| 2012-09-06 23:27:14 |
then
mysql> select date('2013-01-14 09:26:21');
+-----------------------------+
| date('2013-01-14 09:26:21') |
+-----------------------------+
| 2013-01-14 |
+-----------------------------+
and finally I got:
mysql> select DATE('created_on') from categories limit 10;
+--------------------+
| DATE('created_on') |
+--------------------+
| NULL |
| NULL |
| NULL |
| NULL |
As you can see the date wasn't converted correctly. In about 10 minutes is worked ok:
mysql> select date(created_on) from categories limit 10;
+------------------+
| date(created_on) |
+------------------+
| 2013-01-14 |
| 2012-08-15 |
| 2012-09-06 |
| 2012-09-06 |
I'm sure that the data were intact while I was running the queries.
So my question:
Can anybody explain why date function showed different results on the same input?
You have to use::
select DATE(created_on) from categories limit 10;
By using the inverted comma, you are asking the database to treat 'created_on' as parameter, and since it cannot be converted to date type, you are getting null
select DATE('created_on') from categories limit 10;
String versus column:
select DATE(created_on) from categories limit 10;

SQL Query using MySQL sample employee database

I have a problem making an advanced (for me) query.
I am trying to get the first name, last name, title, department name, and first and last name of the manager. I am using this SQL in PHP if for some reason you needed to know. I limit to 10 because the database is enormous. I need only the entry for the employees latest data. They have more than one entry because of job promotions, etc.
I included everything I think you would need to understand my problem. Thanks if you can help.
mysql>
SELECT DISTINCT employees.first_name, employees.last_name,
titles.title, departments.dept_name, z.first, z.last
FROM employees, dept_emp, departments, titles,
(
SELECT employees.first_name AS first, employees.last_name AS last
FROM employees, dept_emp, dept_manager
WHERE
employees.emp_no = dept_emp.emp_no
AND dept_manager.emp_no = dept_emp.emp_no
) AS z
WHERE
employees.emp_no = dept_emp.emp_no
AND dept_emp.dept_no = departments.dept_no
AND titles.emp_no = employees.emp_no
LIMIT 10;
+------------+-----------+-----------------+-------------+-----------+--------------+
| first_name | last_name | title | dept_name | first | last |
+------------+-----------+-----------------+-------------+-----------+--------------+
| Georgi | Facello | Senior Engineer | Development | Margareta | Markovitch |
| Georgi | Facello | Senior Engineer | Development | Vishwani | Minakawa |
| Georgi | Facello | Senior Engineer | Development | Ebru | Alpin |
| Georgi | Facello | Senior Engineer | Development | Isamu | Legleitner |
| Georgi | Facello | Senior Engineer | Development | Shirish | Ossenbruggen |
| Georgi | Facello | Senior Engineer | Development | Karsten | Sigstam |
| Georgi | Facello | Senior Engineer | Development | Krassimir | Wegerle |
| Georgi | Facello | Senior Engineer | Development | Rosine | Cools |
| Georgi | Facello | Senior Engineer | Development | Shem | Kieras |
| Georgi | Facello | Senior Engineer | Development | Oscar | Ghazalie |
+------------+-----------+-----------------+-------------+-----------+--------------+
10 rows in set (0.00 sec)
The database is the employees database from mysql.com:
http://dev.mysql.com/doc/index-other.html
mysql> desc departments;
+-----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| dept_no | char(4) | NO | PRI | NULL | |
| dept_name | varchar(40) | NO | UNI | NULL | |
+-----------+-------------+------+-----+---------+-------+
2 rows in set (0.22 sec)
mysql> desc dept_emp;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| dept_no | char(4) | NO | PRI | NULL | |
| from_date | date | NO | | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.20 sec)
mysql> desc dept_manager;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| dept_no | char(4) | NO | PRI | NULL | |
| emp_no | int(11) | NO | PRI | NULL | |
| from_date | date | NO | | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.21 sec)
mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.32 sec)
mysql> desc salaries;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| salary | int(11) | NO | | NULL | |
| from_date | date | NO | PRI | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.34 sec)
mysql> desc titles;
+-----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| title | varchar(50) | NO | PRI | NULL | |
| from_date | date | NO | PRI | NULL | |
| to_date | date | YES | | NULL | |
+-----------+-------------+------+-----+---------+-------+
4 rows in set (0.60 sec)
I am trying to get the first name, last name, title, department name, and first and last name of the manager
Using the theory (but it's probably not optimized if you have large tables), do it step by step. Assuming you have a $date PHP variable that contains a date (like the result from date('Y-m-d 00:00:00')), or otherwise you may use MySQL's date.
1/ get employees' identity:
SELECT
e.first_name,
e.last_name
FROM
employees AS e
WHERE
1
2/ add his/her current title
SELECT
e.first_name,
e.last_name,
t.title
FROM
employees AS e,
titles AS t
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
3/ get his/her current department (from dept_emp) and retrieve the department's name
SELECT
e.first_name,
e.last_name, t.title,
d.dept_name
FROM
employees AS e,
titles AS t,
dept_emp AS de,
departments as d
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
AND
e.emp_no=de.emp_no AND de.from_date<='$date' AND de.to_date>='$date'
AND
d.dept_no=de.dept_no
4/ get the department's manager (from dept_manager) and retrieve his/her name (from employees)
SELECT
e.first_name AS empFN,
e.last_name AS empLN,
t.title AS empT,
d.dept_name AS dept,
em.first_name AS manFN,
em.last_name AS manLN
FROM
employees AS e,
titles AS t,
dept_emp AS de,
departments as d,
dept_manager AS dm,
employees AS em
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
AND
e.emp_no=de.emp_no AND de.from_date<='$date' AND de.to_date>='$date'
AND
d.dept_no=de.dept_no
AND
em.emp_no=dm.emp_no AND dm.from_date<='$date' AND dm.to_date>='$date'
AND
de.dept_no=dm.dept_no
There you can append a LIMIT (and an ORDER BY probably, you don't know which results will be picked in case of a limit), and/or add in the WHERE an emp_no for the employee(s) you want to have info about. Not tested though, but it's mainly to let you understand how to build the request.
This is theory, and this request is probably be as heavy to process as the one you wrote. Since you use SQL with PHP, it may be better to retrieve only the primary keys you're interested in, then send short requests to retrieve the information you want. Something like that I mean (using fake functions but the spirit's here):
$dateString="from_date<='$date' AND to_date>='$date'";
$qe=query("SELECT emp_no, first_name, last_name, dept_no FROM employees, dept_emp WHERE dept_emp.emp_no=employees.emp_no LIMIT 10 ORDER BY emp_no DESC");
while($r=fetch($qe)) {
$qt=fetch(query("SELECT title FROM titles WHERE emp_no=".$qe['emp_no']." AND ".$dateString));
$qd=fetch(query("SELECT dept_name FROM departments WHERE dept_no=".$qe['dept_no']." AND ".$dateString));
$qm=fetch(query("SELECT first_name, last_name FROM employees AS e, dept_manager AS dm WHERE dept_no=".$qe['dept_no']." AND e.emp_no=dm.emp_no AND ".$dateString));
//echo/process here
}
Hope this helps :)

Categories