PDO: Different results with SQLite BETWEEN - php

I have a SQLite table with dates in unix timestamp format, which I query via PHP PDO. I'd like to mark the rows according to a date range, in my case whether the date falls in the winter semester or the summer semester of the year.
The start and end dates are arbitrary, but in my case I picked
start as March 1st (0301) and end as September 14th (0914), so I could easily check with BETWEEN.
When I run this query in PHP, like so, it returns an incorrect result ("WS" for all rows):
$statement = $db->prepare('SELECT CASE WHEN
(CAST(strftime("%m%d", class_date, "unixepoch") AS DECIMAL)
BETWEEN :start AND :end)
THEN "SS"
ELSE "WS" END
AS semester FROM dates');
$statement->execute(array(
':start' => 301,
':end' => 915
));
But when I embed the start and end dates as numbers, instead of binding them, it works correctly (some rows "WS", some "SS"):
$statement = $db->prepare('SELECT CASE WHEN
(CAST(strftime("%m%d", class_date, "unixepoch") AS DECIMAL)
BETWEEN 301 AND 915)
THEN "SS"
ELSE "WS" END
AS semester FROM dates');
$statement->execute();
What is happening here and how can I fix it? Any help is appreciated.

Documentation of PDOStatement::execute says that it accepts
An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.
This means that even if you put in 301 it will be treated as "301".
Instead you should use PDOStatement::bindValue. Like this:
$statement->bindValue(':start', 301, PDO::PARAM_INT);
$statement->bindValue(':end', 915, PDO::PARAM_INT);
$statement->execute();

Related

Bind the name of a parameter

In Doctrine, is it possible to bind the name of a parameter
(contrary to binding the value of a parameter)?
Why I need it
There is a table having 7 boolean columns, one for each day of the week: monday, tuesday, etc. (these correspond to the structure of the calendar entity defined by GTFS, https://gtfs.org/schedule/reference/#calendartxt).
Given a day of the week (say, monday), I want to get all rows, which are available on Mondays, i.e.:
$statement = $this
->getEntityManager()
->getConnection()
->prepare('
SELECT id
FROM calendar
WHERE monday = 1
');
Generally, I want to be able to supply the name of a day in that query, which I can do simply by:
->prepare("
SELECT id
FROM calendar
WHERE $dayName = 1
");
I wonder whether it's possible to use the parameter binding for the names of parameters, i.e. something like
$statement = $this
->getEntityManager()
->getConnection()
->prepare('
SELECT id
FROM calendar
WHERE :dayName = 1
');
$statement->bindValue('dayName', $dayName);
which does not work, see below.
What I tried
#1
WHERE :dayName = 1
which translates to the following SQL query:
SELECT calendar.id
FROM calendar
WHERE 'monday' = 1
and, because that condition is never true, returns an empty set, [].
#2
WHERE `:dayName` = 1
SELECT calendar.id
FROM calendar
WHERE `'monday'` = 1
Column not found: 1054 Unknown column ''monday'' in 'where clause'
#3
WHERE ":dayName" = 1
# no query
Invalid parameter number: number of bound variables does not match number of tokens
This is of course not possible.
Within the statement, placeholders can be used as parameter markers to indicate where data values are to be bound to the query later when you execute it. The parameter markers should not be enclosed within quotes, even if you intend to bind them to string values. Parameter markers can be used only where expressions should appear, not for SQL keywords, identifiers, and so forth.
In short: you can use parameter markers for literals (string, numbers, ..) only
If you can't change your database design I would recommend using another SQL statement with a simple bit check:
prepare("select id, (monday +
tuesday * 2 +
wednesday * 4 +
thursday * 8 +
friday * 16 +
saturday * 32 + sunday *64) as day
from calendar having (day) & (1 << :daynumber)")
Now you can simply check if a service is available on a specific weekday, by binding the daynumber (monday=0, tuesday=1, .. sunday=6).

PDO statement select where date less than error

I've spent 2 days trying to fix this - my date comparisons in MySQL aren't working when
trying to bind values with the <= less than or equals operator.
I've narrowed it down to the second date field (from a much more complicated script that changes the prepared statement joins/groups/fields/conditions depending on the request..hence why I am referring to table.column and selecting from only one table below - it works either way) but I can't make sense of it. The exact code used to work on php 5.x but I have just upgraded to php7.2.27.
Take the below SQL statement:
$sth = $this->prepare("SELECT
transaction.transactionid,
transaction.accountid,
transaction.userid,
transaction.billdate,
transaction.amount,
transaction.description,
transaction.confirmed
FROM transaction
WHERE
DATE(`billdate`) BETWEEN :startdate AND :enddate #date('2020-03-12')
ORDER BY billdate desc");
Trying to bind the following to it:
$terms = array(':startdate' => "2000-01-01",':enddate' => "2020-03-12");
foreach ($terms as $key => $value) {
if($value == (int)$value)
$sth->bindValue("$key", $value, PDO::PARAM_INT);
else
$sth->bindValue("$key", $value);
}
var_dump($sth);
var_dump($terms);
$sth->execute();
$this->rowCount = $sth->rowCount();
var_dump( $sth->fetchAll(PDO::FETCH_ASSOC));
This returns an empty array.
The table contains a handful of test rows. The below returns correctly:
...
DATE(`billdate`) BETWEEN :startdate AND '2020-03-12'
ORDER BY billdate desc
Wrapping the dates in date('date') seems to make no difference.
I've also tried this...
DATE(`billdate`) >= :startdate AND
DATE(`billdate`) <= '2020-03-12'
ORDER BY billdate desc
");
(Of course, changing the bound terms so that they mirror whatever I am using)
I need to be able to bind both variables.
Grateful for any pointers as I would love to avoid wasting another day debugging this!
Thanks
Just CAST to DATE:
WHERE billdate
BETWEEN CAST(:startdate AS DATE) AND CAST(:enddate AS DATE)"
And bind as PDO::PARAM_STR:
$sth->bindValue("startdate", "2000-01-01", PDO::PARAM_STR);
$sth->bindValue("enddate", "2020-03-12", PDO::PARAM_STR);

Using PHP to select rows between dates through variables

I'm trying to fetch all rows where the date column's value is a day in july using the code below:
$july="07";
$query=$conn->prepare("SELECT * FROM table WHERE date BETWEEN '2018-?-01' AND '2018-?-31'");
$query->execute($july,$july);
$row=$query->setFetchMode();
I have also tried like this:
$july="07";
$month_1="'2018-".$july."-01'";
$month_2="'2018-".$july."-31'";
$query=$conn->prepare("SELECT * FROM table WHERE date BETWEEN ? AND ?");
$query->execute(array($month_1,$month_2));
$row=$query->setFetchMode();
1st case, I get the following error:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens' in file.php on line 35
I assume I get that error because I cannot have '?' in a query between single brackets.
Second case, I get nothing at all.
Solved it with this:
SELECT * FROM table WHERE YEAR(date)=2018 AND MONTH(date)=?
Query 1 fails because you can't quote a placeholder.
Query 2 fails because the single quotes get escaped by the driver and then the whole escaped string gets wrapped in quotes. Roughly
date BETWEEN '\'2018-07-01\''
There are a few ways you could accomplish this.
Take the first 7 values of your date column and compare that against your string.
$date = '2018-07';
...then execute the query like ...
WHERE substr(date, 1, 7) = ?
You also could use the concat method which keeps the placeholder from being quoted.
$july="07";
$query=$conn->prepare("SELECT * FROM table WHERE date BETWEEN concat('2018-', ?, '-01') AND concat('2018-', ?, '-31')");
$query->execute(array($july, $july));
$row=$query->setFetchMode();
First case error is because you passed two variables to PDO::execute() and its expecting array there.
Suggested Reading...
http://php.net/manual/en/pdostatement.execute.php
Second case .. ummm.. you sure it does nothing?
Select * from mytable where date <= '2018-07-31' and date >= '2018-07-01'
or whatever... this maybe ...
select * from mytable where date between UNIX_TIMESTAMP(STR_TO_DATE('Jul 01 2018 12:00AM', '%M %d %Y %h:%i%p')) and UNIX_TIMESTAMP(STR_TO_DATE('Jul 31 2018 11:59PM', '%M %d %Y %h:%i%p'))
Never use * to return all columns in a table–it’s lazy. You should only extract the data you need. Even if you require every field, your tables will inevitably change. -https://www.sitepoint.com/mysql-mistakes-php-developers/

php sum column based on year criteria

I'm trying to sum a column named "total_fee" where the payment date is equaled to the current year but I received a blank. The code works with no error but the query came up empty. The payment_date is a datetime/timestamp. Does anyone know the php statement to sum current year and last year total? I have tried various ways with no lucks.
$stmt2 = $DB_CON_C->prepare("SELECT SUM(total_fee) AS current_year
FROM `".$row['email']."`
WHERE payment_date = date('Y')");
$stmt2->execute();
$sum2 = $stmt2->fetch(PDO::FETCH_ASSOC);
echo '<td>' .$sum2['current_year'].'</td>'
You need to supply the date value, not a php expression for the date.
You are also using prepared statements incorrectly. Look here https://www.w3schools.com/php/php_mysql_prepared_statements.asp
An easy way to test your query is to use 'between'
... WHERE payment_date BETWEEN '2017-01-01' AND '2017-12-31'

Comparing date() in sql query

I have 5 records in mysql database and these records have recorded date within this date interval.
$year=2015;
$month=8;
$datefrom=1;
$dateto=31;
$startdate='$year-$month-$datefrom 00:00:00';
$enddate='$year-$month-$dateto 23:59:59';
So I write a query to get these records out like this:
$sql = "SELECT id FROM newpost WHERE email=:email AND :startdate <= poststart <= :enddate AND postapproved=1";
Given that poststart column in table newpost has SQL Datetime format like this: "Y-m-d H:i:s".
But when I changed variable $year = 2016, I still got 5 results? It should return no record. Because those 5 records are recorded between 1 - 31 August 2015.
So I thought I did something wrong in the sql query especially the comparing date part but I could not configure out how to fix it?
Please help!
You can use BETWEEN in your query
$sql = "SELECT id FROM newpost WHERE email=:email AND (poststart BETWEEN :startmonth AND :endmonth) postapproved=1"
Use single quotes to wrap your date values
$sql = "SELECT id FROM newpost WHERE email=:email AND poststart BETWEEN ':startdate' AND ':enddate' AND postapproved=1";
A couple quick things to check to make sure it's not a syntactical error:
Your variable names don't match up. You defined startdate and enddate, but then in the query you used startmonth and endmonth.
You should probably also use leading zeros in your month and day, i.e.:
$month='08';
$datefrom='01';

Categories