SELECT
<< INSERT | FB 2.0 Language Reference | UPDATE >>
SELECT
Available in: DSQL, ESQL, PSQL
Aggregate functions: extended functionality
Changed in: 1.5
Description
Several types of mixing and nesting aggregate functions are supported since Firebird 1.5. They will be discussed in the following subsections. To get the complete picture, also look at the SELECT :: GROUP BY
sections.
Mixing aggregate functions from different contexts
Firebird 1.5 and up allow the use of aggregate functions from different contexts inside a single expression.
Example
select r.rdb$relation_name as "Table name", ( select max(i.rdb$statistics) || ' (' || count(*) || ')' from rdb$relation_fields rf where rf.rdb$relation_name = r.rdb$relation_name ) as "Max. IndexSel (# fields)" from rdb$relations r join rdb$indices i on (i.rdb$relation_name = r.rdb$relation_name) group by r.rdb$relation_name having max(i.rdb$statistics) > 0 order by 2
This admittedly rather contrived query shows, in the second column, the maximum index selectivity of any index defined on a table, followed by the table's field count between parentheses. Of course you would normally display the field count in a separate column, or in the column with the table name, but the purpose here is to demonstrate that you can combine aggregates from different contexts in a single expression.
Warning: Firebird 1.0 also executes this type of query, but gives the wrong results!
Aggregate functions and GROUP BY
items inside subqueries
Since Firebird 1.5 it is possible to use aggregate functions and/or expressions contained in the GROUP BY
clause inside a subquery.
Examples
This query returns each table's ID
and field count
. The subquery refers to flds.rdb$relation_name
, which is also a GROUP BY
item:
select flds.rdb$relation_name as "Relation name", ( select rels.rdb$relation_id from rdb$relations rels where rels.rdb$relation_name = flds.rdb$relation_name ) as "ID", count(*) as "Fields" from rdb$relation_fields flds group by flds.rdb$relation_name
The next query shows the last field from each table and and its 1-based position. It uses the aggregate function MAX
in a subquery.
select flds.rdb$relation_name as "Table", ( select flds2.rdb$field_name from rdb$relation_fields flds2 where flds2.rdb$relation_name = flds.rdb$relation_name and flds2.rdb$field_position = max(flds.rdb$field_position) ) as "Last field", max(flds.rdb$field_position) + 1 as "Last fieldpos" from rdb$relation_fields flds group by 1
The subquery also contains the GROUP BY
item flds.rdb$relation_name
, but that's not immediately obvious because in this case the GROUP BY
clause uses the column number.
Subqueries inside aggregate functions
Using a singleton subselect inside (or as) an aggregate function argument is supported in Firebird 1.5 and up.
Example
select r.rdb$relation_name as "Table", sum( (select count(*) from rdb$relation_fields rf where rf.rdb$relation_name = r.rdb$relation_name) ) as "Ind. x Fields" from rdb$relations r join rdb$indices i on (i.rdb$relation_name = r.rdb$relation_name) group by r.rdb$relation_name
Nesting aggregate function calls
Firebird 1.5 allows the indirect nesting of aggregate functions, provided that the inner function is from a lower SQL context. Direct nesting of aggregate function calls, as in "COUNT( MAX( price ) )
", is still forbidden and punishable by exception.
Example
See under Subqueries inside aggregate functions, where COUNT()
is used inside a SUM()
.
Aggregate statements: stricter HAVING
and ORDER BY
Firebird 1.5 and above are stricter than previous versions about what can be included in the HAVING
and ORDER BY
clauses. If, in the context of an aggregate statement, an operand in a HAVING
or ORDER BY
item contains a column name, it is only accepted if one of the following is true:
- The column name appears in an aggregate function call (e.g. "
HAVING MAX(SALARY) > 10000
"). - The operand equals or is based upon a non-aggregate column that appears in the
GROUP BY
list (by name or position).
"Is based upon" means that the operand need not be exactly the same as the column name. Suppose there's a non-aggregate column "STR
" in the select list. Then it's OK to use expressions like "UPPER(STR)
", "STR || '!'
" or "SUBSTRING(STR FROM 4 FOR 2)
" in the HAVING
clause – even if these expressions don't appear as such in the SELECT
or GROUP BY
list.
COLLATE
subclause for text BLOB columns
Added in: 2.0
Description
COLLATE
subclauses are now also supported for text BLOBs.
Example
select NameBlob from MyTable where NameBlob collate pt_br = 'João'
See also:
Collate
CREATE COLLATION
(Firebird 2.1)
New collations in Firebird 2.1?
New collations in Firebird 2
Derived tables ("SELECT FROM SELECT
")
Added in: 2.0
Description
A derived table is the result set of a SELECT
query, used in an outer SELECT
as if it were an ordinary table. In other words, it is a subquery in the FROM
clause.
Syntax
(select-query) [[AS] derived-table-alias] [(<derived-column-aliases>)] <derived-column-aliases> := column-alias [, column-alias ...]
Examples
The derived table (shown in red) in the query below contains all the relation names in the database followed by their field count. The outer SELECT
produces, for each existing field count, the number of relations having that field count.
select fieldcount, count(relation) as num_tables from (select r.rdb$relation_name as relation, count(*) as fieldcount from rdb$relations r join rdb$relation_fields rf on rf.rdb$relation_name = r.rdb$relation_name group by relation) group by fieldcount
A trivial example demonstrating the use of a derived table alias and column aliases list (both are optional):
select dbinfo.descr, dbinfo.def_charset from (select * from rdb$database) dbinfo (descr, rel_id, sec_class, def_charset)
Notes
- Derived tables can be nested.
- Derived tables can be unions and can be used in unions. They can contain aggregate functions, subselects and joins, and can themselves be used in aggregate functions, subselects and joins. They can also be or contain queries on selectable stored procedures. They can have
WHERE
,ORDER BY
andGROUP BY
clauses,FIRST
,SKIP
orROWS
directives, etc. etc. - Every column in a derived table must have a name. If it doesn't have one by nature (e.g. because it's a constant) it must either be given an alias in the usual way, or a column aliases list must be added to the derived table specification.
- The column aliases list is optional, but if it is used it must be complete, i.e. it must contain an alias for every column in the derived table.
- The optimizer can handle a derived table very efficiently. However, if the derived table is involved in an inner join and contains a subquery, then no join order can be made.
FIRST
and SKIP
Added in: 1.0
Changed in: 1.5
Better alternative: ROWS
Description
FIRST
limits the output of a query to the first so-many rows. SKIP
will suppress the given number of rows before starting to return output.
Tip: In Firebird 2.0 and up, use the SQL-compliant ROWS
syntax instead.
Syntax
SELECT [FIRST (<int-expr>)] [SKIP (<int-expr>)] <columns> FROM ... <int-expr> ::= Any expression evaluating to an integer. <columns> ::= The usual output column specifications.
Note: If <int-expr>
is an integer literal or a query parameter, the "()
" may be omitted. Subselects on the other hand require an extra pair of parentheses.
FIRST
and SKIP
are both optional. When used together as in FIRST m SKIP n
, the n
topmost rows of the output set are discarded and the first m
rows of the remainder are returned.
SKIP 0
is allowed, but of course rather pointless. FIRST 0
is allowed in version 1.5 and up, where it returns an empty set. In 1.0.x, FIRST 0
causes an error. Negative SKIP
and/or FIRST
values always result in an error.
If a SKIP
lands past the end of the dataset, an empty set is returned. If the number of rows in the dataset (or the remainder after a SKIP
) is less than the value given after FIRST
, that smaller number of rows is returned. These are valid results, not error situations.
Examples
The following query will return the first 10 names from the People
table:
select first 10 id, name from People order by name asc
The following query will return everything but the first 10 names:
select skip 10 id, name from People order by name asc
And this one returns the last 10 rows. Notice the double parentheses:
select skip ((select count(*) - 10 from People)) id, name from People order by name asc
This query returns rows 81–100 of the People table:
select first 20 skip 80 id, name from People order by name asc
Two Gotchas with FIRST
in subselects
- This:
delete from MyTable where ID in (select first 10 ID from MyTable)
ROWS
syntax, available since Firebird 2.0.
- Queries like:
...where F1 in (select first 5 F2 from Table2 order by 1 desc)
IN
predicate to the correlated EXISTS
predicate shown below. It's obvious that in this case FIRST N
doesn't make any sense:
...where exists ( select first 5 F2 from Table2 where Table2.F2 = Table1.F1 order by 1 desc )
See also:
FIRST (m) SKIP (n)
GROUP BY
Description
GROUP BY
merges rows that have the same combination of values and/or NULL
s in the item list into a single row. Any aggregate functions in the select list are applied to each group individually instead of to the dataset as a whole.
Syntax
SELECT ... FROM ... GROUP BY <item> [, <item> ...] ... <item> ::= column-name [COLLATE collation-name] | column-alias | column-position | expression
- Only non-negative integer literals will be interpreted as column positions. If they are outside the range from 1 to the number of columns, an error is raised. Integer values resulting from expressions or parameter substitutions are simply invariables and will be used as such in the grouping. They will have no effect though, as their value is the same for each row.
- A
GROUP BY
item cannot be a reference to an aggregate function (including those that are buried inside an expression) from the same context. - The select list may not contain expressions that can have different values within a group. To avoid this, the rule of thumb is to include each non-aggregate item from the select list in the
GROUP BY
list (whether by copying, alias or position).
Note: If you group by a column position, the expression at that position is copied internally from the select list. If it concerns a subquery, that subquery will be executed at least twice.
Grouping by alias, position and expressions
Changed in: 1.0, 1.5, 2.0
Description
In addition to column names, Firebird 2 allows column aliases, column positions and arbitrary valid expressions as GROUP BY
items.
Examples
These three queries all achieve the same result:
select strlen(lastname) as len_name, count(*) from people group by len_name select strlen(lastname) as len_name, count(*) from people group by 1 select strlen(lastname) as len_name, count(*) from people group by strlen(lastname)
History: Grouping by UDF results was added in Firebird 1. Grouping by column positions, CASE
outcomes and a limited number of internal functions in Firebird 1.5. Firebird 2 added column aliases and expressions in general as valid GROUP BY
items ("expressions in general" absorbing the UDF, CASE
and internal functions lot).
See also:
GROUP BY
HAVING
: stricter rules
Changed in: 1.5
Description
See Aggregate statements: stricter HAVING
and ORDER BY
.
JOIN
Ambiguous field names rejected
Changed in: 1.0
Description
InterBase 6 accepts and executes statements like the one below, which refers to an unqualified column name even though that name exists in both tables participating in the JOIN
:
select buses.name, garages.name from buses join garages on buses.garage_id = garage.id where name = 'Phideaux III'
The results of such a query are unpredictable. Firebird Dialect 3 returns an error if there are ambiguous field names in JOIN
statements. Dialect 1 gives a warning but will execute the query anyway.
CROSS JOIN
Added in: 2.0
Description
Firebird 2.0 supports CROSS JOIN
, which performs a full set multiplication on the tables involved. Previously you had to achieve this by joining on a tautology (a condition that is always true) or by using the comma syntax, now deprecated.
Syntax
SELECT ... FROM table1 CROSS JOIN table2 [WHERE ...] ...
Note: If you use CROSS JOIN
, you can't use ON
.
Example
select * from Men cross join Women order by Men.age, Women.age -- old syntax: -- select * from Men join Women on 1 = 1 -- order by Men.age, Women.age -- comma syntax: -- select * from Men, Women -- order by Men.age, Women.age
See also:
JOIN
ORDER BY
Syntax
SELECT ... FROM ... ... ORDER BY <ordering-item> [, <ordering-item> ...] <ordering-item> ::= {col-name | col-alias | col-position | expression} [COLLATE collation-name] [ASC[ENDING] | DESC[ENDING]] [NULLS {FIRST|LAST}]
Order by column alias
Added in: 2.0
Description
Firebird 2.0 and above support ordering by column alias.
Example
select rdb$character_set_id as charset_id, rdb$collation_id as coll_id, rdb$collation_name as name from rdb$collations order by charset_id, coll_id
Ordering by column position causes * expansion
Changed in: 2.0
Description
If you order by column position in a "SELECT *
" query, the engine will now expand the *
to determine the sort column(s).
Examples
The following wasn't possible in pre-2.0 versions:
select * from rdb$collations order by 3, 2
The following would sort the output set on Films.Director
in previous versions. In Firebird 2 and up, it will sort on the second column of Books
:
select Books.*, Films.Director from Books, Films order by 2
See also:
ORDER BY
Firebird 2.0.4 Release Notes: Improvements in sorting
Firebird 2.1 Release Notes: NULL
s ordering changed to comply with standard
Ordering by expressions
Added in: 1.5
Description
Firebird 1.5 introduced the possibility to use expressions as ordering items. Please note that expressions consisting of a single non-negative whole number will be interpreted as column positions and cause an exception if they're not in the range from 1 to the number of columns.
Example
select x, y, note from Pairs order by x+y desc
Note: The number of function or procedure invocations resulting from a sort based on a UDF or stored procedure is unpredictable, regardless whether the ordering is specified by the expression itself or by the column position number.
Notes
- The number of function or procedure invocations resulting from a sort based on a UDF or stored procedure is unpredictable, regardless whether the ordering is specified by the expression itself or by the column position number.
- Only non-negative whole number literals are interpreted as column positions. A whole number resulting from an expression evaluation or parameter substitution is seen as an integer invariable and will lead to a dummy sort, since its value is the same for each row.
See also:
ORDER BY
NULL
s placement
Changed in: 1.5, 2.0
Description
Firebird 1.5 has introduced the per-column NULLS FIRST
and NULLS LAST
directives to specify where NULL
s appear in the sorted column. Firebird 2.0 has changed the default placement of NULL
s.
Unless overridden by NULLS FIRST
or NULLS LAST
, NULL
s in ordered columns are placed as follows:
- In Firebird 1.0 and 1.5: at the end of the sort, regardless whether the order is ascending or descending.
- In Firebird 2.0 and up: at the start of ascending orderings and at the end of descending orderings.
See also the table below for an overview of the different versions.
NULL s placement | |||
Ordering | Firebird 1 | Firebird 1.5 | Firebird 2 |
order by Field [asc] | bottom | bottom | top |
order by Field desc | bottom | bottom | bottom |
order by Field [asc | desc] nulls first | — | top | top |
order by Field [asc | desc] nulls last | — | bottom | bottom |
Notes
- Pre-existing databases may need a backup-restore cycle before they show the correct
NULL
ordering behaviour under Firebird 2.0 and up. - No index will be used on columns for which a non-default
NULLS
placement is chosen. In Firebird 1.5, that is the case withNULLS FIRST
. In 2.0 and higher, withNULLS LAST
on ascending andNULLS FIRST
on descending sorts.
Examples
select * from msg order by process_time desc nulls first select * from document order by strlen(description) desc rows 10 select doc_number, doc_date from payorder union all select doc_number, doc_date from budgorder order by 2 desc nulls last, 1 asc nulls first
See also:
NULL
s ordering changed to comply with standard
ORDER BY
Firebird 2.0.4 Release Notes: Improvements in sorting
Stricter ordering rules with aggregate statements
Changed in: 1.5
Description
See Aggregate statements: stricter HAVING
and ORDER BY
.
PLAN
Available in: DSQL, ESQL, PSQL
Description
Specifies a user plan for the data retrieval, overriding the plan that the optimizer would have generated automatically.
Syntax
PLAN <plan_expr> <plan_expr> ::= [JOIN | [SORT] [MERGE]] (<plan_item> [, <plan_item> ...]) <plan_item> ::= <basic_item> | <plan_expr> <basic_item> ::= {table | alias} {NATURAL | INDEX (<indexlist>)) | ORDER index [INDEX (<indexlist>)]} <indexlist> ::= index [, index ...]
Handling of user PLAN
s improved
Changed in: 2.0
Description
Firbird 2 has implemented the following improvements in the handling of user-specified PLAN
s:
- Plan fragments are propagated to nested levels of joins, enabling manual optimization of complex outer joins.
- User-supplied plans will be checked for correctness in outer joins.
- Short-circuit optimization for user-supplied plans has been added.
- A user-specified access path can be supplied for any
SELECT
-based statement or clause.
ORDER
with INDEX
Changed in: 2.0
Description
A single plan item can now contain both an ORDER
and an INDEX
directive (in that order).
Example
plan (MyTable order ix_myfield index (ix_this, ix_that))
PLAN
must include all tables
Changed in: 2.0
Description
In Firebird 2 and up, a PLAN
clause must handle all the tables in the query. Previous versions sometimes accepted incomplete plans, but this is no longer the case.
See also:
PLAN
ROWS
Added in: 2.0
Description
Limits the amount of rows returned by the SELECT
statement to a specified number or range.
Syntax
With a single SELECT
:
SELECT <columns> FROM ... [WHERE ...] [ORDER BY ...] ROWS <m> [TO <n>] <columns> ::= The usual output column specifications. <m>, <n> ::= Any expression evaluating to an integer.
With a UNION
:
SELECT [FIRST p] [SKIP q] <columns> FROM ... [WHERE ...] [ORDER BY ...] UNION [ALL | DISTINCT] SELECT [FIRST r] [SKIP s] <columns> FROM ... [WHERE ...] [ORDER BY ...] ROWS <m> [TO <n>]
With a single argument m
, the first m
rows of the dataset are returned.
Points to note:
- If
m >
the total number of rows in the dataset, the entire set is returned. - If
m = 0
, an empty set is returned. - If
m < 0
, an error is raised.
With two arguments m
and n
, rows m
to n
of the dataset are returned, inclusively. Row numbers are 1-based.
Points to note when using two arguments:
- If
m >
the total number of rows in the dataset, an empty set is returned. - If
m
lies within the set butn
doesn't, the rows fromm
to the end of the set are returned. - If
m < 1
orn < 1
, an error is raised. - If
n = m-1
, an empty set is returned. - If
n < m-1
, an error is raised.
The SQL-compliant ROWS
syntax obviates the need for FIRST
and SKIP
, except in one case: a SKIP
without FIRST
, which returns the entire remainder of the set after skipping a given number of rows. (You can often "fake it" though, by supplying a second argument that you know to be bigger than the number of rows in the set.)
You cannot use ROWS
together with FIRST
and/or SKIP
in a single SELECT
statement, but is it valid to use one form in the top-level statement and the other in subselects, or to use the two syntaxes in different subselects.
When used with a UNION
, the ROWS
subclause applies to the UNION
as a whole and must be placed after the last SELECT
. If you want to limit the output of one or more individual SELECT
s within the UNION
, you have two options: either use FIRST/SKIP
on those SELECT
statements, or convert them to derived tables with ROWS
clauses.
ROWS
can also be used with the UPDATE
and DELETE
statements.
See also:
ROWS
Relation alias makes real name unavailable
Changed in: 2.0
Description
If you give a table or view an alias in a Firebird 2.0 or above statement, you must use the alias, not the table name, if you want to qualify fields from that relation.
Examples
Correct usage:
select pears from Fruit select Fruit.pears from Fruit select pears from Fruit F select F.pears from Fruit F
No longer possible:
select Fruit.pears from Fruit F
UNION
Available in: DSQL, ESQL, PSQL
UNION
s in subqueries
Changed in: 2.0
Description
UNION
s are now allowed in subqueries. This applies not only to column-level subqueries in a SELECT
list, but also to subqueries in ANY|SOME
, ALL
and IN
predicates, as well as the optional SELECT
expression that feeds an INSERT
.
Example
select name, phone, hourly_rate from clowns where hourly_rate < all (select hourly_rate from jugglers union select hourly_rate from acrobats) order by hourly_rate
UNION DISTINCT
Added in: 2.0
Description
You can now use the optional DISTINCT
keyword when defining a UNION
. This will show duplicate rows only once instead of every time they occur in one of the tables. Since DISTINCT
, being the opposite of ALL
, is the default mode anyway, this doesn't add any new functionality.
Syntax
SELECT (...) FROM (...) UNION [DISTINCT | ALL] SELECT (...) FROM (...)
Example
select name, phone from translators union distinct select name, phone from proofreaders
Translators who are also proofreaders (a not uncommon combination) will show up only once in the result set, provided their phone number is the same in both tables. The same result would have been obtained without DISTINCT
. With ALL
, they would appear twice.
See also:
UNION
WITH LOCK
Added in: 1.5
Description
WITH LOCK
provides a limited explicit pessimistic locking capability for cautious use in conditions where the affected row set is:
a. extremely small (ideally, a singleton), and
b. precisely controlled by the application code.
This is for experts only!
The need for a pessimistic lock in Firebird is very rare indeed and should be well understood before use of this extension is considered.
It is essential to understand the effects of transaction isolation and other transaction attributes before attempting to implement explicit locking in your application.
Syntax
SELECT ... FROM single_table [WHERE ...] [FOR UPDATE [OF ...]] WITH LOCK
If the WITH LOCK
clause succeeds, it will secure a lock on the selected rows and prevent any other transaction from obtaining write access to any of those rows, or their dependants, until your transaction ends.
If the FOR UPDATE
clause is included, the lock will be applied to each row, one by one, as it is fetched into the server-side row cache. It becomes possible, then, that a lock which appeared to succeed when requested will nevertheless fail subsequently, when an attempt is made to fetch a row which becomes locked by another transaction.
WITH LOCK
can only be used with a top-level, single-table SELECT
statement. It is not available:
- in a subquery specification;
- for joined sets;
- with the
DISTINCT
operator, aGROUP BY
clause or any other aggregating operation; - with a view;
- with the output of a selectable stored procedure;
- with an external table.
A lengthier, more in-depth discussion of "SELECT ... WITH LOCK
" is included in the Notes. It is a must-read for everybody who considers using this feature.
See also:
SELECT
statement
SQL Basics
DECLARE CURSOR
DELETE
INSERT
UPDATE
UPDATE OR INSERT
Firebird 2.0.4 Release Notes: RETURNING
clause for insert statements
INSERT INTO ... DEFAULT VALUES
RETURNING
Firebird 2.0 Language Reference Update: Understanding the WITH LOCK
clause
back to top of page
<< INSERT | FB 2.0 Language Reference | UPDATE >>