My experience with PDO is somewhat limited and I've been stuck on this for a while. The issue is that when I run the code unprepared (because this has proven to be the only way I can debug PDO) I get the results I want. When I run it as a prepared statement, I get different results. See below:
Non-prepared code:
$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type='" . $p_sPostType . "'" .
" AND catID=" . $p_nCatID .
" AND serviceID=" . $p_nServiceID .
" AND serviceIdentity=" . $p_nServiceUserID .
" AND timestamp BETWEEN DATE_SUB(NOW(), $intervalString . ") AND NOW()";
$theQuery = $DB->Query($SQL);
echo "\r\n\r\nQuery:";
print_r($theQuery);
echo "\r\nResult:";
$result = $theQuery->fetch(PDO::FETCH_ASSOC);
print_r($result);
Non-prepared results:
Query:PDOStatement Object
(
[queryString] => SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type='pudding' AND catID=13 AND serviceID=1 AND serviceIdentity=3324848959 AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 DAY) AND NOW()
)
Result:Array
(
[counted] => 15
)
And now the prepared code:
$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type=:postType" .
" AND catID=:catID" .
" AND serviceID=:serviceID" .
" AND serviceIdentity=:serviceIdentity" .
" AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()";
// Execute the statement
try {
$stmt = $DB->prepare($SQL);
$stmt->bindParam(':postType', $p_sPostType, PDO::PARAM_STR, 30);
$stmt->bindParam(':catID', $p_nCatID, PDO::PARAM_INT);
$stmt->bindParam(':serviceID', $p_nServiceID, PDO::PARAM_INT);
$stmt->bindParam(':serviceIdentity', $p_nServiceUserID, PDO::PARAM_INT);
$stmt->bindParam(':interval', $intervalString, PDO::PARAM_STR, 30);
$result = $stmt->execute();
} catch(PDOException $e) {
mm_die($e->getMessage());
}
echo "\r\n\$SQL = $SQL";
// echo "\r\n\$p_nLimitValue = $p_nLimitValue\r\n";
echo "\r\nRow Count: " .$stmt->rowCount() . "\r\n";
And the prepared results:
$SQL = SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type=:postType AND siloID=:siloID AND serviceID=:serviceID AND serviceIdentity=:serviceIdentity AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()
Row Count: 0
Note "Row Count" being zero in the prepared statement. I've stared at this for more time than I care to admit. Can anyone see why one returns results and the other does not? Thanks!
The problem is that the second argument to DATE_SUB() must be an interval, but you're providing a string. The string "INTERVAL 1 HOUR is not automatically converted to the corresponding interval. You can only use a placeholder for the numeric part of the INTERVAL expression, not for the keywords.
Take the time units out of the associative array, and represent everything as hours.
$interval = array("hourly" => 1, "daily" => 24, "weekly" => 7*24, "monthly" => 30*24, "yearly" => 365*24);
Then you can do:
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type=:postType" .
" AND catID=:catID" .
" AND serviceID=:serviceID" .
" AND serviceIdentity=:serviceIdentity" .
" AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL :interval HOUR) AND NOW()";
and
$stmt->bindParam(':interval', $interval[$p_sLimitType], PDO::PARAM_INT);
Placeholder could not represent an arbitrary part of query, but complete string or numeric literal only.
Thus you cannot bind a part of interval.
As long as you have your intervals whitelisted in the code as shown above, you may and you have to stick with old approach for interval.
It's not possible for a select count(*) to return 0 rows. It will always return AT LEAST one row, containing the count of the matched/found rows. Getting 0 rows means your query failed outright, and didn't return a result set, period.
Did you enable exceptions in PDO? By default it does "return false" for failure, and won't throw exceptions unless you explicitly enable them. if they're not enabled, then your try/catch is useless.
Related
I want to get records which exists between given start time and end time, start time and end time will be given by user, i'm using the following query for that :
$this->db->where('TIME(start) BETWEEN "' . date('H:i:s', strtotime($start_time)) . '" and "' . date('H:i:s', strtotime($end_time)) . '"');
$this->db->where('TIME(end) BETWEEN "' . date('H:i:s', strtotime($start_time)) . '" and "' . date('H:i:s', strtotime($end_time)) . '"');
$results = $this->db->get('class')->result();
start and end time column is datetime field:
eg. in database
start time : 2020-06-08 06:15:00
end time : 2020-06-08 09:15:00
#user3653474 You can use TIMEDIFF to check if the time provided is between the values of two columns or not. See if the query below does your deed.
$start = date('H:i:s', strtotime($start_time)); // get time from date in desired format
$end = date('H:i:s', strtotime($end_time));
// where $start is between column {start} and {end} AND $end is between column {start} and {end}
$sql = "SELECT * FROM class
WHERE (TIMEDIFF('$start', TIME(start)) >=0 AND TIMEDIFF('$start', TIME(end)) <= 0)
AND (TIMEDIFF('$end', TIME(start)) >=0 AND TIMEDIFF('$end', TIME(end)) <= 0)";
$results = $this->db->query($sql)->result();
I can't figure out why this is returning "NULL". I've hardcoded the date/time string in to the $scheduledDates variable. In practice that's user input. Everything works fine when I don't prepare the query.
$scheduledDate = "2015-09-01 00:00:00";
$queryString = "SELECT * FROM schedules WHERE event_start > %s AND event_start < %s + INTERVAL 1 DAY";
$scheduled_blocks = $wpdb->get_results( $wpdb->prepare( $queryString, $scheduledDate ) );
The code below works fine, whether I hard code the date/time or not...
$scheduledDate = $_POST['scheduledDate'];
$scheduled_blocks = $wpdb->get_results('SELECT * FROM schedules WHERE event_start > "' . $scheduledDate . '" AND event_start < "' . $scheduledDate . '" + INTERVAL 1 DAY');
Use $wpdb->print_error() to see what errors you get. By the look of your code though, I think the number of placeholders have to be the same as the amount of values you're supplying to the prepare method. Alter your call to this:
$wpdb->prepare($queryString, $scheduledDate, $scheduledDate)
EDIT: I have changed the title of this question, as I am really getting no results, rather than a NULL result.
I am currently working with PDO on a project of mine, and am having a problem I can't seem to get past. A standard MySQL query (executed in phpMyAdmin) returns the results I want, but when I use PDO, it produces no results. I notice if I don't bind the values the query works, so would like to see what I am missing...
Below is the MySQL query:
SELECT AVG( demand_value )
FROM Demand
WHERE DATE = STR_TO_DATE( '06 Nov 2013', '%d %M %Y' )
AND TIME
BETWEEN STR_TO_DATE( '07:00:00', '%k:%i:%s' )
AND STR_TO_DATE( '10:00:00', '%k:%i:%s' );
Below is the PHP code (using PDO) trying to get the same results:
//Assume $date = '06 Nov 2013', $time_from = '07:00:00', $time_to = '10:00:00'
//Those values are parsed in from a custom function
$ave_btwn_times_stmt =
$connection->get_conn()->prepare("SELECT AVG( demand_value )
FROM Demand
WHERE date = STR_TO_DATE(:date, '%d %M %Y')
AND time BETWEEN STR_TO_DATE(:time_from, '%k:%i:%s')
AND STR_TO_DATE(:time_to, '%k:%i:%s')");
$ave_btwn_times_stmt->bindValue(':date', $date, PDO::PARAM_STR);
$ave_btwn_times_stmt->bindValue(':time_from', $time_from, PDO::PARAM_STR);
$ave_btwn_times_stmt->bindValue(':time_to', $time_to, PDO::PARAM_STR);
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . ": " . $ave_date_times[0][0] . ".";
If I implement the code below, I get the results I am after:
$ave_btwn_times_stmt =
$connection->get_conn()->prepare("SELECT AVG( demand_value )
FROM Demand
WHERE date = STR_TO_DATE('$date', '%d %M %Y')
AND time BETWEEN STR_TO_DATE('$from_time', '%k:%i:%s')
AND STR_TO_DATE('$to_time', '%k:%i:%s')");
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . ": " . $ave_date_times[0][0] . ".";
If anyone can help me out on this one I would be very grateful.
Your MySQL query threw this error
I'm sorry I'm afraid you can't do that.SQLSTATE[42S22]: Column not
found: 1054 Unknown column 'TIME' in 'where clause'
I have refactored your query removing BETWEEEN and replacing it with 2 comparison tests.
SELECT AVG( demand_value ) FROM Demand
WHERE DATE = STR_TO_DATE( '06 Nov 2013', '%d %M %Y' )
AND `time_from` >= STR_TO_DATE( `time_from`,'%k:%i:%s' )
AND `time_to` >= STR_TO_DATE(`time_to`,'%k:%i:%s' )
Using unnamed placeholders the following code provides required output.
$dbh = new PDO("mysql:host=$host;dbname=$database", $username, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$ave_btwn_times_stmt= $dbh->prepare("SELECT AVG( demand_value )
FROM Demand WHERE DATE = STR_TO_DATE( ?, '%d %M %Y' )
AND `time_from` >= STR_TO_DATE( ?,'%k:%i:%s' )
AND `time_to` >= STR_TO_DATE(?,'%k:%i:%s' )");
// Assign parameters
$ave_btwn_times_stmt->bindParam(1,$date);
$ave_btwn_times_stmt->bindParam(2,$time_from);
$ave_btwn_times_stmt->bindParam(3,$time_to);
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . " = " . $ave_date_times[0][0] . ".";
}
catch(PDOException $e) {
echo "I'm sorry I'm afraid you can't do that.". $e->getMessage() ;// Remove or modify after testing
file_put_contents('PDOErrors.txt',date('[Y-m-d H:i:s]').", mapSelect.php, ". $e->getMessage()."\r\n", FILE_APPEND);
}
//Close the connection
$dbh = null;
After testing you should remove the error echo, or Try/Catch block and relying on PHP error handling instead
I want to count a record by current date from different tables and return as one row with different column in the new table. The code will update a record every three hours and insert new record if current date changes. I've current date and time data (2013-05-20 14:12:12) in "created_at" column. Here my current code:
require_once('./db_connect.php');
$dbcon = new db;
//test to see if a specific field value is already in the DB
public function in_table($table,$where) {
$query = 'SELECT * FROM ' . $table . ' WHERE ' . $where;
$result = mysqli_query($this->dbh,$query);
$this->error_test('in_table',$query);
return mysqli_num_rows($result) > 0;
}
//running in background
while (true) {
$select= "SELECT (SELECT CURDATE()) AS time," .
"(SELECT COUNT(tweet_id) FROM tweets WHERE created_at= 'CURDATE() %') AS total_count," .
"(SELECT COUNT(fid) FROM fun WHERE ftime= 'CURDATE() %') AS f_count," .
"(SELECT COUNT(sid) FROM sad WHERE stime= 'CURDATE() %') AS s_count";
$results = mysqli_query( $dbcon, $select );
while($row = mysqli_fetch_assoc($result)) {
$time = $row['time'];
$total = $row['total_count'];
$fcount = $row['f_count'];
$scount = $row['s_count'];
$field_values = 'time = "' . $time . '", ' . 'total_count = ' . $total . ', ' . 'fun_count = ' . $fcount . ', ' . 'sad_count = ' . $scount;
if ($dbcon->in_table('count','time= "' . $time . '"')) {
$update = "UPDATE count SET $field_values WHEN time= '$time'";
mysqli_query( $dbcon, $update );
}
else {
$insert = "INSERT INTO count SET $field_values";
mysqli_query( $dbcon, $insert );
}
}
//update record every 3 hour
sleep(10800);
}
With this code I can't get a count record. The result return | 2013-05-18 | 0 | 0 | 0 |. How can I correct this?
I not familiar with PHP, but you can retrieve the count of all records dated any time today using:
SELECT COUNT(tweet_id)
FROM tweets
WHERE created_at >= curDate()
AND created_at < date_add(curDate(), interval 1 day)
It is equivalent to saying
..
WHERE created_at >= (today at midnight *incusive*)
AND created_at < (tomorrow at midnight *exclusive*)
Update:
The advantage of this method is it is index friendly. While using WHERE DATE(Column) = currDate() works, it can prevent the database from using indexes on that column, making the query slower.
Replace the parts where you have this:
WHERE created_at= 'CURDATE() %'
with this:
WHERE DATE(created_at) = CURDATE()
Your existing WHERE clause is comparing created_at to the string constant CURDATE() %, and they'll never match.
You are comparing against created_at= 'CURDATE() %', which is looking for that exact string, not for the result of a function. If the field created_at is a date, it will never match.
And, you are doing that for all counts.
Okay, so I do understand that if I am running the strtotime function on a day that is the 29th or higher I will incur the February bug and get a result for March instead.
Also I get that if I set the date to Feb 1st, I can avoid the issue.
But here is the problem. I am rolling through the last 12 months of records to generate sales/billing numbers for tracking. How do I ask for all records in February when my loop looks like this?
setlocale( LC_MONETARY, 'en_US' );
$i = 0;
while( $i <= 11 ) {
$select = "SELECT * FROM `my_table` " .
"WHERE YEAR( billing_date ) = '" . date( 'Y', strtotime( -$i . ' month' )) . "' " .
" AND MONTH( billing_date ) = '" . date( 'm', strtotime( -$i . ' month' )) . "'";
$result = mysql_query( $select );
$num_rows = mysql_num_rows( $result );
$sales = 16 * $num_rows;
echo "<p align='center'>";
echo "Sales for " . date( 'M, Y', strtotime( '-' . $i . ' month' ) ) .
" " . money_format( '%i', $sales );
echo "</p>";
$i++;
}
How do I avoid the February bug? Would it be an if statement? What would that look like?
Here is a pure SQL solution (if I correctly understand the scenario):
SELECT
YEAR(billing_date) AS billing_year,
MONTH(billing_date) AS billing_month,
16 * COUNT(*) AS sales /* Why x16?!!! */
FROM my_table
GROUP BY YEAR(billing_date), MONTH(billing_date)
This will calculate the sales directly in SQL, so in PHP you just need a display loop.
How do I ask for all records in February...
SELECT * FROM table WHERE MONTH(billing_date) = 2
February bug? It's not a bug. Normalizing dates is a (useful) feature! :-)
If you want the last day in February, ask for the 0th of March.
I don't know the "february bug", but you could try to use MySQL to subtract 1 month. Check
SELECT DATE_SUB(billing_date, INTERVAL 1 MONTH) FROM my_table
=> Manual