NAME
SQL::Builder::Select - Represent a SQL SELECT statement
SYNOPSIS
In the following examples I'll be using figurative database tables that
look like:
users
---------
user_id
name
age
salary_id
salaries
---------
salary_id
salary_amount
Most of the functionality provided by this module is provided by the various classses is uses. This module actually does very little.
# new select object
my $sel = SQL::Builder::Select->new;
# add a table
$sel->tables->add_table(table => "users", alias => "u");
# SELECT * FROM users AS u
print $sel->sql;
We can add more tables to the table list like so:
$sel->tables->add_table(table => "salaries", alias => "sals");
# SELECT * FROM users AS u, salaries AS sals
print $sel->sql;
And of course add a WHERE clause
$sel->where->list_push("u.salary_id = sals.salary_id");
Suppose the database supports JOINs, we can use those to:
my $sel = SQL::Builder::Select->new();
$sel->tables->add_table(table => "users", alias => "u");
$sel->tables->add_join(
table => "salaries",
'using->list_push' => "salary_id"
);
# SELECT * FROM users AS u JOIN salaries USING (salary_id)
Since we care about state, we can do the same thing, except more statefully, and instead of using the USING operator, we'll use ON for the join:
# I'm using globals for simplicity. Please don't do it in code that
# matters
# define a users table
our $tbl_users = SQL::Builder::Table->new(
name => "users",
alias => "u"
);
# define the salaries table
our $tbl_salaries = SQL::Builder::Table->new(
name => "salaries",
alias => "sals"
);
sub get_user_pay {
# declare a new SELECT object
my $sel = SQL::Builder::Select->new();
# add the users table to the SELECT list
$sel->tables->list_push($tbl_users);
# join against the salaries table
$sel->tables->joins->list_push(
SQL::Builder::Join->new(
table => $tbl_salaries,
'on->list_push' => SQL::Builder::BinaryOp->new(
op => "=",
lhs => $tbl_salaries->col(name => 'salary_id'),
rhs => $tbl_users->col(name => 'salary_id')
)
)
);
return $sel
}
Now that we have a stateful representation of our query, it's easy to reuse and modify:
my $query = get_user_pay();
# suppose we only want the user_id and salary_amount columns returned
$query->cols->list_push(
$tbl_users->col(name => 'user_id'),
$tbl_salaries->col(name => 'salary_amount')
);
# SELECT u.user_id, sals.salary_amount
# FROM users AS u
# JOIN salaries AS sal ON sal.salary_id = u.salary_id
We can also filter the results by using a WHERE clause. The following will return users who earn more than $20,000
$query->where->list_push(
SQL::Builder::BinaryOp->(
op => ">",
lhs => $tbl_salaries->col(name => 'salary_amount'),
rhs => 20_000
)
);
# SELECT u.user_id, sals.salary_amount
# FROM users AS u
# JOIN salaries AS sal ON sal.salary_id = u.salary_id
# WHERE u.salary_amount > 20000
But that's not all, we use a "macro" to modify the query to return all users who have a salary of less than $20,000. We'll just flip all instances of ">" with "<":
# find all objects in the WHERE clause with an op()
# method that returns ">"
my $iterator = $query->where->look_down(
op => ">"
);
while($iterator->pull) {
my $binary_op = $iterator->current;
# flip the sign
$binary_op->op("<");
}
# SELECT u.user_id, sals.salary_amount
# FROM users AS u
# JOIN salaries AS sal ON sal.salary_id = u.salary_id
# WHERE u.salary_amount < 20000
For databases that support them, we can easily limit the amount of results also:
$query->limit(10);
# SELECT u.user_id, sals.salary_amount
# FROM users AS u
# JOIN salaries AS sal ON sal.salary_id = u.salary_id
# WHERE u.salary_amount > 20000
# LIMIT 10
pagination is also useful:
$query->offset(10);
# SELECT u.user_id, sals.salary_amount
# FROM users AS u
# JOIN salaries AS sal ON sal.salary_id = u.salary_id
# WHERE u.salary_amount > 20000
# LIMIT 10 OFFSET 10
There is also support for the other common constructs:
# ORDER BY u.name, sals.salary_amount
$query->orderby->list_push(
$tbl_salaries->col(name => 'salary_amount')
);
# GROUP BY u.user_id, sals.salary_amount
$query->orderby->list_push(
$tbl_users->col(name => 'user_id'),
$tbl_salaries->col(name => 'salary_amount'),
);
# DISTINCT
$query->distinct(1);
# HAVING
$query->having(SQL::Builder::BinaryOp->new(
op => ">",
lhs => SQL::Builder::AggregateFunction->new(
name => 'COUNT',
args => "*"
),
rhs => 0
))
DESCRIPTION
SQL::Builder::Select is a child of SQL::Builder::Base(3). Its functionality is provided by the objects it implements. These objects can be changed and even removed; any replacements will be passed through SQL::Builder::Base::dosql() before SQL serialization (sql()). It's possible to use these objects without provided a state (using strings instead of objects), but that would defeat much of the purpose of SQL::Builder.
Some of the example code in the SYNOPSIS is rather wordy, but the future interface will expected to be a little more lightweight. At the very least I expect to have exportable functions that wrap to the object constructors. The more intelligent way of removing wordiness is to subclass the objects you use. This will allow you to apply more useful defaults and states to the objects or constructs used in SQL queries. In the above example, it would have been wise to subclass SQL::Builder::Table for the "users" and "salaries" table. Through these we could encapsulate table name, alias, and write convenience methods. Instead of writing
$tbl_users->col(name => 'user_id')
one can write
Users->user_id
Future docs should cover this in more depth.
METHODS
_distinct()
A private method used to get/set the object used for managing the SELECT columns and toggle the DISTINCT keyword. When called with arguments, the value is set and current object ($self) is returned. Otherwise the value (or SQL::Builder::Distinct(3) object by default) is returned. distinct(), sql(), cols(), and distinct_on() rely on this method to act like SQL::Builder::Distinct(3) does.
_having()
A private method used to get/set the object used for manaing the HAVING clause. When called with arguments, the value is set and current object is returned; otherwise the current value (SQL::Builder::Having(3) by default) is returned. having() and sql() rely on this method to be like SQL::Builder::Having(3)
_limit()
A private method used to get/set the object used for manaing the LIMIT/OFFSET clauses. When called with arguments, the value is set and current object is returned; otherwise the current value (SQL::Builder::Limit(3) by default) is returned. limit(), offset() and sql() rely on this method to be like SQL::Builder::Limit(3)
children()
Return a SQL::Builder::Iterator(3) object to iterate over the values returned by _distinct(), tables(), where(), groupby(), _having(), orderby(), and _limit()
cols(@args)
This is a wrapper to _distinct()->cols(@args) which represents the columns used in a SELECT clause. See SQL::Builder::Distinct::cols()
distinct([0|1])
This can be used to turn on/off the DISTINCT keyword in a SELECT statement. When called with arguments, the keyword is turned on and distinct_on() values are effective... See SQL::Builder::Distinct(3). This method is wrapped using the object returned by _distinct()
distinct_on()
This is a wrapper to SQL::Builder::Distinct::on() which maintains a SQL::Builder::List(3) object by default. See SQL::Builder::Distinct(3). The object on which on() is called is obtained through _distinct()
groupby()
Get/set the groupby() object. This probably won't need to be called with arguments, but if it is, the value is set to the first argument and current object is returned; otherwise the current value (a SQL::Builder::GroupBy(3) object by default) is returned. Typical usage is like:
$select->groupby()->list_push("user_id");
which yields
GROUP BY user_id
See SQL::Builder::GroupBy(3) and SQL::Builder::Group(3) for more information
having()
This is a wrapper to SQL::Builder::Having::expr() which is populated with an "AND" SQL::Builder::BinaryOp(3) object. The object on which expr() is called is obtained through _having(). Typical usage is:
$select->having->list_push("foo > 10");
$select->having->list_push("bar > 20");
which should return something like:
HAVING foo > 10 AND bar > 20
See SQL::Builder::Having(3) and _having()
joins()
This is a wrapper to SQL::Builder::FromList::joins() which is called on tables(). This is used to add JOINs to the SELECT statement
$select->joins->list_push("JOIN salaries AS sal USING (salary_id)");
init()
This calls init() on the parent class and sets up the various members with default objects. See the rest of the documentation...
limit()
This is a wrapper to SQL::Builder::Limit::limit(), which is called on _limit()
$select->limit(10);
yields
LIMIT 10
See SQL::Builder::Limit(3)
offset()
This is a wrapper to SQL::Builder::Limit::offset(), which is called on _limit()
$select->offset(10);
yields
OFFSET 10
See SQL::Builder::Limit(3)
orderby()
Get/set the object used to manage the ORDER BY clause. By default this is populated with a SQL::Builder::GroupBy(3) object, but can be replaced by passing a new value, in which case the current object ($self) is returned. If no no arguments are passed, the current value is returned.
$select->orderby->list_push("user_id DESC");
yields
ORDER BY user_id DESC
See SQL::Builder::OrderBy(3) and SQL::Builder::Order(3)
sql()
This method generates the SQL serialization of the object by passing the values of _distinct(), tables(), where(), groupby(), _having(), orderby(), and _limit() through SQL::Builder::Base::dosql() and assembling the results to construct a SELECT statement. SQL::Builder makes few assumptions about what is valid SQL, so expect bad code to generate bad SQL. If no tables are set, this method will throw an exception. The SQL string is returned
tables()
By default this method returns a SQL::Builder::FromList(3) object, but can be replaced by passing an argument; if an argument is passed, the value is set and current object returned - otherwise the current value is returned. Typically this method is used like
$select->tables->list_push("users");
$select->tables->joins->list_push("JOIN salaries USING (salary_id)");
which generates something like
...
FROM users JOIN salaries USING (salary_id)
...
where()
This is the container for the WHERE clause. By default it's populated with a SQL::Builder::Where(3) object so manipulating the WHERE clause is easy. The WHERE object can be replaced by passing the replacement as an argument, in which case the value is set and current object is returned, otherwise the current value is returned. Typically
$select->where->list_push("foo > 10")
will generate
WHERE foo > 10
SEE ALSO
SQL::Builder(3) SQL::Builder::Distinct(3) SQL::Builder::Limit(3) SQL::Builder::GroupBy(3) SQL::Builder::Group(3) SQL::Builder::Having(3) SQL::Builder::Base(3) SQL::Builder::FromList(3) SQL::Builder::Table(3) SQL::Builder::Join(3) SQL::Builder::OrderBy(3) SQL::Builder::Order(3) SQL::Builder::Where(3)