![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Oh, mightly LJ brain, riddle me this:
I have a table in a MySQL (well, okay, it's Sqlite3 right now, but it'll my MySQL in production) database, and the primary keys are known to not be contiguous. That's important.
It's possible to construct a WHERE clause giving you a single item, if that items qualties are unique enough. Is it possible, using some magic combination of WHERE and ORDER BY and something I don't know, to say "give me unique item N, and using the rule of the ORDER BY clause, also give me the next and previous objects?"
I have a table in a MySQL (well, okay, it's Sqlite3 right now, but it'll my MySQL in production) database, and the primary keys are known to not be contiguous. That's important.
It's possible to construct a WHERE clause giving you a single item, if that items qualties are unique enough. Is it possible, using some magic combination of WHERE and ORDER BY and something I don't know, to say "give me unique item N, and using the rule of the ORDER BY clause, also give me the next and previous objects?"
no subject
Date: 2009-09-30 07:53 pm (UTC)If you are willing to load the next and previous objects in dedicated queries, you can use something like:
select *
from table
where id > ?
order by id
limit 1
no subject
Date: 2009-09-30 07:54 pm (UTC)SELECT * from foo WHERE myfield >= N ORDER BY myfield LIMIT 2;
SELECT * from foo WHERE myfield < N ORDER BY myfield DESC LIMIT 1;
no subject
Date: 2009-09-30 07:55 pm (UTC)no subject
Date: 2009-09-30 08:16 pm (UTC)Here it is.
Date: 2009-09-30 08:25 pm (UTC)I took a minute to demonstrate this in sqlite, because I have sqlite on this machine I'm posting from. Similar principle probably works in mysql.
Example:
sqlite> select key from pageviews where key in (select key from pageviews where
key > 103 ORDER BY KEY LIMIT 1) or key in (select key from pageviews where key <
= 103 ORDER BY key DESC LIMIT 2);
104
102
103
I hope you don't mind the two subqueries inside the WHERE clause, but it does work.
-- Cat Typist
Oops, I only did half the requested query.
Date: 2009-10-01 10:28 pm (UTC)whereas you want the desired row's key to be discovered within the same query.
Now ALL IN ONE QUERY! Also, pretty comical IMHO.
Date: 2009-10-01 10:49 pm (UTC)but the efficiency is somewhat questionable. Also, it's like
a code comedy.
=========
sqlite> -- The key *directly* sought (without the next or prev rows)
sqlite> select key from pageviews where url like '%elf%' and key
...> > 10000
...> order by key asc limit 1;
10198
sqlite> -- Finds the key *directly* sought, also prev and next existing keys.
sqlite> select key from pageviews
...> where key in
...> (select key from pageviews where url like '%elf%' and key >10000
...> ORDER BY key ASC LIMIT 1)
...> -- find prev key
...> or key =
...> (select MAX(key) from pageviews where
...> key <
...> (select key from pageviews where url like '%elf%' and key >10000
...> ORDER BY KEY ASC LIMIT 1)
...> ORDER BY key DESC LIMIT 1)
...> -- find next key
...> or key =
...> (select MIN(key) from pageviews where
...> key >
...> (select key from pageviews where url like '%elf%' and key >10000
...> ORDER BY KEY ASC LIMIT 1)
...> ORDER BY key DESC LIMIT 1)
...> ;
10198
10197
10199
sqlite>
=========
--Cat Typist
Re: Now ALL IN ONE QUERY! Also, pretty comical IMHO.
Date: 2009-10-01 10:51 pm (UTC)Here it is in a more copy-and-pasteable form
Re: Now ALL IN ONE QUERY! Also, pretty comical IMHO.
Date: 2009-10-01 10:58 pm (UTC)This works too, but it's a little less funny, IMHO.
no subject
Date: 2009-09-30 11:37 pm (UTC)Basically:
It's constant time if you have btree indexes for the primary key. Don't use a hash index, even though it might make otherwise sense for non-contiguous data. The MySQL compiler should realize that this is a simple btree lookup, and not even bother scanning. You *can* combine the two aggregate functions into a single query using a GROUP BY clause on a calculated field...but don't. It's horribly inefficient for large tables. Just use the separate queries above, which are optimized into simple index lookups:
If you want it all in a single DB call, use a stored procedure -- then you can just call the stored procedure with the itemid in question (this is what I recommend):
Hope this helps somewhat.
(I actually tested all of the above code, using a test items table that had an INT "id" field and a "content" field, with the first 10 prime numbers as the id's and the first ten letters as the content, on MySQL 5.0.)
no subject
Date: 2009-09-30 11:53 pm (UTC)select * from items where id >= (select max(id) from items where id < N) limit 3
The subselect is (as above) is optimized away to a constant-time index lookup, and the item retrieval is done by walking along the index (signified by the type of "range"), making it also constant-time:
(I say "constant time"...btree lookups are actually O(logN), but with a *very* high logarithmic base. Also, the entire index is usually in memory, so it's blazingly fast even for millions of rows in the table.)
+1 upvote :-)
Date: 2009-10-01 11:15 pm (UTC)Item N, N+-1
Date: 2009-10-01 03:44 pm (UTC)sqlite> insert into t values(1);
sqlite> insert into t values(2);
sqlite> insert into t values(3);
sqlite> insert into t values(4);
sqlite> insert into t values(6);
sqlite> insert into t values(7);
sqlite> insert into t values(10);
sqlite> insert into t values(11);
sqlite> select b.i,count(*) from t a join t b on a.i<b.i group by b.i;
i|count(*)
2|1
3|2
4|3
6|4
7|5
10|6
11|7
sqlite> select * from (select b.i,count(*) as N from t a join t b on a.i<b.i group by b.i) x where x.N=3;
i|N
4|3
Now ask for N+1 and N-1.
Re: Item N, N+-1
Date: 2009-10-01 03:46 pm (UTC)Re: Item N, N+-1
Date: 2009-10-01 11:20 pm (UTC)Re: Item N, N+-1
Date: 2009-10-02 11:39 am (UTC)Note also that it's horribly inefficient, requiring an O(N2) calculation on the entire index to get the position field, and then -- here's the really horrible part -- scanning the entire derived table for specific values, which is unindexed. I strongly recommend against this approach. Especially because you typically want to find "next" and "prev" id's for forward and back links, which means on *every* page view of a potentially large corpus.
Re: Item N, N+-1
Date: 2009-10-04 09:26 am (UTC)No derived table necessary, as stated above.
no subject
Date: 2009-10-01 05:29 pm (UTC)