2

I have a column

id
-----
1
32
3
6
5
22
54
21

Ques1: How can I select all records from the column excepting the first 3 records ?
Ques2: How can select last 3 records ?

-Thanks.

user1844626
  • 1,728
  • 7
  • 23
  • 35

4 Answers4

3

You basically need to put such queries into stored procedures due to some limitations on LIMIT. You can't use sub selects or variables in plain sql. In stored procedures you can use variables.

This works, unfortunately I can't show it in sqlfiddle cause they seem to have limited support for stored procedures.

drop procedure if exists all_but_3;

delimiter //
create procedure all_but_3()
begin
   declare v_max bigint unsigned default ~0;
   select * from your_table limit 3, v_max;
end//

delimiter ;

drop procedure if exists last_3;
delimiter //
create procedure last_3()
begin
   declare v_max bigint;
   declare v_mid bigint;
   select count(*) from your_table into v_max;
   set v_mid := v_max - 3;
   select * from your_table limit v_mid, v_max;
end//

delimiter ;

call all_but_3();
call last_3();

Elaboration on InnoDB Clustered Indexes

After discussions in one of the other answers with @fthiella I've decided to elaborate some on how this can work.

A table using InnoDB as engine will always have a clustered index. Always. It's they way data is stored in InnoDB and it's not in any way possible to create a table without a clustered index.

InnoDB will choose the primary key if there is one or the first unique index with all columns set to not null. If no such index exists InnoDB will create a hidden column with a row id. This row id works similar to auto increment and if it helps to think about it as a invisible column with auto increment I think that is fine.

Further will InnoDB return rows according to the index used. It will always use some index (only way to retrieve data is to use either a secondary index, the clustered index or a combination) so in the case where there are no explicitly created indexes rows are returned by the hidden clustered index.

This means that a query against a table with no primary key and no unique indexes with all columns set to not null and no ORDER BY will return rows in the order they where inserted.

This is the case for this question and the base for mine and many other answers.

I don't mean to say this is a good way to work with the data. Here are some things that you should think about before using this solution:

  • If a index that can be used as a clustered index is ever created the table will be rewritten to use that index and by doing so order the data on disk. If the index is later dropped the original insert order is lost and cannot be retrieved.
  • If a index is created, even if it's not unique, it could be chosen by the optimizer to be used and the rows will be ordered by that index instead.

All this is documented and for 5.5 it's the 3rd bullet points on this page

Andreas Wederbrand
  • 33,680
  • 10
  • 58
  • 75
  • sorry I don't get it. there said "query ok, 0 rows affected". didn't get any result. would you please try this for my table ? I'm giving you the mysql code so it wouldn't take your time,just copy-paste. create table t(id int(3)); insert into t values('1'),('32'),('3'),('6'),('5'),('22'),('54'),('21'); – user1844626 Dec 29 '12 at 10:22
  • I've added the actual calls to the stored procedures. That's how you'll get the result. – Andreas Wederbrand Dec 29 '12 at 10:25
  • yes, your solution worked. please tell me is there any risk or high memory consumption or any other disadvantage of this solution ? – user1844626 Dec 29 '12 at 10:38
  • For the first question there is no negative impact. It isn't functionally any different from other answers. You should keep the procedures in the database and just do the `call procedure_name` from your code. For the second part it has the same limitations as other in that the count(*) part will be slow if the table is big. I don't know of any way to get rid of that part unless you have a index that you can traverse. If you post the complete DDL I might find a way. – Andreas Wederbrand Dec 29 '12 at 10:47
  • default ~0 >> 1, would you please explain this to me ? does this set infinite number or set a number like '18446744073709551615' in "SELECT id FROM table LIMIT 3,18446744073709551615" ? – user1844626 Dec 29 '12 at 10:48
  • ~0 is the biggest possible unsigned bigint number. It's actually more correct in this case (so I've changed). >> 1 is the biggest unsigned bigint. – Andreas Wederbrand Dec 29 '12 at 10:54
  • that means thats the highest number of rows ever that a mysql database can have ? – user1844626 Dec 29 '12 at 10:58
  • No, it just means the highest number LIMIT will accept. There is no limit on how many rows a table can hold. It is however unlikely to have a table with more rows than that :) – Andreas Wederbrand Dec 29 '12 at 11:03
  • hmm, so its logically a limitation but physically can cover a huge database table, so this may throw failure so rarely, right ? – user1844626 Dec 29 '12 at 11:08
  • Only you know the future of your tables but I haven't ever seen a table with millions x millions x millions of row.s – Andreas Wederbrand Dec 29 '12 at 11:14
  • yes, so I can use this without fear until mysql finds out a way to use subquery with LIMIT.so if theres no difference between ~0 and '18446744073709551615' then "SELECT bleh FROM blah LIMIT 3, 18446744073709551615" thiss may be better for the first query(cause just single line) but the second query highly requires procedure. thank you so much for your help. – user1844626 Dec 29 '12 at 11:30
3
  1. select all except the first 3 records:

    select id from table1 limit 3, 18446744073709551615;

  2. select the last 3 records:

    select a.id from (select @row_num:=@row_num+1 as RowNumber,id from table1, (select @row_num:=0) x order by RowNumber desc) as a limit 3;

tristan
  • 349
  • 2
  • 7
  • Not sure the performance implications, but I suppose it does get it done in 1 query... +1 – Corbin Dec 29 '12 at 09:39
  • That second query will create a temporary table, probably on disk, with all values from table1. If that table is even a little large it will have very bad performance and a very high I/O load on the system. – Andreas Wederbrand Dec 29 '12 at 09:48
  • Yeah I agree with Andreas partially. But I'm afraid the DB engine will be responsible for performance of executing subquery. On the other hand, how to get a solution for question 2 without generating temporary table? – tristan Dec 29 '12 at 09:57
  • Does MySQL now have ordered tables? How can you use `limit` without specifying an ordering, and have it mean *anything* at all? – ErikE Dec 29 '12 at 10:01
  • @ErikE You can't. It's basically depending on implementation detail. One of us answers probably should have mentioned that... But yeah.... :) – Corbin Dec 29 '12 at 10:34
  • In InnoDB the clustered index is ordered and if no other ORDER BY is used the results will be ordered by the clustered index of the first joined table. In this simple case (with only one table) it will be ordered by that tables clustered index. In this case it seems to be no explicit primary key or unique index on the table so InnoDB will create a hidden clustered index. It will basically return rows in insertion order. – Andreas Wederbrand Dec 29 '12 at 11:02
1

Use LIMIT


For #1, you theoretically need to know the number of rows, but you can just throw a huge number in instead:

SELECT bleh FROM blah LIMIT 3, 18446744073709551615

(18446744073709551615 is the max number possible -- thanks to alex vasi for pointing me to this)


For #2, you do actually need to know the number of records:

SELECT bleh FROM blah LIMIT NUM-3, 3

You'll have to get the number of rows from a separate query (which should be where NUM is).


If there's a criteria you can sort on, you can actually do the second query without doing a full count:

SELECT bleh FROM blah ORDER BY field DESC LIMIT 3

I'm assuming that's not the case though.


(Another option if you don't want to do a count on the table is to just pull all of the data and ignore the first or last 3 rows in code-land [PHP, C, etc].)

Community
  • 1
  • 1
Corbin
  • 31,183
  • 6
  • 65
  • 77
  • your first query is incorrect, which will result in more number of rows – Sashi Kant Dec 29 '12 at 09:06
  • @SashiKant That will be all but the first three, provided that his table has <= 2 billion rows in it. And I really doubt his table has 2 billion rows in it. It could of course have NUM-3 again, but this way saves doing a count. – Corbin Dec 29 '12 at 09:07
  • For the first 3 rows `LIMIT 3` will do the good, but OP here needs the last 3 records, in which neither of your query is fetching any – Sashi Kant Dec 29 '12 at 09:09
  • @SashiKant Ah whoops. You're right; my second query is wrong. One second. – Corbin Dec 29 '12 at 09:09
  • 2
    in the first query if the record number is greater than 2000000000 then it wouldn't work.how can I select the records if the number of total records is unknown ? in second query what is NUM-3,would you please explain ? – user1844626 Dec 29 '12 at 09:09
  • @user1844626: If you need the last 3 records try :: `SELECT bleh FROM blah LIMIT NUM-3, 3` where NUM is the number of records in the table – Sashi Kant Dec 29 '12 at 09:11
  • Fixed my second query now. And @user1844626 If the number of rows is greater than 2 bil, you'll either need to use a larger number, or just do a COUNT and figure out the actual number. (Oh, and NUM is meant to be the number of rows. You'll have to either use a MySQL variable (maybe -- not sure if that's valid; it's probably not), or get it in the language you're using an interpolate it into the query.) – Corbin Dec 29 '12 at 09:12
  • @Corbin: Please remove your first query, so that I can remove my down vote – Sashi Kant Dec 29 '12 at 09:13
  • 1
    @SashiKant Other than the potential case where there's more than 2 billion rows, what is wrong with the first query? – Corbin Dec 29 '12 at 09:14
  • can I use "select count(*) from table" in the first query to set limit ? if so,then how ? – user1844626 Dec 29 '12 at 09:15
  • @user1844626 You can use that to get the count, but you cannot use that inline. It will have to be done in a separate statement. – Corbin Dec 29 '12 at 09:16
  • Your first query will result in from the fourth rows to the end of the table, and I think this is not what the OP wants, he needs the last 3 records, your second query is fine – Sashi Kant Dec 29 '12 at 09:17
  • @SashiKant It will result in skipping rows 0, 1 and 2 (or 1, 2 and 3 if they're 1-indexed) and selecting the rest of the table. That's what he asked for: "How can I select all records from the column excepting the first 3 records ? " – Corbin Dec 29 '12 at 09:18
  • Your third query is again erroneous, It will return MAX 3 ids, which again the OP doesn't want, Please read what the OP needs – Sashi Kant Dec 29 '12 at 09:18
  • not the 3rd query.I need the records as the same ordered as they are now.(neither ascending nor descending) – user1844626 Dec 29 '12 at 09:21
  • @user1844626 "If there's a criteria you can sort on" :). I figured there wasn't a sort possible based on your sample data, but worth a try. A row count may be expensive on large tables. – Corbin Dec 29 '12 at 09:23
  • @Corbin, please, take a look at this answer: http://stackoverflow.com/questions/255517/mysql-offset-infinite-rows/271650#271650 – alex vasi Dec 29 '12 at 09:25
  • @alexvasi Ah thanks. Was too lazy to look up the maximum myself :). Figured it would be huge, but wow! – Corbin Dec 29 '12 at 09:26
  • ok, heres only the sample table I gave but I need the query when the total number of records are unknown and I need (1)all other records by cutting first 3 records and (2)the last 3 records. my brain storming here but can't figure it out. – user1844626 Dec 29 '12 at 09:26
  • @user1844626 As I said before, you'll have to figure out what `NUM` should. Something vaguely like `$numStmt = $db->query("SELECT COUNT(*) FROM table"); $numRow = $numStmt->fetch(PDO::FETCH_NUM); $num = $numRow[0]; $stmt = $db->query("SELECT bleh FROM blah LIMIT 3, {$num}");`. That would cut off the first three rows. – Corbin Dec 29 '12 at 09:30
  • if I used php with this then its so easy but I'm trying in mysql only.by the way is "18446744073709551615" used in here as infinite ? is there any disadvantage of this ? – user1844626 Dec 29 '12 at 09:34
  • @user1844626 It's the maximum number that the parser will accept. And if you're using MySQL alone, you can just do a count and copy/paste the output. Since `LIMIT` requires constants, there's not much else you can do. `SELECT ... FROM ... LIMIT (SELECT COUNT(*) FROM ...) - 3, 3` is not valid syntax. – Corbin Dec 29 '12 at 09:35
  • thats not valid, but tell me if theres any solution for this even if can do by mysql function or anything else. – user1844626 Dec 29 '12 at 09:36
  • @user1844626 It's simply not possible to do without using a literal number. The grammar of SQL doesn't allow for it. – Corbin Dec 29 '12 at 09:38
  • @user1844626 More specifically, it's not possible in the way I presented. As tristan's answer illustrates, it is possible. It just requires some hackery. – Corbin Dec 29 '12 at 09:39
  • hmm, looks like a simple question but the depth is about to beyond mysql !! I'm not giving up, please keep in touch of this question. if you find any solution then please try to share and if I could find solution then I'll post in here too. – user1844626 Dec 29 '12 at 09:41
  • @user1844626 The problem is that relational databases don't really think in terms of row position mattering. It's a weird question (in the DB's opinion) to ask a RDBMS is what it comes down to :). – Corbin Dec 29 '12 at 09:42
  • I see but I want to see the end :) – user1844626 Dec 29 '12 at 09:46
  • See my answer about solving this in functions/stored procedures. It's cleaner but basically the same as other answers. Regarding the second question I'm not sure it's possible to do in a clean efficient way. – Andreas Wederbrand Dec 29 '12 at 10:08
  • Please consider taking this discussion to [chat]! – jtbandes Dec 29 '12 at 10:19
  • thank you for the first query. if there was option to accept half answer then I could do that :) but for now had to make upvote. – user1844626 Dec 29 '12 at 11:32
-1

Try this:

totalCounts = SELECT COUNT(*) FROM tbl;

SELECT * FROM tbl LIMIT 3,totalCounts
# Retrieve rows 4 to number of table rows
suresh.g
  • 3,030
  • 5
  • 24
  • 58