How to target specific rows returned by mysqli query - php

Let's say I have a table with following columns: id-1, id-2, col-1, col-2, col-3
Here, id-1 is the primary key and is auto-incremented. id-2 is a different id and is not necessary to be unique. There are multiple instances of same id in that column. col-1, col-2, col-3 are just necessary columns.
I pass a query to select data from the table.
mysqli_query($connect, SELECT * FROM table WHERE id-2='some_specific_id')
It will return multiple rows. I would like to know how can I target specific rows, say row number 3.

First, use ":
mysqli_query($connect, "SELECT * FROM table WHERE id-2 = 'some_specific_id'");
Target specific row? Do you mean to limit the fetched rows? Or get the 3rd row?
For limiting the fetched rows, you can use LIMIT:
SELECT * FROM table WHERE id-2='some_specific_id' LIMIT 3
For getting the third row:
SELECT * FROM table WHERE id-2='some_specific_id' LIMIT 2, 1

Well although it seems you just rather needed to learn basic SQL to get your answer, there is still the question in the title, that may attract other people whose problem is formulated exactly like that. So goes the answer:
Mysqli is not very convenient for this task, so we would use PDO.
In case your query is intended to return multiple rows and you need to address one of them by number (which is rather makes little sense, but anyway), use fetchAll() method:
$stmt = $connect->prepare("SELECT * FROM table WHERE id2=?");
$stmt->execute(['some specific id']);
$data = $stmt->fetchAll();
and you will be able to address returned rows by number, starting from zero:
echo $data[0]['col1'];
However, it makes more sense to address the returned rows by some unique id. In this case just add this unique field fiset in the field list and then use the special PDO street magic:
$stmt = $connect->prepare("SELECT id1, table.* FROM table WHERE id2=?);
$stmt->execute(['some specific id']);
$data = $stmt->fetchAll(PDO::FETCH_UNIQUE);
and you will be able to address returned rows by that unique field :
echo $data[$id1]['col1'];

Use LIMIT to get what you want like:
SELECT * FROM table WHERE id-2='some_specific_id' LIMIT 2, 1;
Or, if you want to fetch from array, then use the 3rd index of array.
LIMIT Explanation:
The following illustrates the LIMIT clause syntax with two arguments:
SELECT
column1,column2,...
FROM
table
LIMIT offset , count;
Let's examine the LIMIT clause parameters:
The offset specifies the offset of the first row to return. The offset of the first row is 0, not 1.
The count specifies the maximum number of rows to return.

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.

Get count from mysql_num_rows result

I have a form that collects RSVPs for a party. The person filling out the form (Name) has an option to bring a guest (Guest). In the notification email, I am trying to give an accurate count of RSVPs with total Name and Guest counts.
It is easy to do if I was just getting the Name count (for person filling out the form):
mysql_query($query);
$result = mysql_query("select * from tablename");
$rowCount = mysql_num_rows($result);
mysql_close($link);
and I would simply use $rowCount as the total number of RSVPs. But I also want to include Guests in the count. The Guest is entered as GuestFirstName and GuestLastName in the form and goes on the same record as the person filling out the form. So I tried this. It worked when I included a guest - it said total RSVPs were 2. But when I did not include a guest on the next test I did, it also counted it as 2. Here is the code I used for that:
mysql_query($query);
$result = mysql_query("select * from tablename");
$result_guest = mysql_query("select * from tablename WHERE
GuestFirstName IS NOT NULL);
$rowCount = mysql_num_rows($result) + mysql_num_rows($result_guest) ;
mysql_close($link);
Like I said, this code yields 2 RSVPs even when no guest is entered. Any help would be appreciated. Thanks.
cdr6800
and
You can achieve this in a single query like the following. Also try to use PDO or mysqli_* functions because you use mysql_* which are deprecated.
SELECT
COUNT(*) AS total_names,
COUNT(CASE WHEN GuestFirstName IS NOT NULL THEN 1 END) AS total_guests
FROM tablename;
So, if there are 10 invitation rows and only 4 of them have guests results will be:
total_names total_guests
10 4
That's an expensive way to get a count of rows: selecting all columns from all rows in the table, and fetching those from the database to the client, to get just a "count" of the number of rows.
SQL provides aggregate functions. For example COUNT() can be used to return a count of rows.
As far as why you are getting two rows returned...
WHERE GuestFirstName IS NOT NULL
The most likely explanation is that there are two rows in the table which have a non-NULL value assigned to the GuestFirstName column. It may be an empty (zero-length string), which isn't considered NULL. Consider
SELECT '' IS NULL -- 0 (false)
, '' IS NOT NULL -- 1 (true)
I think you probably also want to exclude from the count rows where the GuestFirstName is equal to the empty string.
The PHP mysql_ extension is deprecated, and will be removed in future release. Two suitable replacements are available: PDO and mysqli.
And use a query that returns a single row with a count of rows. That will be more efficient than returning the entire table, just to get a count.
And do it in one pass through the data. For a row that includes a Guest, increment the count by 2, otherwise increment the count by 1. Use a SUM() aggregate, with an expression that returns either 2 or 1.
For example:
SELECT SUM(IF(t.GuestFirstName<>'',2,1)) AS total_including_guests
, SUM(1) AS count_rsvp
FROM mytable t
Maybe Guest Name has an empty (but not null) value. Try:
$result_guest = mysql_query( select * from tablename
WHERE GuestFirstName IS NOT NULL
AND GuestFirstName != '');
You can actually do this with one query and no additional logic, using SUM()
SELECT SUM(IF(GuestFirstName IS NULL, 1, 2)) AS totalGuests FROM tablename;
This will iterate over all rows and and sum up 1 (if 1 guest in this row is coming) or 2 (if "main" guest + his invite are coming)

ROWNUM doesn't work; trying to grab info on specific row range in SQL query

I have a bit of code that counts the number of rows upon an SQL query and then does another query to grab information on the last four rows. I've used ROWNUM, but it doesn't work.
$newscount = $db->query("
SELECT *
FROM news
");
$counter = $newscount->rowCount();
$grabnewsmsg = $db->query("
SELECT *
FROM news
WHERE ROWNUM >= $counter-4 -- this particular part doesn't owrk
ORDER BY updateno DESC -- an A_I column
");
I've commented the specific areas I'm having problems. The A_I part is fine, since there should be a unique identifier for each row, but ROWNUM just doesn't work despite what I have read on other sites in addition to other questions/answers on SO. It returns an error column rownum does not exist.
I want to get information on solely the last four rows ($query->rowCount()-4), but I can't select via a certain updateno threshhold. If a user deletes a row, the A_I column cannot be appropriately used to determine the row number.
Additionally, I've tried the below:
$grabnewsmsg = $db->query("
SELECT *
FROM news
ORDER BY updateno DESC
LIMIT 0,4
");
And while this gives the desired results, I'm still not sure why ROWNUM doesn't work.
You need to understand the meaning of that AUTO_INC thingy. It is called an unique identifier, and for a reason. "Unique" means no other row should have the same identifier ever.
Yet it has absolutely nothing to do with whatever enumeration. So -
I have an autoincrementing column titled 'updateno' which corresponds to the number of the row.
this is what you are doing wrong.
As a matter of fact, you don't need such a field at all. If you wnat to enumerate your fields - do in on the fly. If you want an identifier - use a conventional name for this - "id"
While for the whatever "rownum" feature you need another mysql operator, namely LIMIT

Getting random results from large tables

I'm trying to get 4 random results from a table that holds approx 7 million records. Additionally, I also want to get 4 random records from the same table that are filtered by category.
Now, as you would imagine doing random sorting on a table this large causes the queries to take a few seconds, which is not ideal.
One other method I thought of for the non-filtered result set would be to just get PHP to select some random numbers between 1 - 7,000,000 or so and then do an IN(...) with the query to only grab those rows - and yes, I know that this method has a caveat in that you may get less than 4 if a record with that id no longer exists.
However, the above method obviously will not work with the category filtering as PHP doesn't know which record numbers belong to which category and hence cannot select the record numbers to select from.
Are there any better ways I can do this? Only way I can think of would be to store the record id's for each category in another table and then select random results from that and then select only those record ID's from the main table in a secondary query; but I'm sure there is a better way!?
You could of course use the RAND() function on a query using a LIMIT and WHERE (for the category). That however as you pointed out, entails a scan of the database which takes time, especially in your case due to the volume of data.
Your other alternative, again as you pointed out, to store id/category_id in another table might prove a bit faster but again there has to be a LIMIT and WHERE on that table which will also contain the same amount of records as the master table.
A different approach (if applicable) would be to have a table per category and store in that the IDs. If your categories are fixed or do not change that often, then you should be able to use that approach. In that case you will effectively remove the WHERE from the clause and getting a RAND() with a LIMIT on each category table would be faster since each category table will contain a subset of records from your main table.
Some other alternatives would be to use a key/value pair database just for that operation. MongoDb or Google AppEngine can help with that and are really fast.
You could also go towards the approach of a Master/Slave in your MySQL. The slave replicates content in real time but when you need to perform the expensive query you query the slave instead of the master, thus passing the load to a different machine.
Finally you could go with Sphinx which is a lot easier to install and maintain. You can then treat each of those category queries as a document search and let Sphinx randomize the results. This way you offset this expensive operation to a different layer and let MySQL continue with other operations.
Just some issues to consider.
Working off your random number approach
Get the max id in the database.
Create a temp table to store your matches.
Loop n times doing the following
Generate a random number between 1 and maxId
Get the first record with a record Id greater than the random number and insert it into your temp table
Your temp table now contains your random results.
Or you could dynamically generate sql with a union to do the query in one step.
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
Note: my sql may not be valid, as I'm not a mySql guy, but the theory should be sound
First you need to get number of rows ... something like this
select count(1) from tbl where category = ?
then select a random number
$offset = rand(1,$rowsNum);
and select a row with offset
select * FROM tbl LIMIT $offset, 1
in this way you avoid missing ids. The only problem is you need to run second query several times. Union may help in this case.
For MySQl you can use
RAND()
SELECT column FROM table
ORDER BY RAND()
LIMIT 4

How to count the number of rows before a given row in MySQL with CodeIgniter?

Simply put, how can I count how many rows there are before a certain row. I'm using incremental ID's, but rows are deleted a random, so just checking to see what the ID is won't work.
If I have, say, 30 rows, and I've selected one based on a name (or anything really), how many rows are there before that one? It could be 16, 1, 12, or anything.
I'm using MySQL and CodeIgniter.
I assume your primary key column has datatype integer
SELECT COUNT(*) FROM `table`
WHERE id < (SELECT id FROM `table` WHERE `conditions are met for specific row`)
Assuming it's an auto_increment column, deleted rows won't be filled in again so this should do the job.
SELECT COUNT(*) FROM table WHERE id_column < your_selected_row_id;
On your model:
$id = $this->db->get('users')->where("name", "John")->id;
$rows = $this->db->get('users')->where("id < ", $id)->num_rows();
return $rows;
Notice how I'm using "chained methods" and for that you need PHP5 which is the default for CI 2.
You first need to get the ID of the record you need to start counting "backwards" which is the first line, considering a table called users and that the column you are filtering is "name" and the row you want to find has the name value of John.
The second line will give you the number of rows that the query "where id < number" returned where number is the ID you got from the first query. Maybe you can even chain both lines.

Categories