Cleanest way to check if a record exists - php

I am using the following code to check if a row exists in my database:
$sql = "SELECT COUNT(1) FROM myTable WHERE user_id = :id_var";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id_var', $id_var);
$stmt->execute();
if ($stmt->fetch()[0]>0)
{
//... many lines of code
}
All of the code works and the doubts I have are concerning if the previous code is clean and efficient or if there is room for improvement.
Currently there are two questions bugging me with my previous code:
Should I have a LIMIT 1 at the end of my SQL statement? Does COUNT(1) already limit the amount of rows found by 1 or does the server keep searching for more records even after finding the first one?
The if ($stmt->fetch()[0]>0). Would this be the cleanest way to fetch the information from the SQL Query and execute the "if conditional"?
Of course if anyone spots anything else that can improve my code, I would love your feedback.

Q: Should I have a LIMIT 1 at the end of my SQL statement? Does COUNT(1) already limit the amount of rows found by 1 or does the server keep searching for more records even after finding the first one?
Your SELECT COUNT() FROM query will return one row, if the execution is successful, because there is no GROUP BY clause. There's no need to add a LIMIT 1 clause, it wouldn't have any affect.
The database will search for all rows that satisfy the conditions in the WHERE clause. If the user_id column is UNIQUE, and there is an index with that as the leading column, or, if that column is the PRIMARY KEY of the table... then the search for all matching rows will be efficient, using the index. If there isn't an index, then MySQL will need to search all the rows in the table.
It's the index that buys you good performance. You could write the query differently, to get a usable result. But what you have is fine.
Q: Is this the cleanest...
if ($stmt->fetch()[0]>0)
My personal preference would be to avoid that construct, and break that up into two or more statements. The normal pattern...separate statement to fetch the row, and then do a test.
Personally, I would tend to avoid the COUNT() and just get a row, and test whether there was a row to fetch...
$sql = "SELECT 1 AS `row_exists` FROM myTable WHERE user_id = :id_var";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id_var', $id_var);
$stmt->execute();
if($stmt->fetch()) {
// row found
} else {
// row not found
}
$stmt->closeCursor();

Related

How can I count entries in mySQL database faster?

I am counting the entries in my SQL database:
$sql = "SELECT * FROM files WHERE id = ?";
$q = $pdo->prepare($sql);
$q->execute([$id]);
$rowCount =$q->rowCount();
The result of $rowCount is 500000.
But to output this single number takes 5 seconds! Is it possible to get this result faster?
Use the COUNT() function https://dev.mysql.com/doc/refman/8.0/en/counting-rows.html:
$sql = "SELECT COUNT(*) FROM files WHERE id = ?";
Also ensure that 'id' is an indexed column:
https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html
Replace * with a field(use auto-increment id) - This will reduce the time a bit.
Index that field. - If you use indexed field the query performance will increase.
SELECT * ..., then counting in PHP, requires shoveling all columns of all rows back to PHP. That's a lot of effort for very little gain.
SELECT COUNT(col) ... does the counting in by MySQL, but it must check for whether col is NULL. And it needs to get at the value of col for every row.
SELECT COUNT(*) ... counts the rows by whatever way is most efficient. This involves looking for the 'smallest' index (or the whole table, if no secondary indexes), and counting through it.
You must learn about INDEXes to get anywhere in databases! This is only one minor use for them.

Check if row exists, The most efficient way?

SQL Queries /P1/
SELECT EXISTS(SELECT /p2/ FROM table WHERE id = 1)
SELECT /p2/ FROM table WHERE id = 1 LIMIT 1
SQL SELECT /P2/
COUNT(id)
id
PHP PDO Function /P3/
fetchColumn()
rowCount()
From the following 3 Parts, What is the best method to check if a row exists or not with and without the ability to retrieve data like.
Retrievable:
/Query/ SELECT id FROM table WHERE id = 1 LIMIT 1
/Function/ rowCount()
Irretrievable
/Query/ SELECT EXISTS(SELECT COUNT(id) FROM table WHERE id = 1)
/Function/ fetchColumn()
In your opinion, What is the best way to do that?
By best I guess you mean consuming the least resources on both MySQL server and client.
That is this:
SELECT COUNT(*) count FROM table WHERE id=1
You get a one-row, one-column result set. If that column is zero, the row was not found. If the column is one, a row was found. If the column is greater that one, multiple rows were found.
This is a good solution for a few reasons.
COUNT(*) is decently efficient, especially if id is indexed.
It has a simple code path in your client software, because it always returns just one row. You don't have to sweat edge cases like no rows or multiple rows.
The SQL is as clear as it can be about what you're trying to do. That's helpful to the next person to work on your code.
Adding LIMIT 1 will do nothing if added to this query. It is already a one-row result set, inherently. You can add it, but then you'll make the next person looking at your code wonder what you were trying to do, and wonder whether you made some kind of mistake.
COUNT(*) counts all rows that match the WHERE statement. COUNT(id) is slightly slower because it counts all rows unless their id values are null. It has to make that check. For that reason, people usually use COUNT(*) unless there's some chance they want to ignore null values. If you put COUNT(id) in your code, the next person to work on it will have to spend some time figuring out whether you meant anything special by counting id rather than *.
You can use either; they give the same result.

SELECT * FROM table HUNDREDS MILLIONS ROWS

Okay so I have a table which currently has 40000 rows and I need to SELECT them all. I use a index for id and url column and if I have to SELECT a value by id or url it's instant but if I have to SELECT * it's very slow. What I'm trying to do is searching my database and output the matches and I did this with a
while($arr = mysqli_fetch_array($query))
{ #code... echo $arr['whatever_i_need']."<br>"; }
$query = mysqli_query($link,"SELECT * FROM table");
In the future I will have hundreds of millions of rows in the database so I would like to return the search results fast in 1 sec or something. If you can give me solutions I would really appreciate! Thanks!
EDIT:
I don't want to display all of the data but I want to loop through it quickly to find all the matches
If you want speed then you definitely don't want the query to return every row from the table, and then "loop through" every row returned by the query to identify the ones you are interested in returning. That approach might give acceptable performance with small tables, but it definitely doesn't scale.
For performance, you want the database to locate just the rows you want to return, filter out the ones you don't want, and return just the subset.
And that comes down to writing an appropriate SQL query; executing an appropriate SELECT statement.
SELECT t.col1
, t.col2
, t.col3
FROM mytable t
WHERE t.col3 LIKE '%foo%'
AND t.col2 >= '2016-03-15'
AND t.col2 < '2016-06-15'
ORDER BY t.col2 DESC, t.col1 DESC
LIMIT 200
Performance is about making sure appropriate indexes are available and that the query execution is making effective use of the available indexes.

How to get number of rows that COULD have been affected by an UPDATE?

Getting the number of rows affected by a MySQL update is one thing, but how do I get the number of matching rows for an UPDATE query? Otherwise put, the number of rows that COULD have been affected by the update query, even if the data I'm trying to update the rows with is identical to the data already contained within them? Because in this specific case, the DB will not overwrite the data with, so the number of affected rows is 0.
Is there some PDO method that helps, similar to PDOStatement::rowCount() ?
I also want to avoid a secondary SQL statement, otherwise the question is pointless.
You can use count() in a select clause for that:
If your update statement would be
update your_table
set some_column = 'hello'
where id = 1
Then you could select the affected rows count like this
select count(*) as affected_rows
from your_table
where id = 1
I wanted to avoid extra queries if possible and fortunately, a colleague of mine pointed out that I can give the PDO instance a particular MySQL driver option that forces the rowCount method to return the number of matches related to the query, instead of the number of affected rows, like so:
$options = array(
PDO::MYSQL_ATTR_FOUND_ROWS => true // forces the rowCount() method to return the number of matching rows, not the number of affected rows
);
$connection = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass, $options);
Here is another way:
mysql_query('begin');
mysql_query('update ...');
mysql_affected_rows();
mysql_query('rollback');
select count(*) is a better thing to do for simple queries. But that won't get correct results in case you are updating multiple tables with joins.
Add the column update_time in model.Whenever update model.Automatically save updated time in that field.
And
How about querying like this
SELECT * from
table_name
WHERE 'updated_time=12:05:23'

Mysql SELECT COUNT(*) OR SELECT 1? PDO

It has long been known that PDO does not support COUNT(*) and a query like below would fail as it doesn't return any affected rows,
$q = $dbc -> prepare("SELECT COUNT(*) FROM table WHERE id = ?");
$q -> execute(array($id));
echo $q -> rowCount();
Doing some research I found that you can also get the row count using other methods of count and not using count at all, for example the following query is supposed be the same as above but will return correct for PDO,
$q = $dbc -> prepare("SELECT 1 FROM table WHERE id = ?");
$q -> execute(array($id));
echo $q -> rowCount();
There are various sources on the internet claiming that;
"SELECT COUNT(*)
"SELECT COUNT(col)
"SELECT 1
Are all the same as each other (with a few differences) so how come using mysql which PDO cannot properly return a true count, does
"SELECT 1
work?
Methods of count discussion
Why is Select 1 faster than Select count(*)?
PDO does not support COUNT(*)
WTF? Of course PDO supports COUNT(*), you are using it the wrong way.
$q = $dbc->prepare("SELECT COUNT(id) as records FROM table WHERE id = ?");
$q->execute(array($id));
$records = (int) $q->fetch(PDO::FETCH_OBJ)->records;
If you are using a driver other than MySQL, you might have to test rowCount first, like this.
$records = (int) ($q->rowCount()) ? $q->fetch(PDO::FETCH_OBJ)->records : 0;
Oh. You are confusing everything.
PDO do not interfere with SQL queries. It support EVERYTHING supported by SQL.
When doing COUNT(*) you shouldn't use rowcount at all, as it just makes no sense. You have to retreive the query result instead.
Dunno what "various sources" you are talking about but COUNT(*) and COUNT(col) (and even COUNT(1)) are the same and the only proper way to get count of records when you need no records themselves.
COUNT is an aggregate function, it counts rows for you. So, it returns the result already, no more counting required. Ad it returns just a scalar value in the single row. Thus, using rowcount on this single row makes no sense
SELECT 1 is not the same as above, as it selects just literal 1 for the every row found in the table. So, it will return a thousand 1s if there is a thousands rows in your database. So, rowcount will give you the result but it is going to be an extreme waste of the server resources.
there is a simple rule to follow:
Always request the only data you need.
If you need the count of rows - request count of rows. Not a thousand of 1s to count them later.
Sounds sensible?
Best way I think to test if a line exist in your database is to perform.
SELECT 1 FROM table WHERE condition LIMIT 1
If it find a row it will stop and tell you there is a line. If it don't and you have an index on your where clause column it will also goes very fast to see there are none available.

Categories