Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-29 Thread Merlin Moncure
Just an update on this:
queries in the 'next key' form on fields(a,b,c)
only ever use the index for the first field (a).  I just never noticed
that before...in most cases this is selective enough.  In most logical
cases in multi part keys the most important stuff comes first.

Curiously, queries in the Boolean reversed form (switching and with or,
etc.) find the index but do not apply any condition, kind of like an
indexed sequential scan...

Well, if and when the rowtype comparison can be made to work over multi
part keys (and the optimizer is made to do tricks there), postgres can
be made to give much better generalized ISAM access.  In the meantime,
I'll just troubleshoot specific cases via application specific behavior
as they come up.  In any case, many thanks to Greg and Tom for taking
the time to pick this apart.

Merlin

---(end of broadcast)---
TIP 4: Don't 'kill -9' the postmaster


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-29 Thread Greg Stark

Merlin Moncure [EMAIL PROTECTED] writes:

 Well, if and when the rowtype comparison can be made to work over multi
 part keys (and the optimizer is made to do tricks there), postgres can
 be made to give much better generalized ISAM access.  In the meantime,
 I'll just troubleshoot specific cases via application specific behavior
 as they come up.  In any case, many thanks to Greg and Tom for taking
 the time to pick this apart.

Well I'm not sure whether you caught it, but Tom did come up with a
work-around that works with the current infrastructure if all the columns
involved are the same datatype.

You can create a regular btree index on the expression array[a,b,c] and then
do your lookup using array[a,b,c]  array[a1,b1,c1].

This will only work in 7.4, not previous releases, for several reasons.

-- 
greg


---(end of broadcast)---
TIP 4: Don't 'kill -9' the postmaster


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-29 Thread Merlin Moncure
Greg Stark wrote:
 Well I'm not sure whether you caught it, but Tom did come up with a
 work-around that works with the current infrastructure if all the
columns
 involved are the same datatype.
 
 You can create a regular btree index on the expression array[a,b,c]
and
 then
 do your lookup using array[a,b,c]  array[a1,b1,c1].

Unfortunately, ISAM files allow keys based on combinations of fields on
any type.  So this is not an option. (I have spent over 6 months
researching this problem).

However, this would work:
Create index on t(stackparam(array[a::text,b::text,c::text),
array['char(2)', 'int', 'date')];

With the 'type strings' queried out in advance.  stackparam(text[],
text[]) is a C function with uses the types and cats the strings
together in such a way that preserves sorting.  In any case, this is an
ugly and inefficient mess, and I have no desire to do this unless there
is no other way.  I would much rather see postgres 'get' (a,b,c)  (a1,
b1, c1)...if there is even a chance this is possible, I'll direct my
efforts there.  IMNSHO, this form was invented by the SQL folks for
dealing with data in an ISAM manner, postgres should be able do it and
do it well.

Merlin

---(end of broadcast)---
TIP 7: don't forget to increase your free space map settings


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-29 Thread Tom Lane
Merlin Moncure [EMAIL PROTECTED] writes:
 I would much rather see postgres 'get' (a,b,c)  (a1,
 b1, c1)...if there is even a chance this is possible, I'll direct my
 efforts there.

For the ISAM context this whole discussion is kinda moot, because you
really don't want to have to plan and execute a fairly expensive query
for every record fetch.  If we had all the improvements discussed, it
would be a reasonably cheap operation by the standards of execute
an independent query, but by the standards of fetch next record
in my query it'll still suck.  (Using PREPARE might help, but only
somewhat.)

It strikes me that what you really want for ISAM is to improve the
cursor mechanism so it will do the things you need.  I'm not sure
what's involved, but let's talk about that angle for a bit.

regards, tom lane

---(end of broadcast)---
TIP 8: explain analyze is your friend


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-29 Thread Merlin Moncure
 Merlin Moncure [EMAIL PROTECTED] writes:
  I would much rather see postgres 'get' (a,b,c)  (a1,
  b1, c1)...if there is even a chance this is possible, I'll direct my
  efforts there.
 
 For the ISAM context this whole discussion is kinda moot, because you
 really don't want to have to plan and execute a fairly expensive query
 for every record fetch.  If we had all the improvements discussed, it
 would be a reasonably cheap operation by the standards of execute
 an independent query, but by the standards of fetch next record
 in my query it'll still suck.  (Using PREPARE might help, but only
 somewhat.)
 
 It strikes me that what you really want for ISAM is to improve the
 cursor mechanism so it will do the things you need.  I'm not sure
 what's involved, but let's talk about that angle for a bit.

Sorry for the long post, but here's an explanation of why I think things
are better off where they are.

I've created a middleware that translates ISAM file I/O on the fly to
SQL and uses prepared statements over parse/bind to execute them.  This
is why I was so insistent against scoping prepared statement lifetime to
transaction level.  
Cursors seem attractive at first but they are a decidedly mixed bag.
First of all, PostgreSQL cursors are insensitive, which absolutely
precludes their use.  Supposing they weren't though, I'm not so sure I'd
use them if it was possible to do the same things via vanilla queries.  

It turns out that prepared statements get the job done.  Moving them to
parse/bind got me a 30% drop in server cpu time and statement execution
times run between .1 and .5 ms for random reads.  Sequential reads go
from that fast to slower depending on index performance.  So, I don't
have performance issues except where the index doesn't deliver.  

2000-1 reads/sec is competitive with commercial ISAM filesystems on
the pc (assuming application is not running on the server), and it is
far better than any other commercial ISAM emulation I've played with up
to this point.  Don't have concrete #s, but the server can deliver 2-3
times that in concurrency situations, in many cases the application
performance is actually network bound...this is all on pc hardware.  Of
course, mainframes can do much, much better than this but that's really
outside the scope of what I'm trying to do.

So, things run pretty fast as they are, and keeping things running
through queries keeps things generic and flexible.  Also, queries
consume zero resources on the server except for the time it takes to
process and deal with them.  Cursors, OTOH, have to be opened up for
every table for every user.  Cursors are read-only always, whereas views
can be updated with rules.  It's worth noting that other commercial ISAM
emulation systems (for example Acu4GL by AcuCorp) cut queries on the fly
as well, even when cursor options are available.

If cursors became sensitive, they would be worth consideration.  I've
never really had 1000 large ones open at once, be interesting to see how
that worked.  In ideal object would be a 'pseudo cursor', an insensitive
cursor shared by multiple users with each having their own record
pointer.  This might be better handled via middleware though (this might
also give better options handling different locking scenarios).

Merlin

---(end of broadcast)---
TIP 7: don't forget to increase your free space map settings


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Greg Stark

Stephan Szabo [EMAIL PROTECTED] writes:

 Given the comment on make_row_op,
   /*
* XXX it's really wrong to generate a simple AND combination for  =
*  =.  We probably need to invent a new runtime node type to handle
* those correctly.  For the moment, though, keep on doing this ...
*/
 I'd expect it'd be accepted.


Hm, this code is new. As of version 1.169 2004/04/18 it only accepted = and
 operators:

/* Combining operators other than =/ is dubious... */
if (row_length != 1 
strcmp(opname, =) != 0 
strcmp(opname, ) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  errmsg(row comparison cannot use operator %s,
 opname)));


I think perhaps it's a bad idea to be introducing support for standard syntax
until we can support the correct semantics. It will only mislead people and
create backwards-compatibility headaches when we fix it to work properly.

Removing ,=,,= would be trivial. Patch (untested):

--- parse_expr.c.~1.174.~   2004-07-28 01:01:12.0 -0400
+++ parse_expr.c2004-07-28 01:52:29.0 -0400
@@ -1695,11 +1695,7 @@
 */
oprname = strVal(llast(opname));
 
-   if ((strcmp(oprname, =) == 0) ||
-   (strcmp(oprname, ) == 0) ||
-   (strcmp(oprname, =) == 0) ||
-   (strcmp(oprname, ) == 0) ||
-   (strcmp(oprname, =) == 0))
+   if (strcmp(oprname, =) == 0)
{
boolop = AND_EXPR;
}


Fixing it to write out complex boolean expressions wouldn't be too hard, but
I'm not clear it would be worth it, since I suspect the end result would be as
the comment indicates, to introduce a new runtime node.

-- 
greg


---(end of broadcast)---
TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Greg Stark

Greg Stark [EMAIL PROTECTED] writes:

 Fixing it to write out complex boolean expressions wouldn't be too hard, but
 I'm not clear it would be worth it, since I suspect the end result would be as
 the comment indicates, to introduce a new runtime node.

Just to prove that it isn't really all that hard, I took a stab at doing this.
This is basically only my second attempt to write even trivial bits of server
backend code so I certainly don't suggest anyone try using this code.

In fact it doesn't quite compile, because I have a bit of confusion between
the char *oprname and List *opname variables. Now I could clear that up, but
I'm missing one piece of the puzzle. To make it work I do need a way to
construct a List *opname from  or = and I don't know how to do that.

I think that's all I'm missing, but perhaps in the morning I'll look at this
code and wonder what was I thinking?!


This approach won't get the optimizer to actually use an index for these
comparisons, but it will fix the semantics to match the spec. Later we can
either improve the optimizer to detect expressions like this (which I think
would be cooler since some users may write them by hand and not use the
row-expression approach, but I don't see how to do it), or introduce a new
run-time node and have the optimizer handle it. But at least we won't have to
worry about backwards-compatibility issues with the semantics changing.

Oh, I tried to stick to the style, but sometimes I couldn't help myself. I
suppose I would have to fix up the style the rest of the way if I got it
working and you wanted a patch to apply.


/*
 * Transform a row op row construct
 */
static Node *
make_row_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
{
Node   *result = NULL;
RowExpr*lrow,
   *rrow;
List   *largs,
   *rargs;
char   *oprname;

/* Inputs are untransformed RowExprs */
lrow = (RowExpr *) transformExpr(pstate, ltree);
rrow = (RowExpr *) transformExpr(pstate, rtree);
Assert(IsA(lrow, RowExpr));
Assert(IsA(rrow, RowExpr));
largs = lrow-args;
rargs = rrow-args;

if (list_length(largs) != list_length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
 errmsg(unequal number of entries in row expression)));

oprname = strVal(llast(opname));

if (strcmp(oprname, =) == 0) 
{
result = make_row_op_simple(pstate, =, largs, rargs);
}

else if (strcmp(oprname, ) == 0) 
{
result = make_row_op_simple(pstate, , largs, rargs);
}

else if ((strcmp(oprname, ) == 0) ||
 (strcmp(oprname, ) == 0)) 
{
result = make_row_op_complex(pstate, oprname, largs, rargs);
}

/* alternatively these last two could just create negated  and 
 * expressions. Which is better depends on whether the extra clause
 * confuses the optimizer more or less than having to push the NOTs down 
 */

else if (strcmp(oprname, =) == 0)
{
Node *branch = make_row_op_simple(pstate, =, largs, rargs);
result = make_row_op_complex(pstate, , largs, rargs);
result = (Node *) makeBoolExpr(OR_EXPR, list_make2(result, branch));
}

else if (strcmp(oprname, =) == 0)
{
Node *branch = make_row_op_simple(pstate, =, largs, rargs);
result = make_row_op_complex(pstate, , largs, rargs);
result = (Node *) makeBoolExpr(OR_EXPR, list_make2(result, branch));
}


else
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 errmsg(operator %s is not supported for row expressions,
oprname)));
}

return result;
}

/*
 * Handle something like
 * (A,B,C) = (X,Y,Z)
 * By constructing
 * (A=X) AND (B=Y) AND (C=Z)
 *
 */

static Node *
make_row_op_simple(ParseState *pstate, char *oprname, 
   List *largs, List *rargs)
{
ListCell *l, *r;
BoolExprType boolop;
Node *result;

boolop = strcmp(oprname, )==0 ? OR_EXPR : AND_EXPR;

forboth(l, largs, r, rargs)
{
Node*larg = (Node *) lfirst(l);
Node*rarg = (Node *) lfirst(r);
Node*cmp;

cmp = (Node *) make_op(pstate, opname, larg, rarg);
cmp = coerce_to_boolean(pstate, cmp, row comparison);
if (result == NULL)
result = cmp;
else
result = (Node *) makeBoolExpr(boolop,
   list_make2(result, cmp));
}

if (result == NULL)
{
/* zero-length rows?  Generate constant TRUE or FALSE */
if (boolop == AND_EXPR)
result = makeBoolConst(true, false);
else
result = makeBoolConst(false, false);
}

return result;
}


/*
 * Handles something like:
 * (A,B,C)  (X,Y,Z)
 *
 * By constructing something like:
 * ( ( A  X) OR (A=X AND BY) OR (A=X AND B=Y AND CZ) )
 *
 

Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Merlin Moncure
 Greg Stark [EMAIL PROTECTED] writes:
 This approach won't get the optimizer to actually use an index for
these
 comparisons, but it will fix the semantics to match the spec. Later we
can
 either improve the optimizer to detect expressions like this (which I
 think
 would be cooler since some users may write them by hand and not use
the
 row-expression approach, but I don't see how to do it), or introduce a
new
 run-time node and have the optimizer handle it. But at least we won't
have
 to
 worry about backwards-compatibility issues with the semantics
changing.
 
 Oh, I tried to stick to the style, but sometimes I couldn't help
myself. I
 suppose I would have to fix up the style the rest of the way if I got
it
 working and you wanted a patch to apply.

Regarding the = and = operators:  can you apply them in the complex
pass? If you can, this might be more efficient.

 /*
  * Handles something like:
  * (A,B,C)  (X,Y,Z)
  *
  * By constructing something like:
  * ( ( A  X) OR (A=X AND BY) OR (A=X AND B=Y AND CZ) )
  *  ^
  */ |

the last comparison of the last major clause (or the only comparison for
a single field row construct) is a special case.  In  cases use , in
= cases use =, etc.; this is logical equivalent to doing or of simple
= intersected with complex .  

Is this step of the transformation visible to the optimizer/planner?
For purely selfish reasons, it would be really nice if a field by field
row construction could get a fast path to the index if the fields match
the index fields.

Merlin

---(end of broadcast)---
TIP 6: Have you searched our list archives?

   http://archives.postgresql.org


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Tom Lane
Greg Stark [EMAIL PROTECTED] writes:
 Removing ,=,,= would be trivial.

... and not backwards-compatible.  If we did that then cases involving
unlabeled row expressions would no longer work as they did in prior
releases.  For example

select (1,2,3)  (4,5,6);

is accepted by all releases back to 7.0, and probably much further (but
7.0 is the oldest I have handy to test).  The only reason the code in
parse_expr.c appears new is that the functionality used to be in gram.y.

I'd like to see this fixed to comply with the spec, but given the lack
of complaints about the existing behavior over so many years, ripping
it out meanwhile doesn't seem appropriate.

regards, tom lane

---(end of broadcast)---
TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Merlin Moncure
 Greg Stark [EMAIL PROTECTED] writes:
  Removing ,=,,= would be trivial.
 
 ... and not backwards-compatible.  If we did that then cases involving
 unlabeled row expressions would no longer work as they did in prior
 releases.  For example
 
   select (1,2,3)  (4,5,6);
 
 is accepted by all releases back to 7.0, and probably much further
(but
 7.0 is the oldest I have handy to test).  The only reason the code in
 parse_expr.c appears new is that the functionality used to be in
gram.y.
 
 I'd like to see this fixed to comply with the spec, but given the lack
 of complaints about the existing behavior over so many years, ripping
 it out meanwhile doesn't seem appropriate.

Just to clarify:
I think Greg is arguing to bring pg to SQL 92 spec and not remove
anything.  ISTM the SQL standard was designed with exactly my problem in
mind: how to get the next key in a table. 

IMHO, relying 
on select (1,2,3)  (4,5,6); 
to give a result which is neither standard nor documented seems to be
bad style.  The current methodology could cause pg to give incorrect
results in TPC benchmarks...not good.  Also, it's trivial to rewrite
that comparison with the old behavior using 'and'.  OTOH, it is not
trivial to rewrite the comparison to do it the correct way...it's kind
of an SQL 'trick question'.  Most likely, a very small minority of pg
users are even away of the above syntax anyways.

To be fair, I'm a big fan of deprecating features for at least one
release for compatibility reasons.  It's no big deal to me, because I'm
already writing the queries out the long way anyways.  My interests are
in the optimizer.  If there is a way to enhance it so that it
multi-column comparisons in a logical way, that would be great.  Is this
theoretically possible (probable)?

Merlin


---(end of broadcast)---
TIP 2: you can get off all lists at once with the unregister command
(send unregister YourEmailAddressHere to [EMAIL PROTECTED])


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Greg Stark

Tom Lane [EMAIL PROTECTED] writes:

 The only reason the code in parse_expr.c appears new is that the
 functionality used to be in gram.y.

Ah, that was what I was missing. Though it's odd since it seems there was code
in parse_expr.c to handle the = case specially.

 I'd like to see this fixed to comply with the spec, but given the lack
 of complaints about the existing behavior over so many years, ripping
 it out meanwhile doesn't seem appropriate.

I tried my hand at this last night and think I did an ok first pass. But I'm
missing one piece of the puzzle to get it to compile.

What do I need to know to be able to construct a List* suitable for passing as
the second arg to make_op() knowing only that I want to create a List* to
represent = or  or so on?

I also had another question I didn't ask in the other email. In the midst of a
forboth() loop, how would I tell if I'm at the last element of the lists?
Would lnext(l)==NULL do it?

-- 
greg


---(end of broadcast)---
TIP 3: if posting/reading through Usenet, please send an appropriate
  subscribe-nomail command to [EMAIL PROTECTED] so that your
  message can get through to the mailing list cleanly


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Tom Lane
Greg Stark [EMAIL PROTECTED] writes:
 Tom Lane [EMAIL PROTECTED] writes:
 The only reason the code in parse_expr.c appears new is that the
 functionality used to be in gram.y.

 Ah, that was what I was missing. Though it's odd since it seems there was code
 in parse_expr.c to handle the = case specially.

IIRC, the case involving a subselect, eg
... WHERE (1,2) = ANY (SELECT a, b FROM foo) ...
has always been handled in parse_expr.c, but cases involving simple
rows were previously expanded in gram.y.  One of the reasons I moved
the logic over to parse_expr.c was the thought that it would be easier
to do it right in parse_expr.c --- gram.y would not be allowed to look
up related operators, which seems necessary to handle the construct
per spec.

 I tried my hand at this last night and think I did an ok first pass.

The main issue in my mind is whether to invent a separate node type for
row comparisons.  This is probably a good idea for a number of reasons,
the most obvious being that there's no way to avoid multiple evaluations
of the subexpressions if you try to expand it into simple comparisons.
Also it seems likely that the planner would find it easier to recognize
the relationship to a multicolumn index than if the thing is expanded.
(But teaching the planner and the index mechanisms themselves about this
is going to be a major project in any case.)

One thing I did not like about your first pass is that it makes
unsupportable assumptions about there being a semantic relationship
between operators named, say, '' and '='.  Postgres used to have such
bogosity in a number of places but we've managed to get rid of most of
it.  (Offhand I think the only remaining hard-wired assumption about
operators of particular names having particular semantics is that the
foreign key mechanisms assume '=' must be the right operator to compare
keys with.  Eventually we need to get rid of that too.)

IMHO the right way to do this is to look up a suitable btree operator
class and use the appropriate member operators of that class.  (In a
separate-node-type implementation, we'd probably ignore the operators
as such altogether, and just call the btree comparison function of the
opclass.)  It's not entirely clear to me how to select the opclass when
the initially given inputs are of different types, though.  In the
present code we leave it to oper() to do the right thing, including
possibly coercing the inputs to matching types.  Possibly we should
still apply oper(), but then insist that the selected operator appear
as a btree opclass member, comparable to the way we handle sort
operators now.

regards, tom lane

---(end of broadcast)---
TIP 4: Don't 'kill -9' the postmaster


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-28 Thread Greg Stark

Tom Lane [EMAIL PROTECTED] writes:

 One thing I did not like about your first pass is that it makes
 unsupportable assumptions about there being a semantic relationship
 between operators named, say, '' and '='.  

Hm, I think I even had caught that issue on the mailing list previously.

In that case though, it seems even the existing code is insufficient. Instead
of testing whether the operator with strcmp against = and  it should
perhaps be looking for an operator class and the strategy number for the
operator and its negator.

-- 
greg


---(end of broadcast)---
TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]


[PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
I am in a situation where I have to treat a table as logically ordered
based on an index.  Right now I'm doing this via queries, and a I need a
better way to do it.  Cursors do not meet my requirements, because they
are always insensitive.  Also, my performance requirements are
extreme...I need 100% index usage.

Currently, I use queries to do this.  Unfortunately, the queries can get
kind of complex because many if the indexes (keys, really) are over 3 or
more columns in a table.

So, for a table t with a three part key over columns a,b,c, the query to
read the next value from t for given values a1, b1, c1 is

select * from t where
a = a1 and
 (a   a1 or b = b1) and
 (a   a1 or b  b1 or c  c1)

In about 95% of cases, the planner correctly selects the index t(a,b,c)
and uses it.  However, the 5% remaining cases usually come at the worst
time, when large tables and 3 or 4 part keys are involved.  In those
cases sometimes the planner applies the filter to a, but not b or c with
a large performance hit.  Manipulating statistics on the table does not
seem to help.

Interestingly, it is possible to rewrite the above query by switching
and with or and = with .  However when written that way, the planner
almost never gets it right.

My problem is deceptively simple: how you read the next record from a
table based on a given set of values?  In practice, this is difficult to
implement.  If anybody can suggest a alternative/better way to this, I'm
all ears.

Merlin

---(end of broadcast)---
TIP 4: Don't 'kill -9' the postmaster


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Tom Lane
Merlin Moncure [EMAIL PROTECTED] writes:
 So, for a table t with a three part key over columns a,b,c, the query to
 read the next value from t for given values a1, b1, c1 is

 select * from t where
   a = a1 and
  (a   a1 or b = b1) and
  (a   a1 or b  b1 or c  c1)

 In about 95% of cases, the planner correctly selects the index t(a,b,c)
 and uses it.

I'm surprised it's that good.  Why not do

select * from t where a = a1 and b = b1 and c = c1
order by a,b,c
limit 1 offset 1;

which has a much more obvious translation to an indexscan.

regards, tom lane

---(end of broadcast)---
TIP 9: the planner will ignore your desire to choose an index scan if your
  joining column's datatypes do not match


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
Greg Stark wrote:
   do it for multi-column keys. It seems it would be nice if some
syntax
   similar to (a,b,c)  (a1,b1,c1) worked for this.
 Hum. It would seem my intuition matches the SQL92 spec and Postgres
gets
 this
 wrong.
[...] 
 Even if Postgres did this right I'm not sure that would solve your
index
 woes.
 I imagine the first thing Postgres would do is rewrite it into regular
 scalar
 expressions. Ideally the optimizer should be capable of then deducing
from
 the
 scalar expressions that an index scan would be useful.

Wow.  For once, the standard is my friend.  Well, what has to be done?
:)  Does pg do it the way it does for a reason?  From the outside it
seems like the planner would have an easier job if it can make a field
by field comparison.  

Would a patch introducing the correct behavior (per the standard) be
accepted?  It seems pretty complicated (not to mention the planner
issues).

Merlin



---(end of broadcast)---
TIP 8: explain analyze is your friend


[PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
 SELECT * FROM t WHERE
 (a = a1 AND b=b1 AND c=c1) ORDER BY a,b,c LIMIT 1 OFFSET 1;
 
 using the way LIMIT cuts down on sort time (I've never tried it with
both
 LIMIT and OFFSET, though; you could always use LIMIT 2 and skip a
record
 client-side if that works better).

Don't want to further clutter the list (answered this question several
times already), but your query does not work.  What I meant to write
was:

select * from t where
a = a1 and
 (a   a1 or b = b1) and
 (a   a1 or b  b1 or c  c1)
order by a, b, c limit 1

The problem with your query is it excludes all values of c = c1
regardless of values of a and b.

Merlin

---(end of broadcast)---
TIP 7: don't forget to increase your free space map settings


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Greg Stark


 Interestingly, it is possible to rewrite the above query by switching
 and with or and = with .  However when written that way, the planner
 almost never gets it right.

Well, note it's still not really getting it right even in your case. It's
doing an index scan on a=a1 but if you have lots of values in your table
where a=a1 and bb1 then it's going to unnecessarily read through all of
those.


One thing that can help is to add ORDER BY a,b,c LIMIT 1 to your query. That
will virtually guarantee that it uses an index scan, which will at least avoid
making it scan all the records *after* finding the match. However it still
doesn't seem to make Postgres use an Index Cond to allow it to do an instant
lookup.

I expected WHERE (a,b,c)  (a1,b1,c1) to work however it doesn't. It appears
to mean aa1 AND bb1 AND cc1 which isn't at all what you want. I imagine the
standard dictates this meaning.

 My problem is deceptively simple: how you read the next record from a
 table based on a given set of values?  In practice, this is difficult to
 implement.  If anybody can suggest a alternative/better way to this, I'm
 all ears.

I've done this a million times for simple integer keys, but I've never had to
do it for multi-column keys. It seems it would be nice if some syntax similar
to (a,b,c)  (a1,b1,c1) worked for this.

-- 
greg


---(end of broadcast)---
TIP 8: explain analyze is your friend


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread andrew
Merlin Moncure [EMAIL PROTECTED] wrote ..
[snip]
 select * from t where
   a = a1 and
  (a   a1 or b = b1) and
  (a   a1 or b  b1 or c  c1)

I don't see why this is guaranteed to work without an ORDER BY clause, even if TABLE t 
is clustered on the correct index. Am I missing something? I have two suggestions: 

(1) I think I would have written 

SELECT * FROM t WHERE
(a = a1 AND b=b1 AND c=c1) ORDER BY a,b,c LIMIT 1 OFFSET 1;

using the way LIMIT cuts down on sort time (I've never tried it with both LIMIT and 
OFFSET, though; you could always use LIMIT 2 and skip a record client-side if that 
works better).

(2) I've seen code where depending on the types and values of the fields, it was 
possible to construct a string from a, b, c by some sort of concatenation where the 
index now agreed with the lexicographic (dictionary) ordering on the string. Postgres 
could do that with a functional index, if your values can be used with this trick.

---(end of broadcast)---
TIP 5: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faqs/FAQ.html


Correction of [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Markus Schaber
Hi, Merlin,

On Tue, 27 Jul 2004 16:13:25 +0200, I myself wrote:


 You mut not

Should be must, not mut :-)

  My problem is deceptively simple: how you read the next record from
  a table based on a given set of values?  In practice, this is
  difficult to implement.  If anybody can suggest a alternative/better
  way to this, I'm all ears.
 
 So you really want something like
 
 'SELECT * FROM t WHERE a=a1 AND b=b1 AND c=c1 ORDER BY a,b,c ASC
 LIMIT 1'

Sorry, as you want the _next_, and I assume that a1, b1 and c1 are the
current row's values, you should rather use something like:

'SELECT * FROM t WHERE a=a1 AND b=b1 AND c=c1 ORDER BY a,b,c ASC
LIMIT 1 OFFSET 1'

HTH,
Markus

-- 
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 zürich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:[EMAIL PROTECTED] | www.logi-track.com

---(end of broadcast)---
TIP 6: Have you searched our list archives?

   http://archives.postgresql.org


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Markus Schaber
Hi, Merlin,

On Tue, 27 Jul 2004 09:07:02 -0400
Merlin Moncure [EMAIL PROTECTED] wrote:

 So, for a table t with a three part key over columns a,b,c, the query
 to read the next value from t for given values a1, b1, c1 is
 
 select * from t where
   a = a1 and
  (a   a1 or b = b1) and
  (a   a1 or b  b1 or c  c1)

You mut not rely on such trickery to get any ordering, as the SQL data
model contains no ordering, and a query optimizer is free to deliver you
the tuples in any order it feels like.

Why don't you add a 'ORDER BY a,b,c ASC' to your query?

 Interestingly, it is possible to rewrite the above query by switching
 and with or and = with .  However when written that way, the planner
 almost never gets it right.

That's the reason why you cannot rely on any implicit ordering, the
planner is free to rewrite a query as it likes as long as it delivers
the same tuples, but in any order it wants.

 My problem is deceptively simple: how you read the next record from a
 table based on a given set of values?  In practice, this is difficult
 to implement.  If anybody can suggest a alternative/better way to
 this, I'm all ears.

So you really want something like

'SELECT * FROM t WHERE a=a1 AND b=b1 AND c=c1 ORDER BY a,b,c ASC LIMIT 1'


HTH,
Markus
-- 
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 zürich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:[EMAIL PROTECTED] | www.logi-track.com

---(end of broadcast)---
TIP 5: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faqs/FAQ.html


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Rod Taylor
You only want one record to be returned? Tack a LIMIT 1 onto the end of 
the query.

 My problem is deceptively simple: how you read the next record from a
 table based on a given set of values?  In practice, this is difficult to
 implement.  If anybody can suggest a alternative/better way to this, I'm
 all ears.



---(end of broadcast)---
TIP 9: the planner will ignore your desire to choose an index scan if your
  joining column's datatypes do not match


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Tom Lane
I said:
 Oh, wait, you're right --- I'm mis-visualizing the situation.
 Hmm, it sure seems like there ought to be an easy way to do this...

The problem is that a multi-column index doesn't actually have the
semantics you want.  If you are willing to consider adding another
index (or replacing the existing 3-column guy), how about

create index ti on t((array[a,b,c]));

select * from t where array[a,b,c] = array[a1,b1,c1]
order by array[a,b,c]
limit 1 offset 1;

This seems to do the right thing in 7.4 and later.  It does require that
all three columns have the same datatype though; you weren't specific
about the data model ...

regards, tom lane

---(end of broadcast)---
TIP 8: explain analyze is your friend


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Markus Schaber
Hi, Merlin,

On Tue, 27 Jul 2004 10:21:32 -0400
Merlin Moncure [EMAIL PROTECTED] wrote:

 The basic problem is the planner can't always match the query to the
 index.  So, either the planner has to be helped/fixed or I have to
 explore another solution.  This seems to happen most when the 'a'
 column has very poor selectivity.  In this case, the planner will only
 examine the 'a' part of the key.

So it may help to add some more indices so you have an index for all permutations,

Create an index on (a,b,c), (a,c,b), (b,c,a), (b,a,c), (c,a,b) and (c,b,a).

So as long as one of the rows has enough selectivity, the planner should
be able to select the correct index. Maybe increasing the number of
random samples for the rows is useful.

HTH,
Markus

-- 
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 zürich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:[EMAIL PROTECTED] | www.logi-track.com

---(end of broadcast)---
TIP 5: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faqs/FAQ.html


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
Greg wrote:
 One thing that can help is to add ORDER BY a,b,c LIMIT 1 to your
query.
 That
 will virtually guarantee that it uses an index scan, which will at
least
 avoid
 making it scan all the records *after* finding the match. However it
still
 doesn't seem to make Postgres use an Index Cond to allow it to do an
 instant
 lookup.

Yes, order by/limit was accidentally left of my original example.  My
problem is with the word 'virtually'.

 do it for multi-column keys. It seems it would be nice if some syntax
 similar
 to (a,b,c)  (a1,b1,c1) worked for this.

'nice' would be an understatement...
if the above syntax is not defined in the standard, I would humbly
suggest, well, beg for it to work as you thought it did.  That would be
GREAT!  ISMT it may be that that is in fact standard...(I don't have it,
so I don't know).

Merlin



---(end of broadcast)---
TIP 3: if posting/reading through Usenet, please send an appropriate
  subscribe-nomail command to [EMAIL PROTECTED] so that your
  message can get through to the mailing list cleanly


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Tom Lane
Merlin Moncure [EMAIL PROTECTED] writes:
 Plus, your where clause does not guarantee results.

No, but in combination with the ORDER BY it does.  Please note also
that the offset would *always* be one, so your gripe about it not
scaling seems misguided to me.

regards, tom lane

---(end of broadcast)---
TIP 3: if posting/reading through Usenet, please send an appropriate
  subscribe-nomail command to [EMAIL PROTECTED] so that your
  message can get through to the mailing list cleanly


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Greg Stark

Merlin Moncure [EMAIL PROTECTED] writes:

  do it for multi-column keys. It seems it would be nice if some syntax
  similar to (a,b,c)  (a1,b1,c1) worked for this.
 
 'nice' would be an understatement...

 if the above syntax is not defined in the standard, I would humbly suggest,
 well, beg for it to work as you thought it did. That would be GREAT! ISMT it
 may be that that is in fact standard...(I don't have it, so I don't know).


Hum. It would seem my intuition matches the SQL92 spec and Postgres gets this
wrong.

From page 208 (Section 8.2.7) of
 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt 


 7) Let Rx and Ry be the two row value constructors of the com-
parison predicate and let RXi and RYi be the i-th row value
constructor elements of Rx and Ry, respectively. Rx comp op
Ry is true, false, or unknown as follows:

a) x = Ry is true if and only if RXi = RYi for all i.

b) x  Ry is true if and only if RXi  RYi for some i.

c) x  Ry is true if and only if RXi = RYi for all i  n and
  RXn  RYn for some n.

d) x  Ry is true if and only if RXi = RYi for all i  n and
  RXn  RYn for some n.

...


(This is A July 10, 1992 Proposed revision, I don't know how far it differs
from the final. I imagine they mean Rx in all the places they use x alone)

That fairly clearly specifies (a,b,c)  (a1,b1,c1) to work the way you want it
to. Less-than-or-equal is then defined based on the above definition.


Even if Postgres did this right I'm not sure that would solve your index woes.
I imagine the first thing Postgres would do is rewrite it into regular scalar
expressions. Ideally the optimizer should be capable of then deducing from the
scalar expressions that an index scan would be useful.

-- 
greg


---(end of broadcast)---
TIP 8: explain analyze is your friend


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Tom Lane
Tom Lane [EMAIL PROTECTED] writes:
 Merlin Moncure [EMAIL PROTECTED] writes:
 Plus, your where clause does not guarantee results.

 No, but in combination with the ORDER BY it does.

Oh, wait, you're right --- I'm mis-visualizing the situation.

Hmm, it sure seems like there ought to be an easy way to do this...

regards, tom lane

---(end of broadcast)---
TIP 8: explain analyze is your friend


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
  select * from t where
  a = a1 and
   (a   a1 or b = b1) and
   (a   a1 or b  b1 or c  c1)
 
  In about 95% of cases, the planner correctly selects the index
t(a,b,c)
  and uses it.
 
 I'm surprised it's that good.  Why not do

It is.  In fact, it's so good, I mistakenly assumed it would get it
right all the time.  That led me directly to my current situation.
 
   select * from t where a = a1 and b = b1 and c = c1
   order by a,b,c
   limit 1 offset 1;
Note: I left off the limit/order part of the query in my original
example.

My previous experience with offset was that it's not practical for this
type of use.  Query time degrades when offset gets large...it's
basically n^2/2 for a scan of a table.  If offset was pumped up to O(1)
for any sized offset, the problem would be trivial.  

Plus, your where clause does not guarantee results.

Imagine:
a  b  c
2  3  4
4  2  1

c ! c1

The only other way to rewrite the query is thus (pg has much more
trouble with this form):
select * from t where
aa1 or
 (a =  a1 and b  b1) or
 (a =  a1 and b = b1 and c  c1)

Merlin

---(end of broadcast)---
TIP 5: Have you checked our extensive FAQ?

   http://www.postgresql.org/docs/faqs/FAQ.html


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Merlin Moncure
 Hmm, it sure seems like there ought to be an easy way to do this...

Here is the only alternative that I see:
create function column_stacker(text[] columns, text[] types) returns
text
[...]
language 'C' immutable;

the above function stacks the columns together in a single string for
easy range indexing.

create index on t_idx(array[t.a::text, t.b::text, t.c::text],
array['int', 'int', 'char(2)']);

This is a lot more complicated then it sounds but it can be done.  The
use of arrays is forced because of limitations in the way pg handles
parameters (no big deal).  The real disadvantage here is that it these
indexes don't help with normal queries so every key gets two indexes :(.

I'm just looking for a nudge in the right direction here...if the answer
is GIST, I'll start researching that, etc.  The ideal solution for me
would be a smarter planner or a simple C function to get the next record
out of the index (exposed through a UDF).

Everything has to stay generic...the ultimate goal is an ISAM driver for
pg.

Merlin



---(end of broadcast)---
TIP 3: if posting/reading through Usenet, please send an appropriate
  subscribe-nomail command to [EMAIL PROTECTED] so that your
  message can get through to the mailing list cleanly


Re: [PERFORM] best way to fetch next/prev record based on index

2004-07-27 Thread Stephan Szabo

On Tue, 27 Jul 2004, Merlin Moncure wrote:

 Greg Stark wrote:
do it for multi-column keys. It seems it would be nice if some
 syntax
similar to (a,b,c)  (a1,b1,c1) worked for this.
  Hum. It would seem my intuition matches the SQL92 spec and Postgres
 gets
  this
  wrong.
 [...]
  Even if Postgres did this right I'm not sure that would solve your
 index
  woes.
  I imagine the first thing Postgres would do is rewrite it into regular
  scalar
  expressions. Ideally the optimizer should be capable of then deducing
 from
  the
  scalar expressions that an index scan would be useful.

 Wow.  For once, the standard is my friend.  Well, what has to be done?
 :)  Does pg do it the way it does for a reason?  From the outside it
 seems like the planner would have an easier job if it can make a field
 by field comparison.

 Would a patch introducing the correct behavior (per the standard) be
 accepted?  It seems pretty complicated (not to mention the planner
 issues).

Given the comment on make_row_op,
  /*
   * XXX it's really wrong to generate a simple AND combination for  =
   *  =.  We probably need to invent a new runtime node type to handle
   * those correctly.  For the moment, though, keep on doing this ...
   */
I'd expect it'd be accepted.


---(end of broadcast)---
TIP 8: explain analyze is your friend