First off, LIMIT
/ OFFSET
without ORDER BY
are of limited usefulness, since the order is arbitrary and can change any time (when VACUUM
runs or when the table is manipulated in at any way). It is only somewhat reliable with read-only tables. That's fine if you don't care which rows you get back, but it may break paging.
You may be able to solve your conundrum with the good old EXISTS
. PostgreSQL can stop searching for more hits as soon as the first is found - as opposed to your query with DISTINCT
, where it tries to collect all matches.
It's hard to be more specific without knowing the table structure, cardinalities, index definitions and what's behind your peculiar WHERE
expression. But this might just do it:
SELECT s.chem_compound_id AS chem1_0_
-- 10 more columns all short varchar or date fields
FROM simple_compound s
WHERE EXISTS (
SELECT 1
FROM compound_composition sc
JOIN chemical_structure c ON sc.chemical_structure_id = c.structure_id
WHERE c.structure_id @ ('CCNc1ccccc1', '')::bingo.sub
AND sc.chem_compound_id = s.chem_compound_id
)
LIMIT 5
OFFSET 5
This also assumes you are only interested in columns from simple_compound
in the output.
If we can make the assumption that the unnest()
operation will always return a set where a member "id" is listed followed by its role in a single column of text
entries, then I've written up a quick solution for you.
I made a quick data set as:
CREATE TABLE mylist (entry text);
INSERT INTO mylist VALUES
('n240045827'),
('admin_centre'),
('w185481610'),
('outer'),
('w191987461'),
('outer'),
('w166286278'),
('outer'),
('w166290573'),
('outer'),
('w65481875'),
('outer'),
('w166278211'),
('outer'),
('w166278224'),
('outer'),
('w166278225'),
('outer'),
('w166278227'),
('outer'),
('w185148605'),
('outer');
Assuming we can return this, then the script
WITH numbered AS(
SELECT row_number() OVER() AS row, entry
FROM mylist)
SELECT a.entry
FROM numbered AS a JOIN numbered AS b
ON a.row = b.row-1 AND b.entry = 'admin_centre';
will return only n240045827
.
How does it work? Well, I just took a straightforward approach to assigning order to the returned list by using row_number() OVER()
. In this example, I've used a CTE WITH...
to add the row after the fact, but in your case you could add it into your query like so:
...
SELECT ltrim(member, 'n')::bigint AS osm_id
FROM (
SELECT row_number() OVER() a AS row, unnest(members) AS member
FROM planet_osm_rels
...
Finally, I simply join this result to itself, comparing one row to the next by its row number, and looking for the 'admin_centre'
text entry.
Here's a SQL Fiddle for testing.
For your perusal:
Common Table Expressions
Window Functions
Edit
If you want to return all rows as column pairs, then use the following approach:
WITH numbered AS(
SELECT row_number() OVER() AS row, entry
FROM mylist)
SELECT a.entry, b.entry
FROM numbered AS a JOIN numbered AS b
ON a.row = b.row-1 AND a.row%2 = 1;
Also, here's a SQL Fiddle #2 for testing.
Best Answer
You need to remove
user_id
from your group by clause. But that requires putting it into an aggregate function in the SELECT list. One way to do that is to usestring_agg()
Online example