How can I get total documents/objects retrieved from an MySQL query? - php

I have this query: SELECT SQL_CALC_FOUND_ROWS DISTINCT something.* FROM someting WHERE TRUE AND something.is_active=1 ORDER BY long_id DESC. The query above will retrieve some objects and document.
Is it possible to get the total number of documents/objects from that query before retrieving the documents/objects themselves?
The case here is that table "something" can have over 100,000 documents/objetcs hence cannot be handled by PHP (returning fatal error insufficient memory). So I want to know how many documents/objects will be retrieved from the query.
I have tried any combinations of COUNT:
SELECT COUNT(*) SQL_CALC_FOUND_ROWS DISTINCT something.* FROM something WHERE TRUE AND something.is_active=1 ORDER BY long_id DESC
SELECT COUNT(SQL_CALC_FOUND_ROWS DISTINCT something.*) FROM something WHERE TRUE AND something.is_active=1 ORDER BY long_id DESC
SELECT SQL_CALC_FOUND_ROWS DISTINCT COUNT(something.*) FROM something WHERE TRUE AND something.is_active=1 ORDER BY long_id DESC
but always have this error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ....

SELECT COUNT(DISTINCT something.*) FROM something WHERE something.is_active=1 ORDER BY long_id DESC
Since you use COUNT, there is no need to use SQL_CALC_FOUND_ROWS. Also, I remove TRUE in where clause because it is unnecessary.

SELECT COUNT(*) FROM something WHERE something.is_active=1
Just simplify your query and count all of the results. DISTINCT something.* makes no sense either (unless your table doesn’t have a primary key, which I doubt is the case).

Related

What is the best way to do the job of Group By in mysql when sql_mode=only_full_group_by

I want to perform a query like to get the last record of any type in DB, at my localhost, I use Maria Db and the query is as follow:
SELECT *
FROM table_a
WHERE column_a=999
OR column_b=999
GROUP
BY `group`;
group is a column which I save type in it, for instance: marketing, blog, order, etc
This query works fine on local, but on the server I get the following error:
SQLSTATE[42000]:
Syntax error or access violation:
1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
'db_name.table_a.id' which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by\n
The SQL being executed was:
SELECT * FROM `table_a` WHERE (`column_a`=999) OR (`column_b`=999) GROUP BY `group`"
According to MySQL document I can use the following command to make this possible:
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
But I don't have the sufficient privilege on Db and get the following error:
#1227 - Access denied; you need (at least one of) the SUPER privilege(s) for this operation
I asked the hosting to do this for me, they replied that they don't want to do this action
I use the YII2 framework, and now I want a way to add this on the option of database_config.php of the framework or change the query to something else with the same result, and not losing performance
ONLY_FULL_GROUP_BY is a good thing, which enforces basic ANSI SQL rules. Don't change it, fix your code instead.
From there one: you want entire records, so you should not think aggregation, but filtering.
Then: in a database table, records are unordered; for your question to just make sense, you need a column that defines the ordering of rows in each group, so it unambiguous what "last" record mean. Let me assume that you have such column, called id.
Here is a typical approach at this top-1-per-group problem, using a correlated subquery for filtering:
SELECT *
FROM table_a a
WHERE
999 IN (column_a, column_b)
AND id = (
SELECT MAX(a1.id)
FROM table_a a1
WHERE 999 IN (a1.column_a, a1.column_b) AND a1.grp = a.grp
)
Alternatively, if you are running MySQL 8.0, you can use window functions:
SELECT *
FROM (
SELECT a.*,
ROW_NUMBER() OVER(PARTITION BY grp ORDER BY id DESC) rn
FROM table_a a
WHERE 999 IN (column_a, column_b)
) a
WHERE rn = 1
Side note: group is a language keyword, hence a poor choice for a column name. I renamed it to grp in the queries.
There are a few ways to "bypass" the sql_mode but be aware that the result you get might not be correct.
First you can use ANY_VALUE(). Example like this:
SELECT any_value(column_a), any_value(column_b), `group` FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
When using ANY_VALUE() function you have to write all the columns in SELECT from the table and append with ANY_VALUE() except for the column that you use in the GROUP BY.
Using MAX() or MIN() can return result but still it might not be the correct
result, especially for any row(s) that have more than 1 count:
SELECT MAX(column_a), MAX(column_b), `group`
FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
Using GROUP_CONCAT will give you a view at what are the values in non-grouped columns. Compare the results with the other queries above and you can see on row(s) that returns more than one count, does the other queries returning according to what you want?
SELECT group_concat(column_a), group_concat(column_b), group_concat(`group`)
FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
I'm not sure if you can do this but you can set the sql_mode off temporarily then you should be able to run your query:
SET sql_mode=""; -- you don't need to set global privilege.
SELECT * FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
Demo here.
Still, the best option is to retain the sql_mode as it is and construct the query according to the requirement.
P/S: GROUP is a reserved word in both MySQL & MariaDB. You can use it as column name but you have to always add back-ticks to define the column or else, running the query will return you an error like
Query: select * from table_a group by group
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'group' at line 1

SQL_CALC_FOUND_ROWS with FOUND_ROWS always returns 1

select SQL_CALC_FOUND_ROWS, col1 , mytable.id from mytable group by col1;
select found_rows();
select found_rows() , col1 , mytable.id from mytable group by col1;
This above query always results 1 for found rows. I am using php 7.1 and maria db 10.1 .
My backend is laravel 5.5 and I am making DB::raw request on mysql.
Server apache lamp server.
Is it a bug or there is some way around for this ?
Solution 1:
Please check the mysql version. This bug exists in 5.6.* and is solved in 5.6.11 release. Verify that your server has version different from [5.6.* < 5.6.11] to make this work.
This is a bug posted in mysql [not in mariadb] here:
http://bugs.mysql.com/bug.php?id=68458.
If you have verified the version of mysql, then check below explanation:
NOTE: It is mentioned in the bug link that "MySQL server compiled from "5.6.11-log Source distribution" still has this bug."
Solution 2:
found_rows() and limit in the same query will not result in total count but will give count for data with limit.
If there is a limit in the select query, you will have to use
"select SQL_CALC_FOUND_ROWS, ....... limit x;"
and immediately next execute
"select found_rows();"
OR
If there is no limit in the select statement then you can directly execute
"select found_rows(), .......;"
Explanation here: https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_found-rows
The example query seems to be wrong, as you should have the col1 to be a part of selected fields
found_rows returns the number of rows found in the previous query.
Have you checked the result of the first query? how many rows it returns?
If it returns more than 1 rows - meaning that some query is being executed between your query and select found_rows() at the same session.
I didn't succeed to reproduce this problem, but you can try using transaction here.
On the other part -
From the query it seems that you are trying to get a distinct count from a table, but using a group by for some reason.
did you try using
select count( disctinct col1) from mytable
If this does not work for you, your second best option is:
select count(1) from (<your query goes here>)

Mysql query showing different results

I want to count total records in the table. My table contains 1 million records, so I'm using
EXPLAIN
SELECT COUNT(id) FROM table_name
instead of
SELECT COUNT(id) FROM table_name
for faster retrieval.
If I'm using SELECT COUNT(id) FROM table_name means I'm getting proper count. EXPLAIN SELECT COUNT(id) FROM table_name gives wrong count. I have reduced the record count to 10000, But still I can't find the issue.
Here is my Query
EXPLAIN SELECT COUNT(id) FROM table_name - 12764 - wrong
SELECT COUNT(id) FROM table_name - 10000 - right
Explain is not an optimization it is a description of the execution plan for the specified statement. This explanation is returned as a table. And this is what you are counting.
Therefore you are counting completely different things.
From the MySQL docs:
EXPLAIN is used to obtain a query execution plan (that is, an explanation of how MySQL would execute a query).
As explain select can return the rows affected. You may want to use something like
EXPLAIN
SELECT SUM(rows)
FROM table_name
GROUP BY (table, rows)

SQL MAX in conjunction with WHERE clause

I need to find the largest value from one particular column in a mysql table where the value of another column is equivalent to something. But, with the query that I'm using, I keep on getting a message that displays and SQL error. My query is aS follows:
SELECT MAX(message_id) AS top_message HAVING(child_id) = '".$message_id[$i]."'
Any suggestions?
You are also missing a table name:
SELECT MAX(message_id) AS top_message FROM tablename WHERE child_id = '".$message_id[$i]."'
You should use WHERE instead of HAVING Clause:
SELECT MAX(message_id) AS top_message
FROM tablename
WHERE child_id = '".$message_id[$i]."'
Use only HAVING clause when you have an aggregated conditon.
You need a from clause and a where clause. The having clause is used for group filters. You don't have a group by clause, so there is no reason to write a having clause. If the table where you want to select from is called 'MyTable', then your query is as follows:
SELECT MAX(message_id) AS top_message
FROM MyTable
WHERE child_id = '".$message_id[$i]."'
Note, that the paranthesis around child_id is not needed. Please read SQL and MySQL tutorials for more information, your life will be much easier.

Error when using SQL_CALC_FOUND_ROWS()

I'm doing a simple MySQL query to count the number of rows a query is returning without the effect of the LIMIT clause. I'm using Active Records with Codeigniter PHP framework.
Problem: I'm getting an error when SQL_CALC_FOUND_ROWS is used. Why is this so?
Query
SELECT `listing_id`, SQL_CALC_FOUND_ROWS listing_id FROM (`listings`) LIMIT 100
Error
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'SQL_CALC_FOUND_ROWS listing_id FROM (listings) LIMIT 100' at
line 1
Codeigniter Active Records
$this->db->select('listing_id')
->select('SQL_CALC_FOUND_ROWS listing_id', FALSE)
->from('listings')
->where('price < 1000')
->limit($limit, $offset)
->order_by('listing_id', 'desc');
SQL_CALC_FOUND_ROWS doesn't return a value, it's simply a modifier to indicate that the number of rows —where the LIMIT clause is not taken into account— should be saved so it can be retrieved later on, by using a second query (without generating the complete result set twice). Think of it the same as the DISTINCT keyword.
For more information, please read the documentation on this topic.
You can use MySql's count() function to achieve what you are looking for ie.
SELECT `listing_id`, count(*) FROM (`listings`) LIMIT 100

Categories