I'm doing an info system that has announcements, so I wanted to get the data from announce table, first should be the one with max id of course since it is the latest announcement and here is my code for it:
SELECT image FROM announce where id=(SELECT max(id) FROM announce);
Now I wanted to get the data before the max id and I'm using this code here:
SELECT image FROM announce where id=(SELECT max(id)-1 FROM announce);
But it only works if there are no deleted rows,if there are some deleted rows it doesn't work like max id is 10 and the id's 9,8,7 are deleted. That means the present id's are: 1,2,3,4,5,6,10, how will I be able to get the data in the id 6,5,4,3,2,1?
If you ORDER the query by descending order (DESC), and have an OFFSET of one (OFFSET 1), you would get the second to last row. And offset of two would give the row before that, and so on. This also uses LIMIT 1, so we just get one row. LIMIT 1, 1 is the same as LIMIT 1 OFFSET 1.
SELECT image
FROM announce
ORDER BY id DESC
LIMIT 1, 1
MySQL on SELECT statements
You can just use one query with order by and limit instead of the two queries you use:
select image
from announce
order by id desc
limit 2
In case you want to query them separately, you can change limit 2 to limit 1, 1 for you second query to work as you wish.
Related
I have this MariaDB table:
id, name
The id column has these attributes: Primary, auto_increment, unique.
The table has 40,000 rows.
I'm using this PHP & MariaDB to load rows from this table.
This is the PHP code:
$get_rows = $conn->prepare("SELECT * FROM my_table where id> 0 ORDER BY id ASC LIMIT 30 OFFSET ?");
$get_rows->bind_param('i', $offset);
//etc.
The query returned everything correctly at the first time, but in the next query (made through AJAX), I received the next 30 rows with a gap of one row between the current result and the next one. And this goes on and on.
In the table, the row #1 had been deleted. So, I restored it, and now the query works. However, I will definitely have to delete more rows in the future. (I don't have the option of soft-deleting).
Is there any way I can keep deleting rows, and have these queries return correct results (without skipping any row)?
EDIT
Here's an example of the range of the ids in the first 2 queries:
Query 1:
247--276
Query 2:
278--307
(277 is missing)
NB I asked ChatGPT, but it couldn't help. :')
LIMIT and OFFSET query rows by position, not by value. So if you deleted a row in the first "page," then the position of all subsequent rows moves down by one.
One solution to ensure you don't miss a row is to define pages by the greatest id value on the preceding page, instead of by the offset.
$get_rows = $conn->prepare("
SELECT * FROM my_table where id> ?
ORDER BY id ASC LIMIT 30");
$get_rows->bind_param('i', $lastId);
This only works if your previous query viewed the preceding page, so you can save the value of the last id in that page.
I have a query that fetches a set of records as shown:
select id
from wallactions
where status=1 and created > DATE_ADD(NOW(),INTERVAL -1 DAY)
order by id desc LIMIT $priorRecordsCnt
The LIMIT variable $priorRecordsCnt changes often and can grow very large. It's necessary because i need to know the last id in that set of records based on the $priorRecordsCnt count value.
The problem is, I need to only access the last id in this set, as shown:
$last=array_values(array_slice($result,-1))[0]['id'];
I feel like this is pretty expensive to just get the id of the last record in the set.
Is there a way to optimize this query so it uses the count variable $priorRecordsCnt but i don't need to fetch all the records just to obtain the last value?
The limit clause takes two arguments - an optional offset and the row count. Instead of using $priorRecordsCnt as the record count, you should use it as the offset, and limit the record count to 1:
select id
from wallactions
where status=1 and created > DATE_ADD(NOW(),INTERVAL -1 DAY)
order by id desc LIMIT $priorRecordsCnt, 1
-- Here ---------------------------------^
I have a table with 8,000 Records, I want to select randomly some records (for example 10) from this table. These records have to be different from previously selected records.
For example with this query I select some questions:
SELECT coloumn1,column2 FROM `myTable` WHERE `status`=1 AND `group`=6 ORDER BY RAND() LIMIT 0, :max
Now how can I select new records randomly and different from previously selected records?
Simply you could store the id's of previously selected records and then add to your next query like below, to avoid selecting them twice:
WHERE id NOT IN = (list of ids)
Based on you'r example:
SELECT coloumn1,column2 FROM `myTable` WHERE `status`=1 AND `group`=6 AND id NOT IN (_IDS_) ORDER BY RAND() LIMIT 0
try :
SELECT coloumn1, column2 FROM myTable WHERE status=1 AND group=6
AND coloumn1 NOT IN (id,id,id,id,ids concatenated in php)
ORDER BY RAND() LIMIT 0, :max
and also in select, better select a column that is unique identifier for the row, and use that column with the NOT IN ()
avoid RAND() as its extremely slow - better work around it with php
If the number of selected records is too big, put them in a temp table, and use
NOT IN (SELECT id FROM temp table...)
So, I'm writing an application to fetch records from the database, and my client has asked if I could sort the data by area. I've got the following query:
SELECT
*
FROM
customers
WHERE
account_type != 'x'
AND
account_type != 'tor'
ORDER BY
area ASC,
id ASC
LIMIT 30
The query returns 30 rows worth of data, all with an area value of 1.
I've got an AJAX query set up to fetch another 30 rows, however as I am ordering the data by area, I am unable to just fit a simple range string to my query as shown in the example below:
...
AND
id > 30
...
How would I write a query that would collect the next 30 records from the database?
Also, there is a record with an area value of 0. This is not shown as the first record in the list. Why is this? And how can I fix this?
Thanks
First, combine the where conditions into a single not in:
SELECT c.*
FROM customers c
WHERE account_type NOT IN ('x', 'tor')
ORDER BY area ASC, id ASC
LIMIT 30;
Then, for the next set, use:
SELECT . . .
OFFSET 30
LIMIT 30
This assumes that id is unique in the table. That is important, because that makes the sort stable.
Have a look at the the SELECT query documentation:
https://dev.mysql.com/doc/refman/5.7/en/select.html
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
In summary, just limit returns you x records. With the offset you can set a starting position, basically allows you for paging.
MySQL LIMIT take an optional parameter offset, so you could send in a parameter telling where to start when picking the 30 results you want
SELECT
*
FROM
customers
WHERE
account_type != 'x'
AND
account_type != 'tor'
ORDER BY
area ASC,
id ASC
LIMIT 30, 30
Should give you the next 30 rows starting from row 30
I'm trying to make an image database which does not keep a consistent record of ID's. For example it might go 1,2,6,7,12, but as you can see that is only 5 rows.
Inside the table I have fileid and filename. I created a PHP script to show me the image when I give the fileid. But if I give it the ID 5 which does not exist I get an error. That's fine as I want an error for that, but not for users who will browse through these images using forward and back buttons. The forward and back buttons would need to retrieve the true fileid which comes after the given ID. Hopefully that makes sense.
This is how I imagine the code to look like:
SELECT offset( WHERE fileid=4 )
That would give me the offset of the row where fileid is equal to 4. I think this is easy enough to understand. The reasons I need this are for creating the forward and back button. So I planned to add 1 or take 1 from the offset which gives me the new ID, and the new filename. That way when users browse it will skip the dead ID values automatically, but it will give an error when giving a false ID.
Going up:
SELECT * FROM table WHERE id > 'your_current_id' ORDER BY id LIMIT 1;
Going down:
SELECT * FROM table WHERE id < 'your_current_id' ORDER BY id DESC LIMIT 1;
ps: it is better to make LIMIT 2, so that you can see that you are at the first or at the last records in the database when only one record is returned.
If your results are ordered by x, ascending, the following will give you your current offeset in the table:
SELECT COUNT(*) FROM tablename WHERE x < x_of_your_current_item;
If you just want to SELECT the next or previous row, you can skip having to do two queries by just directly selecting one row:
SELECT * FROM tablename WHERE x > x_of_your_current_item ORDER BY x LIMIT 1;
will give you the next item (and similarly < and adding DESC to the order-by would give you previous).
You can use offset. Initially set offset as zero.
First time your query will be
SELECT * FROM TABLE order by table_id LIMIT 0,1
and next
SELECT * FROM TABLE order by table_id LIMIT 1,1
..
and so on
This way one will get records from the beginning till the end.
Now about back and forward buttons.
Back Button: First time or whenever offset is zero disable back button
Forward Button:
when you query for a current record you check for the next record too
i.e. after this query
SELECT * FROM TABLE order by table_id LIMIT 0,1 fire a query like this
SELECT * FROM TABLE order by table_id LIMIT current_offset+1,1 and check if the query produces any results if it produces a result then set a boolean say next = TRUE else next = FALSE;
Using this boolean enable or disable Forward button.
One more thing on click of back button send the offset as current_offset - 1 and for forward button current_offset + 1
I hope this helps. I just came across this and thought of this solution.
SELECT * FROM table WHERE ... ORDER BY id DESC LIMIT 50,10;
SELECT * FROM table WHERE ... ORDER BY id DESC LIMIT 60,10;
SELECT * FROM table WHERE ... ORDER BY id DESC LIMIT 70,10;