Put a PHP variable in a SQL Query - php

I have the following code:
try
{
$sql = 'SELECT id, type, date, amount, description, category
FROM `transactions`
WHERE type = "income"
AND month(date) = '$monthselect'
ORDER BY `transactions`.`id` DESC
LIMIT 0,50';
$result2 = $pdo->query($sql);
}
Now, I want to give this month(Date) a variable which month I want to select. if I put 1, it will give me January. So i thought, if I define a variable with 1, I can use it to select a month, right?
$monthselect = 1;
It doesnt work. What am I doing wrong?

Use prepared statements:
$stm = $pdo->prepare('SELECT id, type, date, amount, description, category
FROM `transactions`
WHERE type = "income"
AND month(date) = ?
ORDER BY `transactions`.`id` DESC
LIMIT 0,50');
$stm->execute(compact('monthselect'));
$result2 = $stm->fetchAll();
Since you're not adding "1" directly in your query, I'm assuming here that the variable comes from user input.

To concatenate strings in PHP you need to use the . operator.
$sql = 'SELECT id, type, date, amount, description, category
FROM `transactions`
WHERE type = "income"
AND month(date) = ' . $monthselect . '
ORDER BY `transactions`.`id` DESC
LIMIT 0,50';

I'll frequently use double quotes to substitute variables in PHP:
$sql = "SELECT id, type, date, amount, description, category
FROM `transactions`
WHERE type = 'income'
AND month(date) = $monthselect
ORDER BY `transactions`.`id` DESC
LIMIT 0,50";
Note that you need to swap the existing double quotes (inside the string) to single quotes. You can escape them too, but I find this way makes it much more readable.

Your issue is that you are trying to use a variable inside single quotes, inside which php is not translated
I find by using double quote marks around my queries it allows me to not only use variables in them but to also be able to use single quote mark around the values passed to the db
$sql = "SELECT id, type, date, amount, description, category
FROM `transactions`
WHERE type = 'income'
AND month(date) = $monthselect
ORDER BY `transactions`.`id` DESC
LIMIT 0,50";

Related

SQLSTATE[42S22]: Column not found: 1054 Unknown column PhP PDO MariaDB10

I found myself in a really weird situation in PDO. A query doesn't want to execute when called from PhP but it does when called from HeidiSQL.
The error is in title.
SQL query from statement debugDumpParams :
SELECT s_id AS id,
s_title AS title,
genre.g_name AS genreName,
accounts.ac_public_name AS producerName,
s_price AS price,
DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') AS lastModifiedDate,
DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') AS addedDate,
s_downloads AS downloads,
s_sales AS sales,
s_rating AS rating,
s_status AS STATUS
FROM song
JOIN accounts ON accounts.ac_id = song.s_producer
JOIN genre ON genre.g_id = song.s_genre
WHERE 1=1 AND genre.g_id = '1'
ORDER BY s_status ASC, s_added_date DESC
LIMIT 0, 5;
Next is the part where I add genre.g_id = :id in the query string
if(isset($filterData["genreId"]) && $filterData["genreId"] !== ""){
$queryString .= " AND genre.g_id = :genreId";
}
And where I bind it
if(isset($filterData["genreId"]) && $filterData["genreId"] !== ""){
$genreParam = $filterData["genreId"];
$stmt->bindParam('genreId', $genreParam);
}
In both cases $filterData["genreId"] is set and it have a value, so no problems with the if. And if $filterData["genreId"] would be empy or not set there would be no problem with the query.
And the error : SQLSTATE[42S22]: Column not found: 1054 Unknown column \'genre.g_id\' in \'where clause\'
But wait, there is more.
If I put genre.g_id2 instead of genre.g_id it will look like this:
if(isset($filterData["genreId"]) && $filterData["genreId"] !== ""){
$queryString .= " AND genre.g_id2 = :genreId";
}
Now it won't even reach $stmt->debugDumpParams();
And all it gives out it's this error: SQLSTATE[42S22]: Column not found: 1054 Unknown column \'genre.g_id2\' in \'where clause\ , no query like before.
The column 100% exists in the table. Similar problem whith this query:
SELECT s_id AS id,
s_title AS title,
genre.g_name AS genreName,
accounts.ac_public_name AS producerName,
s_price AS price,
DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') AS lastModifiedDate,
DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') AS addedDate,
s_downloads AS downloads,
s_sales AS sales,
s_rating AS rating,
s_status AS STATUS
FROM song
JOIN accounts ON accounts.ac_id = song.s_producer
JOIN genre ON genre.g_id = song.s_genre
WHERE 1=1 AND accounts.ac_id = '999999'
ORDER BY s_status ASC, s_added_date DESC
LIMIT 0, 5;
It seems the problem is only with columns that are from the tables that table song JOIN with.
Next query works perfectly.
SELECT s_id AS id,
s_title AS title,
genre.g_name AS genreName,
accounts.ac_public_name AS producerName,
s_price AS price,
DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') AS lastModifiedDate,
DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') AS addedDate,
s_downloads AS downloads,
s_sales AS sales,
s_rating AS rating,
s_status AS STATUS
FROM song
JOIN accounts ON accounts.ac_id = song.s_producer
JOIN genre ON genre.g_id = song.s_genre
WHERE 1=1 AND s_status = '0'
ORDER BY s_status ASC, s_added_date DESC
LIMIT 0, 5;
2 days on this and no solutons. Most solutions I find are to check again if the column really exists :|
Does any one have a better solution for this specific problem ?
The goal is to select the data where the g_id is equal with the value I pass to it.
How I create the query string:
$queryString = "SELECT s_id as id,
s_title as title,
genre.g_name as genreName,
accounts.ac_public_name as producerName,
s_price as price,
DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') as lastModifiedDate,
DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') as addedDate,
s_downloads as downloads,
s_sales as sales,
s_rating as rating,
s_status as status
FROM song
JOIN accounts on accounts.ac_id = song.s_producer
JOIN genre on genre.g_id = song.s_genre
WHERE 1=1 ";
then in a function I add this
if(isset($filterData["genreId"]) && $filterData["genreId"] !== ""){
$queryString .= " AND genre.g_id = :genreId";
}
then I do this
$queryString .= " ORDER BY s_status asc, s_added_date desc";
$queryString .= " LIMIT :offset, :limit;";
and finally
$stmt = $dbConnector->getConnection()->prepare($queryString);
This is the part that is related to the problem. I can't post the entire function, it's really long.
This is used to search data based on some inputs or combinations of inputs. The entire DAO class is really big :)
OS: Windows 10,
PhP version: 7.2.19,
Apache version: 2.4.35,
MariaDB version: 10.4
Heidi SQL: 10.2.0.5599
One more edit:
If I add the columun in the queryString when I first declare it:
$queryString = "SELECT s_id as id,
s_title as title,
genre.g_name as genreName,
accounts.ac_public_name as producerName,
s_price as price,
DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') as lastModifiedDate,
DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') as addedDate,
s_downloads as downloads,
s_sales as sales,
s_rating as rating,
s_status as status
FROM song
JOIN accounts on accounts.ac_id = song.s_producer
JOIN genre on genre.g_id = song.s_genre
WHERE 1=1 AND genre.g_id = :genreId";
$queryString = $this->filterDataQuery($queryString, $filterData, "songs");
$queryString .= " ORDER BY s_status asc, s_added_date desc";
$queryString .= " LIMIT :offset, :limit;";
$stmt = $dbConnector->getConnection()->prepare($queryString);
$genreParam = $filterData["genreId"];
$stmt->bindParam(':genreId', $genreParam);
and not inside $this->filterDataQuery($queryString, $filterData, "songs"); when I build the queryString based on selected filters it works perfectly.
This is the select from the browser console now:
SELECT s_id AS id,
s_title AS title,
genre.g_name AS genreName,
accounts.ac_public_name AS producerName,
s_price AS price, DATE_FORMAT(s_last_modified_date, '%d/%m/%Y %H:%i:%s') AS lastModifiedDate, DATE_FORMAT(s_added_date, '%d/%m/%Y %H:%i:%s') AS addedDate,
s_downloads AS downloads,
s_sales AS sales,
s_rating AS rating,
s_status AS STATUS
FROM song
JOIN accounts ON accounts.ac_id = song.s_producer
JOIN genre ON genre.g_id = song.s_genre
WHERE 1=1 AND genre.g_id = '1'
ORDER BY s_status ASC, s_added_date DESC
LIMIT 0, 5;
Isn't this one the same with the first? For it is. I am blind ?
After rereading your first post dozens of times and testing locally all sorts of normal things without getting the same error, I think the $stmt variable inside filterDataQuery() holds a total different query than posted here. And it just doesn't include the table genre as shown in the error message.
If you're not using that function, seeing your second last code block, all works well (if I understand you correctly).
Also according to the code shown, I don't understand how you would call filterDataQuery() to add criteria to the querystring, and directly binding the params to the statement object that's prepared later on and thus is not available there (or not the one you would expect).
To prove my theory my complete test code, still using this db-fiddle:
ini_set('display_errors', 1);
error_reporting(E_ALL);
$DB_USER = '*****';
$DB_PASS = '*****';
$db = new PDO('mysql:host=localhost;dbname=testing.project', $DB_USER, $DB_PASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$select = 'SELECT s_id AS id,
s_title AS title,
genre.g_name AS genreName,
accounts.ac_public_name AS producerName,
s_price AS price,
DATE_FORMAT(s_last_modified_date, "%d/%m/%Y %H:%i:%s") AS lastModifiedDate,
DATE_FORMAT(s_added_date, "%d/%m/%Y %H:%i:%s") AS addedDate,
s_downloads AS downloads,
s_sales AS sales,
s_rating AS rating,
s_status AS STATUS
FROM song
JOIN accounts ON accounts.ac_id = song.s_producer
JOIN genre ON genre.g_id = song.s_genre';
$where = ' WHERE genre.g_id = :genreId';
$order = ' ORDER BY s_status ASC, s_added_date DESC';
$limit = ' LIMIT 0, 5';
// No problemo:
$sql = $select . $where . $order . $limit;
$stmt = $db->prepare($sql);
$genreId = 1;
$stmt->bindParam(':genreId', $genreId);
$stmt->execute();
// Trigger error column not found, $stmt containing an unexpected query:
$select = 'SELECT * FROM `song`';
$sql = $select . $where . $order . $limit;
$stmt = $db->prepare($sql);
$genreId = 1;
$stmt->bindParam(':genreId', $genreId);
$stmt->execute();
First, I don't pass the $stmt variable to filterDataQuery. I pass it to bindParams. bindParams function is called aftert filterDataQuery is done and after adding the strings for order by and limit.
I really have to thank you,Piemol , for trying to help me.
I checked Maria DB logs after a professor from my college adviced me to do so, and I found the problem there
In the same controller I was calling two DAO methods, one after another, one to bring the data, and the sencond to do a count.
$responseMessage = json_encode(AdminDAO::getInstance()->getFilteredSongsList($message));
$count = AdminDAO::getInstance()->getFilteredSongsListItemsCount($message); // this one was the problem
The problem was not in the one that was getting the data, the one I posted about. The problem was with the one doing the count. I did not added the joins there :| .
Alose there where no indication from what method the error was comming so I focused on the wrong one(the one that was displayed in log). Something like this would have never happened if I would have used a logger like I do in Java.
Here are the queries from MariaDB log file: https://www.heypasteit.com/clip/0IUPWG
First one is the one that fails and the 2nd one is the one that works.
I miss log4j from Java so much.
Thanks to everybody who tried to help me.
For those who have this kind of weird problems, check logs, put echos, don't focus on the first thing you see in a AJAX response log.

How to query first and last dates

I'm querying a wildlife sightings database to display the first sighting of Dingy_Skipper based on querystring 'yr' AS Dingy_Skipper_FDate. I would like to also display the last/latest sighting from the same querystring 'yr' AS Dingy_Skipper_LDate
I presume I need to use UNION but I have tried several times and can't seem to get it to work. I have never used UNION before so any help appreciated. Massive thank you in advance!
I have tried the following query but this produces an error on line $Dingy_Skipper1 = $sp1->query($Dingy_Skipper);.
<?php
// connect
$sp1 = dbConnect('read', 'pdo');
// prepare query
$theyear = $_GET['yr'];
$Dingy_Skipper = "
SELECT rDate AS Dingy_Skipper_FDate, Dingy_Skipper
FROM wbcrecords
WHERE YEAR(rDate) = '$theyear' AND Dingy_Skipper >='1' ORDER BY rDate ASC Limit 1
UNION
SELECT rDate AS Dingy_Skipper_LDate, Dingy_Skipper
FROM wbcrecords
WHERE YEAR(rDate) = '$theyear' AND Dingy_Skipper >='1' ORDER BY rDate DESC Limit 1";
// submit query capture result
$Dingy_Skipper1 = $sp1->query($Dingy_Skipper);
// free database
$Dingy_Skipper1->closeCursor();
?>
I have updated the query (see below) which now works correctly and I presume will protect against SQL injection? However, what would be the most efficient way of selecting the first and last date from a second column in the same table called Grizzled_Skipper so I have the first and last dates as $Grizzled_Skipper_FDate and $Grizzled_Skipper_LDate for the Grizzled_Skipper column as well as $Dingy_Skipper_FDate and $Dingy_Skipper_LDate for the Dingy_Skipper column?
<?php
if (isset($_GET['yr'])) {
require_once('inc/connection.php');
$conn = dbConnect('read', 'pdo');
$sql = 'SELECT MIN(rDate) AS Dingy_Skipper_FDate, MAX(rDate) AS Dingy_Skipper_LDate, Dingy_Skipper
FROM wbcrecords
WHERE YEAR(rDate) = :yr AND Dingy_Skipper >="1"';
$searchterm = $_GET['yr'];
$Species = $conn->prepare($sql);
$Species->bindParam(':yr', $searchterm, PDO::PARAM_STR);
$Species->bindColumn(1, $Dingy_Skipper_FDate);
$Species->bindColumn(2, $Dingy_Skipper_LDate);
$Species->bindColumn(3, $Dingy_Skipper);
$Species->execute();
$numRows = $Species->rowCount();
}
?>
You could likely get this using MIN() and MAX(), like this:
SELECT MIN(rDate) AS Dingy_Skipper_FDate,
MAX(rDate) AS Dingy_Skipper_LDate,
Dingy_Skipper
FROM wbcrecords
WHERE YEAR(rDate) = '$theyear'
AND Dingy_Skipper >='1'
GROUP BY Dingy_Skipper
Be sure to escape the variables that you are using in this query. Otherwise, you are wide open to SQL injection attacks.
EDIT: Rather than escaping, you would be better off using prepared statements and using the $theyear variable as a parameter.

MySQL: How to SELECT only three of each differing x-column-name

I want to display my blogs in a very future proof way. So I need to make a query that will reference new blog types if they are created. Hence the x-column-name. In this case that's blogType. This select query should contain the blog information for all blog types, but for each blog type get 3 Blogs. It's kind of confusing so this is why I am reiterating!
I have done a bit of work already in googling how to limit the results. Shown below:
$query = 'SELECT * FROM blogs
ORDER BY dateWritten
ASC LIMIT 3';
I'll be outputting the results to blog_rss.php using an array and a foreach loop. I get the array from a function like this:
function get_Recent_Blogs() {
global $db;
$query = 'SELECT * FROM blogs
ORDER BY dateWritten
ASC LIMIT 3';
try {
$statement = $db->prepare($query);
$statement->execute();
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
$statement->closeCursor();
return $result;
} catch (PDOException $e) {
$error_message = $e->getMessage();
display_db_error($error_message);
}
}
It's not too important but at least it gives you some context.
SELECT *
FROM (
SELECT b.*, ROW_NUMBER() OVER (PARTITION BY b.blogType ORDER BY b.blogID DESC) as rn
FROM blogs b
) x
WHERE x.rn <= 3
ORDER BY x.blogType, x.blogID DESC';
So I've done my best to implement the solution but I'm getting some errors. I'm not sure if I should start a new post for this one or not but the code above is what I used and this is the error I'm getting:
You have an error in your SQL syntax; it seems the error is around: '( PARTITION BY b.blogType ORDER BY b.dateWritten DESC ' at line 7
Since your MySQL version supports window functions, you can use ROW_NUMBER(), which will enumerate your entries per blog type. Then you just need to pick the first three rows per type.
SELECT *
FROM (
SELECT b.*, ROW_NUMBER() OVER (PARTITION BY b.type ORDER BY b.dateWritten DESC) as rn
FROM blogs b
) x
WHERE x.rn <= 3
ORDER BY x.type, x.dateWritten DESC -- adjust as needed
Notes:
I assume that the blog type is determined by the type column. Adjust it if needed.
You should use DESC instead of ASC, since you want to get the most recent entries.
I would rather use an AUTO_INCEMENT id column instead of dateWritten for sorting. That is more reliable, since a DATE or even a TIMESTAMP can have duplicates.
For older versions which don't support window functions I would first fetch all types and generate a UNION ALL query. With PDO it could be something like the following:
$types = $db
->query('SELECT DISTINCT type from blogs ORDER BY type')
->fetchAll(PDO::FETCH_COLUMN);
$subqueries = array_map(function($type){
return '(SELECT * FROM blogs WHERE type = ? ORDER by dateWritten DESC LIMIT 3)';
}, $types);
$query = implode(' UNION ALL ', $subqueries) . ' ORDER BY type, dateWritten DESC';
$statement = $db->prepare($query);
$statement->execute($types);
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
This will generate the following query:
(SELECT * FROM blogs WHERE type = ? ORDER by dateWritten DESC LIMIT 3)
UNION ALL
...
UNION ALL
(SELECT * FROM blogs WHERE type = ? ORDER by dateWritten DESC LIMIT 3)
ORDER BY type, dateWritten DESC
Note 1: Even though two queries are executed, given a composite index on (type, dateWritten) this can still be faster than other solutions. When you have a couple of blog types and many articles per type, this can even be faster than the ROW_NUMBER() solution.
Note 2: I would usually have a separate table for types, and the blogs table would reference the primary key type_id column. In that case the first query would be SELECT type_id from blog_types, and the subqueries would have the condition WHERE type_id = ?.

How to write sql query in php with if statement in WHERE

How do I write a SQL query with an if statement in the WHERE part of a query?
Here is my sql:
$sql = 'SELECT First, Second,
Third,Fifth, Status, Isik_id, Comments, Code
FROM Persons WHERE Status = 1 && Third = "'.$whoislogged.'" ORDER BY RAND() LIMIT 1';
The Third = "'.$whoislogged.'" is a word what comes from session.
If $whoislogged exists and is equal to Third then the query should be like:
$sql = 'SELECT First, Second, Third,Fifth, Status, Isik_id, Comments, Code
FROM Persons
WHERE Status = 1 && Third = "'.$whoislogged.'" ORDER BY RAND() LIMIT 1';
But if the $whoislogged dosent appear then the query should be like:
$sql = 'SELECT First, Second,
Third,Fifth, Status, Isik_id, Comments, Code
FROM Persons WHERE Status = 1 ORDER BY RAND() LIMIT 1';
So how can I put all this into one query?
You can handle it in query itself.
WHERE Status = 1 and if(Third = "'.$whoislogged.'", Third = "'.$whoislogged.'" , 1=1)
the condition part will check third is match with $whoislogged if it's true then add id as condition otherwise omit this condition.
Simply make a new variable that contains the Third statement
if ( !empty($whoislogged) ) $third = "AND Third = ". $whoislogged;
else $third = "";
$sql = 'SELECT First, Second,
Third,Fifth, Status, Isik_id, Comments, Code
FROM Persons WHERE Status = 1 '. $third .' ORDER BY RAND() LIMIT 1';
It is not the most efficient way but it works

Use declared "AS" variable in SQL query

I have the following query, which doesn't work:
$sql = "SELECT dma, COUNT(*) as dma_count, round(dma_count/32434 * 100) as dma_percent FROM {$table} where dma != '0' GROUP BY dma ORDER BY dma_count DESC;";
The reason (I know) it doesn't work is because I'm using dma_count in this part round(dma_count/32434 * 100).
What's the correct way to do this?
EDIT:
Additional challenge. Instead of using 32434, I want to use a variable. I get the variable like this:
$get_total = "SELECT count(DISTINCT `exuid`) from {$table};";
$total = $dbh->query($get_total)->fetchAll(PDO::FETCH_ASSOC);
so my query becomes (with the fix recommended in the comments)
$sql = "SELECT dma, COUNT(*) as dma_count, round(COUNT(*)/{$total} * 100) as dma_percent FROM {$table} where dma != '0' GROUP BY dma ORDER BY dma_count DESC;";
This doesn't work because I think $total is in the wrong format. How can I fix this?
EDIT AGAIN:
Got it! $total is just the count of my rows, so I have this instead.
SELECT dma, COUNT(*) as dma_count, round(COUNT(*)/(SELECT COUNT(*) FROM {$table}) * 100,2) as dma_percent FROM {$table} where dma != '0' GROUP BY dma ORDER BY dma_count DESC;"
you can use COUNT(*) in the equation instead of the alias, or the finial answer a sub query. In general avoid sub queries if you can use a join.

Categories