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.
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.
I would like to make a SQL query of counted grouped results. The only way I was able to do it was with the help of PHP. But I am sure that it is possible to do it in one SQL statement.
The table's structure is the following:
id, user_hash, timestamp
And my little PHP workaround looks like this:
$sql = 'SELECT count(id) AS \'total clicks\' FROM stats WHERE timestamp > UNIX_TIMESTAMP(NOW() - INTERVAL '.$num_days.' DAY) GROUP BY user_hash ORDER BY count(id) DESC';
$result = $mysqli->query($sql);
$count = array();
while ($row = mysqli_fetch_assoc($result)) {
if (isset($count[$row['total clicks']])) {
$count[$row['total clicks']]++;
} else {
$count[$row['total clicks']] = 1;
}
}
foreach ($count as $k => $v) {
echo $v." users clicked ".$k." times\n";
}
I am probably going to kick myself how easy it is, but I just can't seem to get to an all-in-one SQL solution. :)
Please try:
$sql = 'SELECT
A.`user_times_clicked`,
COUNT(A.user_hash) AS `user_count`
FROM(
SELECT
count(id) AS `user_times_clicked`,
user_hash
FROM
stats
WHERE
timestamp > UNIX_TIMESTAMP(NOW() - INTERVAL '.$num_days.' DAY)
GROUP BY
user_hash
) AS A
GROUP BY
A.`user_times_clicked`
ORDER BY
COUNT(A.user_hash) DESC'
I hope it helps.
I have a query that uses PostgreSQL generate_series function but when it comes to large amounts of data, the query can be slow. An example of code the generates the query is below:
$yesterday = date('Y-m-d',(strtotime ( '-1 day' ) ));
$query = "
WITH interval_step AS (
SELECT gs::date AS interval_dt, random() AS r
FROM generate_series('$yesterday'::timestamp, '2015-01-01', '1 day') AS gs)
SELECT articles.article_id, article_title, article_excerpt, article_author, article_link, article_default_image, article_date_published, article_bias_avg, article_rating_avg
FROM development.articles JOIN interval_step ON articles.article_date_added::date=interval_step.interval_dt ";
if (isset($this -> registry -> get['category'])) {
$query .= "
JOIN development.feed_articles ON articles.article_id = feed_articles.article_id
JOIN development.rss_feeds ON feed_articles.rss_feed_id = rss_feeds.rss_feed_id
JOIN development.news_categories ON rss_feeds.news_category_id = news_categories.news_category_id
WHERE news_category_name = $1";
$params = array($category_name);
$query_name = 'browse_category';
}
$query .= " ORDER BY interval_step.interval_dt DESC, RANDOM() LIMIT 20;";
This series looks for only content that goes one day back and sorts the results in random order. My question is what are was that generate_series can be optimized to improve performance?
You don't need that generate_series at all. And do not concatenate query strings. Avoid it by making the parameter an empty string (or null) if it is not set:
if (!isset($this -> registry -> get['category']))
$category_name = '';
$query = "
select articles.article_id, article_title, article_excerpt, article_author, article_link, article_default_image, article_date_published, article_bias_avg, article_rating_avg
from
development.articles
inner join
development.feed_articles using (article_id)
inner join
development.rss_feeds using (rss_feed_id)
inner join
development.news_categories using (news_category_id)
where
(news_category_name = $1 or $1 = '')
and articles.article_date_added >= current_date - 1
order by
date_trunc('day', articles.article_date_added) desc,
random()
limit 20;
";
$params = array($category_name);
Passing $yesterday to the query is also not necessary as it can be done entirely in SQL.
If $category_name is empty it will return all categories:
(news_category_name = $1 or $1 = '')
Imho, try removing that random() in your order by statement. It probably has a much larger performance impact than you think. As things are it's probably ordering the entire set by interval_dt desc, random(), and then picking the top 20. Not advisable...
Try fetching e.g. 100 rows ordered by interval_dt desc instead, then shuffle them per the same logic, and pick 20 in your app. Or wrap the entire thing in a subquery limit 100, and re-order accordingly along the same lines.
My problem is:
I have a dbase query (A) with events_A with today date
$STH_1 = $DBH_R->query("SELECT table_name
FROM information_schema.tables
WHERE table_name
LIKE 'v_c_ei\_9%' ");
$stmts_1 = array();
while (($row_1 = $STH_1 -> fetch_assoc()) !== null){
$table_name = $row_1['table_name'];
$stmts_1[] = sprintf("
SELECT *
FROM $table_name
WHERE date_time = $today_date
");
}
$stmt_1 = implode("\nUNION\n", $stmts_1);
$stmt_1 .= "\nORDER BY date_time ASC";
$STH_5_2 = $DBH_R->query($stmt_1);
while (($row_5_2 = $STH_5_2 -> fetch_assoc()) !== null) { }
and I can display the results from this in the table_A.
But I have a dbase query (B) with events_B with today date
$table_name_2 = 'v_c_e';
$STH_e = $DBH_R->query("
SELECT *
FROM `$table_name_2`
WHERE date_time = $today_date
");
while (($row_e = $STH_e -> fetch_assoc()) !== null) { }
and I can display the results from this in the table_B.
The source tables for the first query and for the second query have only one common column - date_time.
What I must to do (and how) to display events_A and events_B in one table (ORDER BY date_time ASC)? - and for future - how add events_C (f.ex. similar to events_B)?
// I know that after all I need to format the data so that they look similar and it can be displayed in one table.
I'm not clear from your question whether you want to join the results - that is, the information from both tables is related in that two records sharing a date_time should be the same event - or unrelated.
If it is the latter, you cannot combine them meaningfully; you effectively have two different schemas.
If the information in tables A and B refers to the same events, and the date_time is a unique key, then you can do the following:
SELECT * FROM events_A, events_B WHERE events_A.date_time=events_B.date_time;
... or the like.
I think, from the query you built in PHP, you're not very strong on the features of SQL and when you would want to use them. It might be worth reading something like this guide to get a better handle on what should be done in the database rather than in PHP code before proceeding.