How to execute a PDO query with a condition based on calculation - php

I work on a site where visitors can create an account, and to do so, they have to confirm their email adresses in the end of the process.
Before the account is created, the entered values such as email, pass etcetc are kept in a special table temporarily. That means a visitor has an hour to open their email and klick the link, or else that row will be deleted, to prevent "garbage" from bots and evil or simply overlazy people.
My idea was to let the users clean up the mess, and this is how: when a user klick the link sent to them, the row in the table with temporarily stored values is moved to the actual table for members, and another function will clean up rows that are "outdated", that is, who are inserted more than one hour ago.
This is my current code:
$stmt3 = $dbc->prepare('DELETE FROM temp_storage WHERE time() - time > 3600');
$stmt3->execute();
(time is the column with the time when the row was inserted is stored)
However this code is appareantly not working. I know I could do a workaround with SELECT FROM temp_storage and then check if the row is inserted too long ago, but I thought that, why would it be impossible to do it this way?
Now my question is, is it, or am I doing it the wrong way?

TIME() in MySQL does not give you the current time, it strips the "time portion" from a timestamp. You are looking for a different time function, probably UNIX_TIMESTAMP() if that is how you are storing your timestamps in the table.
Review MySQL date and time functions here: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

The function you're using to get the current time is not correct. It's not time() it's now().
If you check what $pdo->errorInfo() returns you'll see an error message.
In light of your comments about echo time() i get what you wanted to do but in order for that to work you should have written the following:
$stmt3 = $dbc->prepare('DELETE FROM temp_storage WHERE '.time().' - time > 3600');
$stmt3->execute();
By doing this you're using the return value of PHP's time() function to build a string which will then be sent to MySQL to be executed as a query.
You have to understand the difference between PHP-realm and SQL-realm code:
PHP only constructs text strings. It patches together various words an letters to compose a string. PHP doesn't even care what that string is for.
PHP can never look into that string and say "hey this is some good SQL queries right here".
What it can do is send the text you composed to an SQL server; the sql server will try and execute the text as if if were a corect SQL language statement.
If it hits errors it'll report them back to PHP if not it returns the results to PHP. In any case the SQL statements are strings and they get composed before being actually sent to the server.
"Interrupting" a string and concatenating another string to it such as "me"."&"."you" is just part of the process of building the string before sending it to the SQL server.

Try this:
$stmt3 = $dbc->prepare('DELETE FROM temp_storage WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(time) > 3600');

I assume the entry_dt column is a datetime column.
where CURRENT_TIMESTAMP - interval 1 HOUR > entry_dt
You should avoid naming columns the same as sql functions and keywords
ref
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html

Related

Get value of NOW() in MySQLi PHP

Assume that your webserver is detached from your database server. Their clocks might not be properly synchronized (down to milliseconds etc.).
You perform an insert such as "INSERT INTO sometable (VALUE,VALUE2,DATETIME) VALUES ("something","something else",NOW());"
Is there any way to get the timestamp generated by MySQL back into PHP? In the same fashion as when using $sqlObject->insert_id to get the AUTO_INCREMENT value of the last inserted row.
Edit:
I realize I could just use the insert_id and run a SELECT to get the timestamp, but this would require that I run another statement. I am wondering if it is possible in a single statement.
You can not get the direct value but you can do it via 2 ways.
1) Make a select query like select now() as cur_time. and take its value and use in the 2nd query. [ Its possible that you might get different time for this solution, SO i will suggest not to go with this solution and better you go with option 2. ]
2) After inserting the data get its ID and and make a query and get the inserted date time.
If your PHP and MYSQL server date time are same then you may use php's date function to get the current date / time .

PHP Foreach Loop Query

I've been tinkering with PHP lately (self-taught, no formal training), trying to understand how to grab data from a database and display the data somewhere. So far I have learned quite a bit, but now I am stumped.
I have a list of about 200 users in my local database in a table called site_members. The table has three fields: id, name, birth_date. Via PHP, I want to display all the users on a webpage, and have something like "Birthday soon!" be mentioned right after their name. Something like this:
John Smith (Birthday soon!)
I haven't written the code to do this, because I usually write pseudocode first before actually diving into the coding part. Here's the pseudocode:
Get the current date and time and convert it to Unix timestamp
Start foreach loop and go through list of users
Query the database table, get the birthdate of a user by their id, and store it in a variable named bdate.
Convert bdate to Unix timestamp
Subtract the current date from bdate, convert it into days remaining, and store it in a variable called remaining_days.
If the user's bdate is within 15 days (remaining_days is less than 15)
Display their name, followed by (Birthday soon!)
otherwise
Just display their name only
End if
End foreach loop
Here's my problem: With the above pseudocode once translated into actual code, there would be a database query made every time in that foreach loop. Some of the tutorials I consulted mentioned I should avoid that for efficiency reasons, and it makes sense. I ran Google searches to find something similar, but that didn't do much. I do not want anyone to write any actual code for me. I just want a better solution to the querying.
Thanks in advance!
I think your concept for the pseudo code is right, and you're understanding of doing multiple database queries is also right, you just tangled the two into giving you a wrong idea.
If you construct your select statement properly (that's basically what you'd be using to access the database), you actually pull the information for everyone out of the database and store it once in an array (or some other form of object). You can then start your foreach loop using the array as your value and perform the rest of your checks that way.
$date = date("m.d.y");
$people = ** insert your commands to grab the info from the DB **
foreach($people as $person) {
// do your comparison checks and echo's etc in here
}
Does this make sense?
There can be two solutions to your problem:-
1:
Instead of making query for every user, first get the data for all the users.
Traverse the data using foreach loop php
Do the processing and display the results.
2:
Store the user date_of_birth in proper mysql date datatype
Change your mysql query to use date function to get all the users who match your date difference criteria and just display those users.
It seems you failed to read up properly on the relationship between SQL and PHP. If you actually posted code, then you could have been easily unstumped because there are many ways to do the simple task from legacy tutorials to current PDO or even MVC within in 5mins or less.
I'm not going to write the code but you need to change HOW you think in your "pseudo code".
The problem with your pseudo code is because you believe that the DB is not smart and you are doing it as if it was only for storage.
The correct pattern for PHP is the following:
1) use the Date function to retrieve current day + 15. Get month and
day only.
2) you make a SQL query that retrieve all users who's
birth_date field's month and day are GREATER THAN (or equal) to
TODAY and who are less than or equal to today + 15 (day and month
only)
3) execute the query.
4) with the returned data set (if any)
you can choose two path depending situation and design
a) you can loop it with a simple FETCH which fetch each row retrieve
and display name and extra message.
or
b) iterates through the result set and store the display message
into a variable and then finally display it once the iteration is
done.
(option b is prefered because its more flexible since you can use this technique to out into a file instead of an echo)
THIS pseudo-code ensures that you are only retrieve the correct data set with the aid of the SQL system (or storage system).
In terms of overall process, aashnisshah is absolutely correct. First, you should retrieve all the records you need from your database then loop through each row to do your data comparisons and finally close the loop.
As for finding out if their birthday is close and if you want MySQL to do the hard work, you can build your query like that in PHP:
$query = "SELECT *, DATEDIFF(DATE_FORMAT(dob, '" . date('Y') . "-%m-%d'), CURDATE()) AS days_to_dob FROM Members";
The idea is to fetch an extra column called 'days_to_dob' containing the amount of days until that person's date of birth. Note that it will be negative if that date has passed for this year. With that extra column you can easily evaluate whether their dob is within 15 days.
If you don't want any php code, then here is my pseudocode:
Get date and time -> UTC stamp and store in $time_current
Get all from site_members and store in $data
for each entry in $data, store in $record
get birth_date from $record and convert to utc stamp and store in $birthday
print name from $record
if $birthday is close to $time_current then print "Birthday soon" end if
print new line
end for
That performs only one request to your database.

Getting SQLSTATE[HY000]: General error but query is performed anyway

I'm making a system to save users' IPs into a table along with the last time they posted something.
First, I check for an already-existent record of the user's IP with
SELECT lastpost FROM users WHERE ip = '$IP'
I then check whether the result of that query is empty, and if it is it means that the user's IP is not present and it should be recorded, so I proceed by doing this query:
INSERT INTO users (ip, lastpost) VALUES ('$IP', '$ctime')
Where $IP is the user's IP fetched using $_SERVER['REMOTE_ADDR'] and $ctime is a time string created using date("Y-m-j H:i:s").
Even though after performing the latter query the data is present into the table, I keep getting the error
SQLSTATE[HY000]: General error
Which is, by itself, not very helpful. I'd like to understand what I'm doing wrong here. Thanks in advance.
P.S.: I'll use this question to also ask how I could compare, using a query, the time I have calculated with PHP's date() with the time already present in the table (of type "datetime") to see if the one calculated with date() is greater than the one in the database by at least one minute.
I would first try to cast $ctime as a datetime CAST('$ctime' AS DATETIME) to see if that works.
For testing minute difference use TIMEDIFF().
SELECT IF(TIMEDIFF(CAST('$ctime' AS DATETIME), lastpost) > CAST('00:01:00.000000' AS DATETIME), /*do this*/, /*else do this*/)
This will return a time difference. If the time difference is negative, $ctime is earlier than lastpost. But you can get into pretty fine granularity here.
See if the above helps.
(Oh, I found all of this via Google and searching for mysql and datediff, timediff, or cast.)

Question regarding MySQL timestamp comparisons

I'll explain my goal first: I want the user to query the database, and return rows only if those rows have been updated since their last query. No sense returning data they'd already have. So I created a column called 'lastupdated', a timestamp type which autoupdates every time any content in the row is updated. This works fine. Now, I want to form the query correctly. The user will have their previous query's timestamp saved, and via php will use it to compare their previous query's time with the time each row has been updated. If the row was updated after their last query, the row should be returned.
I made something like this,
SELECT * FROM users WHERE '2011-02-26 01:50:30' <= lastupdated
but its obviously much too simple. I checked the MySQL manual and found this page MySQL Time/Date Page. I'm sure the answer is here, but I've read through it any nothing really makes sense. I have a timestamp in the same format used by the MySQL timestamp type, but I don't know how I will compare them. Thank you very much for your help.
That query is exactly how you'd do it. As long as a stringified date-time is in MySQL's preferred format (yyyy-mm-dd hh:mm:ss), then it will be internally converted into a datetime value, and the comparisons will go ahead.
You'd only need the date/time functions you found if you want to do something more complicated than simple "greater/less than/equal" type comparison, e.g. "any records that have a December timestamp".
As Marc said, your code should work. But you probably want to do this programmatically with a variable for the time instead of the literal.
If you don't have the date-time specified as a string, but rather as a timestamp (e.g. from using the php time() function), then you can use the following query:
$query = "SELECT * FROM users WHERE FROM_UNIXTIME(" . $timestamp . ") <= lastupdated";
The key is the FROM_UNIXTIME() MySQL function.

MySQL cache and date functions

I once read in a performance blog that it is better to use PHP's date functions to set dates in a MySQL query instead of using mysql date functions like curdate() because mysql can then cache the query or the result or something like that. Does anyone have any insight into this? Does it hold any water or is it baseless?
example:
$query = 'SELECT id FROM table WHERE publish_date = \''.date('Y-m-d').'\'';
vs
$query = 'SELECT id FROM table WHERE publish_date = CURDATE()';
Any function containing CURDATE() will not be cached. Source
Hardcoding the date should still be cached as far as I can tell. Though you might want to consider using the prepare functionality instead of splicing strings into your query (for sanity and security sake).
It's quite simple actually. The MySQL server does not see your PHP code so it'll receive one of these:
SELECT id FROM table WHERE publish_date = '2010-01-18';
SELECT id FROM table WHERE publish_date = CURDATE();
It will not read your intentions either. For MySQL, '2010-01-18' is a string and is deterministic: its value is always '2010-01-18'. However, CURDATE() is not deterministic: its value varies depending on the date when you run it. Thus the first one is cacheable and the second one is not.
I personally preffer first way, because it give clear head about server time (time zone), my mysql server happend to be 10h earlier when promissed :)
localtime in your PHP script will apply in SQL

Categories