.\" Automatically generated by Pod::Man 2.23 (Pod::Simple 3.14) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SQL::Abstract::Limit 3pm" .TH SQL::Abstract::Limit 3pm "2011-09-17" "perl v5.12.4" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" SQL::Abstract::Limit \- portable LIMIT emulation .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use SQL::Abstract::Limit; \& \& my $sql = SQL::Abstract::Limit\->new( limit_dialect => \*(AqLimitOffset\*(Aq );; \& \& # or autodetect from a DBI $dbh: \& my $sql = SQL::Abstract::Limit\->new( limit_dialect => $dbh ); \& \& # or from a Class::DBI class: \& my $sql = SQL::Abstract::Limit\->new( limit_dialect => \*(AqMy::CDBI::App\*(Aq ); \& \& # or object: \& my $obj = My::CDBI::App\->retrieve( $id ); \& my $sql = SQL::Abstract::Limit\->new( limit_dialect => $obj ); \& \& # generate SQL: \& my ( $stmt, @bind ) = $sql\->select( $table, \e@fields, \e%where, \e@order, $limit, $offset ); \& \& # Then, use these in your DBI statements \& my $sth = $dbh\->prepare( $stmt ); \& $sth\->execute( @bind ); \& \& # Just generate the WHERE clause (only available for some syntaxes) \& my ( $stmt, @bind ) = $sql\->where( \e%where, \e@order, $limit, $offset ); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Portability layer for \s-1LIMIT\s0 emulation. .IP "new( case => 'lower', cmp => 'like', logic => 'and', convert => 'upper', limit_dialect => 'Top' )" 4 .IX Item "new( case => 'lower', cmp => 'like', logic => 'and', convert => 'upper', limit_dialect => 'Top' )" All settings are optional. .RS 4 .IP "limit_dialect" 8 .IX Item "limit_dialect" Sets the default syntax model to use for emulating a \f(CW\*(C`LIMIT $rows OFFSET $offset\*(C'\fR clause. Default setting is \f(CW\*(C`GenericSubQ\*(C'\fR. You can still pass other syntax settings in method calls, this just sets the default. Possible values are: .Sp .Vb 4 \& LimitOffset PostgreSQL, SQLite \& LimitXY MySQL, MaxDB, anything that uses SQL::Statement \& LimitYX SQLite (optional) \& RowsTo InterBase/FireBird \& \& Top SQL/Server, MS Access \& RowNum Oracle \& FetchFirst DB2 \& Skip Informix \& GenericSubQ Sybase, plus any databases not recognised by this module \& \& $dbh a DBI database handle \& \& CDBI subclass \& CDBI object \& \& other DBI\-based thing .Ve .Sp The first group are implemented by appending a short clause to the end of the statement. The second group require more intricate wrapping of the original statement in subselects. .Sp You can pass a \s-1DBI\s0 database handle, and the module will figure out which dialect to use. .Sp You can pass a Class::DBI subclass or object, and the module will find the \f(CW$dbh\fR and use it to find the dialect. .Sp Anything else based on \s-1DBI\s0 can be easily added by locating the \f(CW$dbh\fR. Patches or suggestions welcome. .RE .RS 4 .Sp Other options are described in SQL::Abstract. .RE .ie n .IP "select( $table, \e@fields, $where, [ \e@order, [ $rows, [ $offset ], [ $dialect ] ] ] )" 4 .el .IP "select( \f(CW$table\fR, \e@fields, \f(CW$where\fR, [ \e@order, [ \f(CW$rows\fR, [ \f(CW$offset\fR ], [ \f(CW$dialect\fR ] ] ] )" 4 .IX Item "select( $table, @fields, $where, [ @order, [ $rows, [ $offset ], [ $dialect ] ] ] )" Same as \f(CW\*(C`SQL::Abstract::select\*(C'\fR, but accepts additional \f(CW$rows\fR, \f(CW$offset\fR and \f(CW$dialect\fR parameters. .Sp The \f(CW$order\fR parameter is required if \f(CW$rows\fR is specified. .Sp The \f(CW$fields\fR parameter is required, but can be set to \f(CW\*(C`undef\*(C'\fR, \f(CW\*(Aq\*(Aq\fR or \&\f(CW\*(Aq*\*(Aq\fR (all these get set to \f(CW\*(Aq*\*(Aq\fR). .Sp The \f(CW$where\fR parameter is also required. It can be a hashref or an arrayref, or \f(CW\*(C`undef\*(C'\fR. .ie n .IP "where( [ $where, [ \e@order, [ $rows, [ $offset ], [ $dialect ] ] ] ] )" 4 .el .IP "where( [ \f(CW$where\fR, [ \e@order, [ \f(CW$rows\fR, [ \f(CW$offset\fR ], [ \f(CW$dialect\fR ] ] ] ] )" 4 .IX Item "where( [ $where, [ @order, [ $rows, [ $offset ], [ $dialect ] ] ] ] )" Same as \f(CW\*(C`SQL::Abstract::where\*(C'\fR, but accepts additional \f(CW$rows\fR, \f(CW$offset\fR and \f(CW$dialect\fR parameters. .Sp Some \s-1SQL\s0 dialects support syntaxes that can be applied as simple phrases tacked on to the end of the \s-1WHERE\s0 clause. These are: .Sp .Vb 4 \& LimitOffset \& LimitXY \& LimitYX \& RowsTo .Ve .Sp This method returns a modified \s-1WHERE\s0 clause, if the limit syntax is set to one of these options (either in the call to \f(CW\*(C`where\*(C'\fR or in the constructor), and if \f(CW$rows\fR is passed in. .Sp Dies via \f(CW\*(C`croak\*(C'\fR if you try to use it for other syntaxes. .Sp \&\f(CW$order\fR is required if \f(CW$rows\fR is set. .Sp \&\f(CW$where\fR is required if any other parameters are specified. It can be a hashref or an arrayref, or \f(CW\*(C`undef\*(C'\fR. .Sp Returns a regular \f(CW\*(C`WHERE\*(C'\fR clause if no limits are set. .IP "insert" 4 .IX Item "insert" .PD 0 .IP "update" 4 .IX Item "update" .IP "delete" 4 .IX Item "delete" .IP "values" 4 .IX Item "values" .IP "generate" 4 .IX Item "generate" .PD See SQL::Abstract for these methods. .Sp \&\f(CW\*(C`update\*(C'\fR and \f(CW\*(C`delete\*(C'\fR are not provided with any \f(CW\*(C`LIMIT\*(C'\fR emulation in this release, and no support is planned at the moment. But patches would be welcome. .SS "Limit emulation" .IX Subsection "Limit emulation" The following dialects are available for emulating the \s-1LIMIT\s0 clause. In each case, \f(CW$sql\fR represents the \s-1SQL\s0 statement generated by \f(CW\*(C`SQL::Abstract::select\*(C'\fR, minus the \s-1ORDER\s0 \s-1BY\s0 clause, e.g. .PP .Vb 1 \& SELECT foo, bar FROM my_table WHERE some_conditions .Ve .PP \&\f(CW$sql_after_select\fR represents \f(CW$sql\fR with the leading \f(CW\*(C`SELECT\*(C'\fR keyword removed. .PP \&\f(CW\*(C`order_cols_up\*(C'\fR represents the sort column(s) and direction(s) specified in the \f(CW\*(C`order\*(C'\fR parameter. .PP \&\f(CW\*(C`order_cols_down\*(C'\fR represents the opposite sort. .PP \&\f(CW\*(C`$last = $rows + $offset\*(C'\fR .IP "LimitOffset" 4 .IX Item "LimitOffset" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $rows OFFSET $offset .Ve .Sp or .Sp .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $rows .Ve .Sp if \f(CW\*(C`$offset == 0\*(C'\fR. .IP "Databases" 8 .IX Item "Databases" .Vb 2 \& PostgreSQL \& SQLite .Ve .RE .RS 4 .RE .IP "LimitXY" 4 .IX Item "LimitXY" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $offset, $rows .Ve .Sp or .Sp .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $rows .Ve .Sp if \f(CW\*(C`$offset == 0\*(C'\fR. .IP "Databases" 8 .IX Item "Databases" .Vb 1 \& MySQL .Ve .RE .RS 4 .RE .IP "LimitYX" 4 .IX Item "LimitYX" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $rows, $offset .Ve .Sp or .Sp .Vb 1 \& $sql ORDER BY order_cols_up LIMIT $rows .Ve .Sp if \f(CW\*(C`$offset == 0\*(C'\fR. .IP "Databases" 8 .IX Item "Databases" .Vb 2 \& SQLite understands this syntax, or LimitOffset. If autodetecting the \& dialect, it will be set to LimitOffset. .Ve .RE .RS 4 .RE .IP "RowsTo" 4 .IX Item "RowsTo" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 1 \& $sql ORDER BY order_cols_up ROWS $offset TO $last .Ve .IP "Databases" 8 .IX Item "Databases" .Vb 2 \& InterBase \& FireBird .Ve .RE .RS 4 .RE .IP "Top" 4 .IX Item "Top" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 10 \& SELECT * FROM \& ( \& SELECT TOP $rows * FROM \& ( \& SELECT TOP $last $sql_after_select \& ORDER BY order_cols_up \& ) AS foo \& ORDER BY order_cols_down \& ) AS bar \& ORDER BY order_cols_up .Ve .IP "Databases" 8 .IX Item "Databases" .Vb 2 \& SQL/Server \& MS Access .Ve .RE .RS 4 .RE .IP "RowNum" 4 .IX Item "RowNum" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD Oracle numbers rows from 1, not zero, so here \f(CW$offset\fR has been incremented by 1. .Sp .Vb 9 \& SELECT * FROM \& ( \& SELECT A.*, ROWNUM r FROM \& ( \& $sql ORDER BY order_cols_up \& ) A \& WHERE ROWNUM <= $last \& ) B \& WHERE r >= $offset .Ve .IP "Databases" 8 .IX Item "Databases" .Vb 1 \& Oracle .Ve .RE .RS 4 .RE .IP "FetchFirst" 4 .IX Item "FetchFirst" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 10 \& SELECT * FROM ( \& SELECT * FROM ( \& $sql \& ORDER BY order_cols_up \& FETCH FIRST $last ROWS ONLY \& ) foo \& ORDER BY order_cols_down \& FETCH FIRST $rows ROWS ONLY \& ) bar \& ORDER BY order_cols_up .Ve .IP "Databases" 8 .IX Item "Databases" \&\s-1IBM\s0 \s-1DB2\s0 .RE .RS 4 .RE .IP "GenericSubQ" 4 .IX Item "GenericSubQ" When all else fails, this should work for many databases, but it is probably fairly slow. .Sp This method relies on having a column with unique values as the first column in the \f(CW\*(C`SELECT\*(C'\fR clause (i.e. the first column in the \f(CW\*(C`\e@fields\*(C'\fR parameter). The results will be sorted by that unique column, so any \f(CW$order\fR parameter is ignored, unless it matches the unique column, in which case the direction of the sort is honoured. .RS 4 .IP "Syntax" 8 .IX Item "Syntax" .Vb 6 \& SELECT field_list FROM $table X WHERE where_clause AND \& ( \& SELECT COUNT(*) FROM $table WHERE $pk > X.$pk \& ) \& BETWEEN $offset AND $last \& ORDER BY $pk $asc_desc .Ve .Sp \&\f(CW$pk\fR is the first column in \f(CW\*(C`field_list\*(C'\fR. .Sp \&\f(CW$asc_desc\fR is the opposite direction to that specified in the method call. So if you want the final results sorted \f(CW\*(C`ASC\*(C'\fR, say so, and it gets flipped internally, but the results come out as you'd expect. I think. .Sp The \f(CW\*(C`BETWEEN $offset AND $last\*(C'\fR clause is replaced with \f(CW\*(C`< $rows\*(C'\fR if <$offset == 0>. .IP "Databases" 8 .IX Item "Databases" Sybase Anything not otherwise known to this module. .RE .RS 4 .RE .IP "Skip" 4 .IX Item "Skip" .RS 4 .PD 0 .IP "Syntax" 8 .IX Item "Syntax" .PD .Vb 1 \& select skip 5 limit 5 * from customer .Ve .Sp which will take rows 6 through 10 in the select. .IP "Databases" 8 .IX Item "Databases" Informix .RE .RS 4 .RE .SH "SUBCLASSING" .IX Header "SUBCLASSING" You can create your own syntax by making a subclass that provides an \&\f(CW\*(C`emulate_limit\*(C'\fR method. This might be useful if you are using stored procedures to provide more efficient paging. .ie n .IP "emulate_limit( $self, $sql, $order, $rows, $offset )" 4 .el .IP "emulate_limit( \f(CW$self\fR, \f(CW$sql\fR, \f(CW$order\fR, \f(CW$rows\fR, \f(CW$offset\fR )" 4 .IX Item "emulate_limit( $self, $sql, $order, $rows, $offset )" .RS 4 .PD 0 .ie n .IP "$sql" 4 .el .IP "\f(CW$sql\fR" 4 .IX Item "$sql" .PD This is the \s-1SQL\s0 statement built by SQL::Abstract, but without the \s-1ORDER\s0 \s-1BY\s0 clause, e.g. .Sp .Vb 1 \& SELECT foo, bar FROM my_table WHERE conditions .Ve .Sp or just .Sp .Vb 1 \& WHERE conditions .Ve .Sp if calling \f(CW\*(C`where\*(C'\fR instead of \f(CW\*(C`select\*(C'\fR. .ie n .IP "$order" 4 .el .IP "\f(CW$order\fR" 4 .IX Item "$order" The \f(CW\*(C`order\*(C'\fR parameter passed to the \f(CW\*(C`select\*(C'\fR or \f(CW\*(C`where\*(C'\fR call. You can get an \f(CW\*(C`ORDER BY\*(C'\fR clause from this by calling .Sp .Vb 1 \& my $order_by = $self\->_order_by( $order ); .Ve .Sp You can get a pair of \f(CW\*(C`ORDER BY\*(C'\fR clauses that sort in opposite directions by saying .Sp .Vb 1 \& my ( $up, $down ) = $self\->_order_directions( $order ); .Ve .RE .RS 4 .Sp The method should return a suitably modified \s-1SQL\s0 statement. .RE .SH "AUTO-DETECTING THE DIALECT" .IX Header "AUTO-DETECTING THE DIALECT" The \f(CW$dialect\fR parameter that can be passed to the constructor or to the \&\f(CW\*(C`select\*(C'\fR and \f(CW\*(C`where\*(C'\fR methods can be a number of things. The module will attempt to determine the appropriate syntax to use. .PP Supported \f(CW$dialect\fR things are: .PP .Vb 4 \& dialect name (e.g. LimitOffset, RowsTo, Top etc.) \& database moniker (e.g. Oracle, SQLite etc.) \& DBI database handle \& Class::DBI subclass or object .Ve .SH "CAVEATS" .IX Header "CAVEATS" Paging results sets is a complicated undertaking, with several competing factors to take into account. This module does \fBnot\fR magically give you the optimum paging solution for your situation. It gives you a solution that may be good enough in many situations. But if your tables are large, the \s-1SQL\s0 generated here will often not be efficient. Or if your queries involve joins or other complications, you will probably need to look elsewhere. .PP But if your tables aren't too huge, and your queries straightforward, you can just plug this module in and move on to your next task. .SH "ACKNOWLEDGEMENTS" .IX Header "ACKNOWLEDGEMENTS" Thanks to Aaron Johnson for the Top syntax model (SQL/Server and \s-1MS\s0 Access). .PP Thanks to Emanuele Zeppieri for the \s-1IBM\s0 \s-1DB2\s0 syntax model. .PP Thanks to Paul Falbe for the Informix implementation. .SH "TODO" .IX Header "TODO" Find more syntaxes to implement. .PP Test the syntaxes against real databases. I only have access to MySQL. Reports of success or failure would be great. .SH "DEPENDENCIES" .IX Header "DEPENDENCIES" SQL::Abstract, DBI::Const::GetInfoType, Carp. .SH "SEE ALSO" .IX Header "SEE ALSO" DBIx::SQLEngine, DBIx::SearchBuilder, DBIx::RecordSet. .SH "BUGS" .IX Header "BUGS" Please report all bugs via the \s-1CPAN\s0 Request Tracker at http://rt.cpan.org/NoAuth/Bugs.html?Dist=SQL\-Abstract\-Limit . .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" Copyright 2004 by David Baird. .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .SH "AUTHOR" .IX Header "AUTHOR" David Baird, \f(CW\*(C`cpan@riverside\-cms.co.uk\*(C'\fR .SH "HOW IS IT DONE ELSEWHERE" .IX Header "HOW IS IT DONE ELSEWHERE" A few \s-1CPAN\s0 modules do this for a few databases, but the most comprehensive seem to be DBIx::SQLEngine, DBIx::SearchBuilder and DBIx::RecordSet. .PP Have a look in the source code for my notes on how these modules tackle similar problems.