Basic SQL subqueries
Subqueries are the bread and butter of making SQL effective and powerful as a programming language. They enable you to find lookup values, like primary keys. They let you validate whether values exist or don’t exist, and thereby serve to filter the result set of the master or outer query.
Subqueries are queries that run inside other queries or statements, like data manipulation statements – INSERT
, UPDATE
, and DELETE
. The query or statement that contains a subquery is also known as an outer query, and subqueries are inner queries.
You can nest subqueries within subqueries. I’ve never found the upward limit (in practice or the documentation) but there may be one. The logic of many nested subqueries can be daunting. I’d say three or four is probably a limit that should lead you to consider alternative solutions. Some developers overtly rely on subqueries to avoid complex joins semantics.
Subquery Definitions
Summary
There are three basic types of subqueries. The simplest and sometimes known as ordinary subqueries are those that return results independent of the outer query. A correlated query runs for each row of the outer query. Correlated queries act as audits of the existence or non-existence of a row based on a join between the outer and inner query. Last, there is an inline view. Inline views are queries that produce filtered result sets, and they as run time tables.
Ordinary subqueries come in a general form as a multiple row subqueries. A single row subquery is specialized form of a multiple row subquery because it must resolve to a single row. The most specialized ordinary subquery is a SQL expression, which is a single row subquery that returns only one column. The SQL expression is more commonly referred to as a scalar subquery.
Correlated subqueries let you validate whether values exist or don’t exist, and thereby serve to filter the result set of the master or outer query. They also let you perform complex associations to conditionally update or delete multiple rows in other tables. You should have unique indexes on these join columns because correlated subqueries run for each row returned by the outer query. Unique indexes let correlated queries perform faster with fewer computing resources.
Inline views let you create filtered data sets that aren’t defined as permanent catalog views. Inline views, like catalog views, become virtual tables that let you join their contents to other tables and catalog views.
Dictionary ↓
Word | Definition |
Base table |
A base table is any table qualified in the FROM of a query. The term implies that the data comes from a base location, which is the table. |
Catalog | A catalog is the data dictionary or metadata of a database management system. |
Catalog view | A catalog view is a query assigned a name and saved inside a database catalog. Most views join data from one or more tables to provide broader perspective of data. Catalog views don’t copy data from the sources, they only facilitate querying a filtered data set through a catalog name (view name). Many catalog views are updateable, which means you can read data from them and update it to their native tables where data are stored. Views that aren’t updateable contain aggregation expressions, set operators, or sotring operations. |
Correlated subquery | A correlated subquery is a subquery that contains a join to an outer query. The join may be to the immediate outer query or the outermost query. |
Data dictionary | A catalog is the data dictionary or metadata of a database management system. |
Inline view | A query that becomes a run time filtered result set or view of data. You can join an inline view to other tables, catalog views, or other inline views, provided they are in the scope of operation. |
Intermediate node query |
An intermediate node query is a query found in a SELECT or DML statement that contains one or more subqueries. |
Leaf node query | A subquery of another query or statement that contains no other subquery. |
Metadata | Metadata is data about data. Metadata is organized as a taxonomy to support structures that implement hierarchical, networked, relational, or object relational database. Metadata is stored in the data dictionary or catalog of a database. The patterns are similar but different in various implementations of database management systems. The database management system creates databases by building the data catalof first. |
Named view |
A named view is a view that is a catalog view, which means it was defined by a CREATE VIEW statement. Named views contain a query that marshals data from one or more base tables. |
Non-updateable view |
A non-updateable view is a view that: (1) contains a collection, set operator, subquery in a SELECT list, any aggregation syntax, or any analytic syntax (e.g., MODEL ; or (2) excludes a preserved key for any base table. |
Root node query |
A root node query is a SELECT statement or DML statement that contains one or more subqueries. |
Run time view |
A query that returns a filtered result set or view of data that exists in the FROM clause of a SELECT statement. A run time view is also known as an inline view. |
Select list |
A select list is the collection of columns and string or numeric literals returned in the SELECT clause of a query. |
Subquery | A subquery runs independently of the containing query. Subqueries are also known as ordinary subqueries to differentiate them from correlated subqueries. A subquery without any dependent subqueries is known as a leaf node. Subqueries always return a value, list of values, or table of values to their respective outer query. |
Updateable view |
An updateable view is a view that: (1) provides one key preserved table, which means a set of columns that includes the primary key of a base table; (2) supports a single DML (an INSERT , UPDATE , or DELETE ) statement that only affects a single base table; and (3) doesn’t contain a collection, set operator, subquery in a SELECT list, any aggregation syntax, or any analytic syntax (e.g., MODEL . Oracle also disallows an updateable view when it includes a WITH READ ONLY subquery, WITH CHECK OPTION , or any hierarchical join syntax. Updateable views are very dependent on the database management systems, and you should pay close attention to the supporting documentation. |
View | A view is a structure that is stored in the catalog view and contain a query that marshals data from one or more base tables. |
In short, subqueries fall into three categories – inside out, outside in, and bi-directional. Ordinary subqueries, or subqueries, are inside out, which means they run before the outer query and return the results to the outer query. Correlated subqueries are outside in because they run for each row returned by the outer query. Correlated subqueries match one or more outer query row’s column values against any equal number of the innner query row’s column values. Inline views are bi-directional, which means the way they’re joined is determined by the SQL cost optimizer. SQL cost optimizers are product specific to database management systems, and you can refer to this SQL join semantic blog post for more information.
The drawing illustrates the relationshps between queries and subqueries in the various scenarios. More or less subqueries return a value, a row of values, or a table of values, while a correlated subquery returns a Boolean acknowledgment of existence or non-existence. Inline views create filtered result sets that substitutes for a table or catalog view in a relational join pattern.
The next sections examine the specifics of the subquery types and inline view.
Scalar subquery ↓
A scalar subquery returns only one column for a single row and is also known as a SQL expression. You can use a scalar subquery in the VALUES
clause of an INSERT
statement, the WHERE
clause of a SELECT
, the SET
clause of an UPDATE
, and the WHERE
clause of a DELETE
statement.
Rules ↓
A scalar subquery returns a variable like a number, date, or string. Scalar variables are also known as primitives in some languages, like Java. It uses a SQL statement to look something up in the database based on business rules because business rules are more human friendly.
Description
A scalar subquery is one of the closest relative SQL has to a traditional pass-by-value function. A pass-by-value function can also be described as a black box, where you put some raw material in and get some processed product out.
A scalar subquery is independent of its outer query. Consider that a query with subqueries is like an inverted tree. The outermost query is the root node of the inverted tree. Any subqueries of the root node are branches, and subqueries of the first generation of subqueries are branches from branches. The lowest subquery in any sequence of branches is also known as a leaf node. Leaf nodes resolve first, followed by their siblings. After leaf nodes, the branch resolves that is their parent, the parent of the parent and so forth until all values are passed to the outermost subquery. If there’s another branch with leaf nodes, the behavior is repeated until all branches resolve with values to the outermost query. At this point the outermost query runs. This is why scalar subqueries are considered inside out queries.
The following demonstrates two nested scalar subqueries. The outer query depends on the result returned by the subquery to the address
table. While the subquery against the address
table depends on the result returned by the subquery to the telephone
table.
SELECT first_name||' '||last_name FROM contact WHERE contact_id = (SELECT a.contact_id -- An intermediate branch subquery. FROM address a WHERE a.address_id = (SELECT t.address_id -- A leaf node subquery. FROM telephone t WHERE t.telephone_id = 1001)); |
If either of the two subqueries returns no more than one row, the query would fail with an ORA-01427
error. The error means that a single row subquery returned more than one row. The scalar subquery architecture is risky because it is data centric, and it relies on both subqueries returning a single key that has only one parent. A multiple row subquery is a more flexible solution when coupled with an IN
or other multiple row comparison operator.
INSERT
statement ↓
An INSERT
statement can only use a scalar subquery in the VALUES
clause, which makes their use natural in this context. Scalar subqueries uses a SQL statement to look something up in the database based on business rules because business rules. Writing queries by using business rules is more user friendly.
You typically use a scalar subquery when you lookup a primary key to insert it as a foreign key column. An example of a scalar subquery would be looking up the common_lookup_id
for a DVD, as shown below:
INSERT INTO item VALUES ( item_s1.nextval ,'ASIN: B0001US8F8' ,(SELECT common_lookup_id -- Primary key scalar query lookup. FROM common_lookup WHERE common_lookup_type = 'DVD_WIDE_SCREEN') ,'Around the World in 80 Days' ,'Two-Disc Special Edition' ,'NR' ,'MPAA' ,'18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE); |
The scalar subquery above looks up a surrogate key based on a human friendly definition of DVD_WIDE_SCREEN
. Actually, that value isn’t too human friendly. The form or web page would actually present something like DVD Wide Screen, which would then map to the uppercase string shown in the example. You would need to define both columns in a table like the common_lookup_table
table. Then, every row can map and substitute the end-user friendly and code reusable values or vice versa.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT). UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
UPDATE
statement ↓
An UPDATE
statement can use a scalar subquery in the SET
or WHERE
clauses. Like an INSERT
statement, an UPDATE
statement uses SQL subqueries to look things up in the database. Lookup values also rely on business rules in UPDATE
statements because they are more human friendly.
You typically use a scalar subquery when looking up a single column value from another table or collections of tables. The SET
clause generally requires a scalar subquery or at least a single row subquery, but doesn’t impose a single row subquery constraint in all cases. You can use a multiple row subquery when you correlate the rows of a subquery with the rows of an UPDATE
statement.
A pair of scalar subqueries are shown in the UPDATE
statement.
UPDATE rental_item ri SET ri.rental_id = (SELECT r.rental_id -- A scalar subquery assignment. FROM rental r , contact c WHERE r.customer_id = c.contact_id AND c.last_name = 'Winn' AND c.first_name = 'Randi') WHERE ri.rental_id = (SELECT r.rental_id -- A scalar subquery comparison. FROM rental r , contact c WHERE r.customer_id = c.contact_id AND c.last_name = 'Winn' AND c.first_name = 'Brian'); |
The two scalar subqueries in the UPDATE
statement accomplish the same type of thing but in two different contexts. One works in the assignment of a new value through the SET
clause, while the other filters the rows to update in the WHERE
clause. You should note that both subqueries run before the outer query (in this case an UPDATE
statement). Scalar subqueries either assign values or compare values in an UPDATE
statement, and they assign or compare values through an equality, =
, operator.
A scalar subquery is data centric in this type of solution. The statement would throw an ORA-01427
error when two or more rows are returned by a subquery. A multiple row subquery is more flexible and data set independent when you combine it with an IN
or other multiple row comparison operator.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT). UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
DELETE
statement ↓
A DELETE
statement can only use a scalar subquery in the WHERE
clause. Scalar subqueries look something up in your database. They use business rules to do that. Business rules are typically more human friendly ways to find data.
You use a scalar subquery when looking up a single column value from another table or collections of tables. An example of a scalar subquery follows in a DELETE
statement.
DELETE FROM rental_item ri WHERE ri.rental_id = (SELECT r.rental_id -- A scalar subquery comparison. FROM rental r , contact c WHERE r.customer_id = c.contact_id AND c.last_name = 'Winn' AND c.first_name = 'Brian'); |
The scalar subquery in the WHERE
clause of the DELETE
statement runs first. The subquery returns a single scalar value. The DELETE
statement uses the value returned from the scalar subquery to filter which rows are deleted from a table. You should note that the subquery compares values through an equality, =
, comparison operator.
A scalar subquery is data centric in this type of solution. The statement would throw an ORA-01427
error when two or more rows are returned by a subquery. A multiple row subquery is more flexible and data set independent when you combine it with an IN
or other multiple row comparison operator.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT). UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
Single row subquery ↓
A single row subquery returns all columns for a single row. You can use a single row subquery as the select list of arguments for an INSERT
statement. You can also use a single row subquery in the WHERE
clause of a SELECT
, UPDATE
, and DELETE
statement. Like the scalar subquery, you use an equality, =
, comparison operator, but unlike a scalar subquery, you compare record structures not singular column values.
Rules ↓
A single row subquery returns a set of variables, or a row of data. The values are comma delimited and generally they’re scalar variables. Scalar variables are number, date, or string data types. Scalar variables are also known as primitives in some languages, like Java. Single row subqueries typically uses a SQL statement to look for a collection of things in the database. Single row subqueries rely on business rules because they are more human friendly than matching keys. You use an equality, =
, comparison operator to ensure only one row is returned. The equality comparison operator throws an ORA-01427
error when a subquery returns more than one row. This type of structure comparison lets you compare two comma separated lists of columns or values that are enclosed by parentheses.
Description
A single row subquery is a close relative in SQL to a pass-by-value function. Unlike a scalar subquery that returns a single value, a single row subquery returns a structure. The record structure of a single row subquery is a comma delimited list of columns or values. Pass-by-value functions are black boxes. Black boxes let you put some raw material in, process it, and get some related series of processed products out. The difference between a scalar and single row subquery is the return data type.
A single row subquery is independent of its outer query. Consider that a query with subqueries is like an inverted tree. The outermost query is the root node of the inverted tree. Any subqueries of the root node are branches, and subqueries of the first generation of subqueries are branches from branches. The lowest subquery in any sequence of branches is also known as a leaf node. Leaf nodes resolve first, followed by their siblings. After leaf nodes, the branch resolves that is their parent, the parent of the parent and so forth until all values are passed to the outermost subquery. If there’s another branch with leaf nodes, the behavior is repeated until all branches resolve with values to the outermost query. At this point the outermost query runs. This is why scalar subqueries are considered inside out.
The following demonstrates a single row subqueries. The outer query depends on the result returned by the subquery. The subquery uses a first name to filter the result set, and hopefully returns a single row of data. Naturally, this is just a sample program and the question could be resolved with a simple WHERE
clause and doesn’t require a subquery.
SELECT c.first_name||' '||c.last_name AS full_name FROM contact c WHERE (c.first_name, c.last_name) = (SELECT ic.first_name, ic.last_name -- Single row subquery. FROM contact ic WHERE ic.first_name = 'Randi'); |
If the subquery returns two rows, the query would fail with an ORA-01427
error. The error means that a single row subquery returns more than one row. This type of query architecture is risky because it is data centric. The occurrence of two first names that are alike would cause this subquery architecture to fail. It relies on the subquery returning a row of data that is only found once in the result set of a subquery. A multiple row subquery is typically a better solution when the number of row returns is uncertain because it can inspect multiple row returns with an IN
or other multiple row comparison operator.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT) as part of the record structure. UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
INSERT
statement ↓
An INSERT
statement can use a single row subquery when it provides all required values for a single row insertion. A subquery in an INSERT
statement can be a subquery, nested subquery, or an inline view. Since nested subqueries and inline views are discussed elsewhere, this section focuses on single row subqueries in INSERT
statements.
You eliminate the VALUES
clause when you use a subquery in an INSERT
statement. Parentheses around the subquery are optional in Oracle and precluded in MySQL. While a single row subquery works inside an INSERT
statement, so do multiple row subqueries. There is no real difference in the syntax between the two, and a multiple row subquery raises no exception. A multiple row subquery merely inserts more than one row.
Like the scalar query, a single row subquery lets you look for something in the database to insert into another table. The single row subquery relies on business rules to find the record set for the insertion. Business rules are typically more human friendly than mapping surrogate keys between tables. They also let you develop effective queries to find data.
The following INSERT
statement demonstrates a single row subquery that provides the required columns for a row insert into the item
table.
INSERT INTO item ( SELECT item_s1.nextval -- Single row subquery. , 'ASIN: B0001US8F8' , (SELECT common_lookup_id -- Leaf node scalar subquery. FROM common_lookup WHERE common_lookup_type = 'DVD_WIDE_SCREEN') , 'Around the World in 80 Days' , 'Two-Disc Special Edition' , 'NR' , 'MPAA' , '18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE FROM dual); |
The single row subquery above creates a row of data that also includes a nested scalar subquery or SQL expression. The SQL expression looks up the surrogate key based on the human friendly definition of DVD_WIDE_SCREEN
, and returns a foreign key value. The returned values becomes part of the record structure of the root query. You should note that the list of return columns in the SELECT
clause includes only string and numeric literals along with the SQL expression. While this could just as easily be written with a VALUES
clause, it illustrates the use of a subquery in an INSERT
statement.
As mentioned, the scalar subquery runs first as the leaf node, and it’s value is added to the list of values in the outer query. The outer query result set is then passed to the INSERT
statement for processing.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT) as part of the record structure. UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
UPDATE
statement ↓
An UPDATE
statement can use a single row subquery in the SET
or WHERE
clauses. Like the scalar subquery, it uses SQL statements to look for a set of related things in the database based. Single row subqueries typically use business rules to find the filtered result set. Like their name implies, single row subqueries return a single row of data, which is also known as a structure. A subquery makes finding a row of data easier because business rules are more human friendly.
The following demonstrates a single row subquery in the context of an UPDATE
statement.
UPDATE contact c SET (c.first_name,c.last_name) = (SELECT 'Lori','Winn' FROM dual) WHERE (c.first_name,c.last_name) = (SELECT 'Randi','Winn' FROM dual); |
There are two single row subqueries in the sample UPDATE
statement. One supports the assignment of new values in the SET
clause, while the other filters the data in the WHERE
clause. Both subqueries work with assigning or comparing a set of two column values.
You could rewrite the single row subquery statement with scalar subqueries, but it means each single row subquery activity becomes two scalar subqueries. The next example shows how you could implement scalar subqueries to do it. Remember, it’s only an example of what you shouldn’t do. It sole purpose is to show you the power of single-row subqueries.
UPDATE contact c SET c.first_name = (SELECT 'Lori' FROM dual) , c.last_name = (SELECT 'Winn' FROM dual) WHERE c.first_name = (SELECT 'Randi' FROM dual) AND c.last_name = (SELECT 'Winn' FROM dual); |
The advantage of comparing comma separated lists is a tremendous feature of single row subqueries. It is a technique often not fully understood, and seldom exploited frequently enough in real world SQL code.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT) as part of the record structure. UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
DELETE
statement ↓
A DELETE
statement can only use a single row subquery in the WHERE
clause. DELETE
statements use single-row subqueries SQL statements to look up a related row of things. The returned row becomes a structure that you can compare against a comma delimited set of columns in the WHERE
clause. A single-row subquery lets you use business rules to find data sets rather than manually inspecting the data.
You typically use a single row subquery when you lookup a record structure, as a set of column values from another table or collections of tables in a filtered join. The lookup follows a business rule that can’t easily be done by using manual inspection of the data. Below is an example of a DELETE
statement that compares a record structure returned by a subquery.
DELETE FROM contact c WHERE (c.first_name, c.last_name) = (SELECT 'Randi','Winn' FROM dual); |
The preceding example uses a single-row subquery in an DELETE
statement follows below. The subquery fabricates a row for one that may not exist in a database table.
Like the single-row subquery in the foregoing UPDATE
statement section, you can rewrite this row selection into two scalar subqueries. As mentioned in that section, there’s no purpose in doing so except for demonstrating the concept. A single-row subquery is a better relative solution between the two.
DELETE FROM contact c WHERE c.first_name = (SELECT 'Randi' FROM dual) AND c.last_name = (SELECT 'Winn' FROM dual); |
Both scalar subqueries must complete before the comparisons can be made in the WHERE
clause. If both single comparison conditions are met, the query returns qualified rows. It is the equivalent of writing two scalar subqueries.
In some implementations, like Oracle, you can also return a User-Defined Type (UDT) as part of the record structure. UDTs are typically objects, arrays, or lists, and they are specific to object-relational technology – more or less an Oracle database.
Under construction still … much bigger than I thought … I’ve finished the first two sections of multiple row subqueries … come back soon …
Multiple row subquery ↓
A multiple row subquery returns an aggregate table, which is a filtered result set of one or more columns and one or more rows. You can use a multiple row subquery in SELECT
, INSERT
, UPDATE
, or DELETE
statements. A multiple row subquery can replace the VALUES
clause in an INSERT
statement, like the single-row subquery discussed earlier. You can also put a multiple row subquery in the WHERE
clause of a SELECT
, UPDATE
, and DELETE
statement, but unlike scalar and single-row subqueries, multiple row subqueries can’t work with the equality, =
, operator. Multiple row subqueries require either the IN
operator, or and equality/inequality operator combined with an ALL
, ANY
, or SOME
operator.
Rules ↓
A multiple row subquery returns a list of record structures, which is a table of data. The return type is formally known as an aggregate table. The column list generally contains a list of scalar variables. Scalar variables are number, date, or string data types. Scalar variables are also known as primitives in some languages, like Java.
Description
Like the scalar and single-row subquery, multiple row subqueries are close relatives in SQL to a pass-by-value function. Unlike those seen earlier in the blog page, they return a two-dimensional structure instead of a scalar variable or record structure. As discussed, a pass-by-value function can also be described as a black box. In the case of a multiple row subquery, you put some raw material in and get a table of processed products out.
You typically use a multiple row subquery when you lookup a related set of information that has more than one row. It is important to note that all columns must return the same number of rows of data. For example, you can’t use a scalar subquery in the select list of a multiple row subquery without raising an ORA-01427
error because it returns only one row by definition. This is the opposite of data centric errors from single-row subqueries when the nested query actually returns more rows than the base query.
The following base subquery returns string and numeric literals for all but one column, which is returned by what is hoped to be a scalar subquery. Unfortunately, the business logic is fatally flawed and the subquery returns three rows.
SELECT 'ASIN: B0001US8F8' , (SELECT common_lookup_id -- Actually returns more than one row. FROM common_lookup WHERE common_lookup_type LIKE 'DVD%') , 'Around the World in 80 Days' , 'Two-Disc Special Edition' , 'NR' , 'MPAA' , '18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE FROM dual; |
The imbalance between the number of rows returned between columns, triggers an ORA-01427
, as noted below.
, (SELECT common_lookup_id * ERROR at line 3: ORA-01427: single-ROW subquery RETURNS more than one ROW |
You can fix this type of problem without a great deal of difficulty. You simply cross join the queries, which produces a product of the rows of one times the rows of the other. This technique balances the select list elements.
SELECT 'ASIN: B0001US8F8' , il.common_lookup_id -- Column value from inline view. , 'Around the World in 80 Days' , 'Two-Disc Special Edition' , 'NR' , 'MPAA' , '18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE FROM dual CROSS JOIN (SELECT common_lookup_id FROM common_lookup WHERE common_lookup_type LIKE 'DVD%') il; |
The multiple row subquery creates more than one row of data by cross joining against what was a subquery in the previous example. The subquery becomes an inline view to resolve the row imbalance. You create a repeating group of the base query through the multiplication property of the cross join or Cartesian product. This technique lets you resolve the row imbalanced.
INSERT
statement ↓
An INSERT
statement can use a single row subquery as a subquery only. When you use a subquery in an INSERT
statement, you eliminate the VALUES
clause. Like the scalar query, you use a single row subquery to look something up in the database based on business rules because business rules are more human friendly.
You typically use a multiple row subquery when you lookup a related set of information that has more than one row. In an INSERT
statement, a multiple row subquery performs much like a single-row subquery. It replaces the VALUES
clause with a SELECT
statement. It is important to note that all columns must return the same number of rows of data. For example, you can’t use a scalar subquery in the select list of a multiple row subquery without raising an ORA-01427
error because it returns only one row by definition. This is the opposite of data centric errors from single-row subqueries when the nested query actually returns more rows than the base query.
The following base subquery is the sample used previously in the description of multiple row subqueries. It returns string and numeric literals for all but one column, which makes the base query a single-row subquery. The non-compliant column is actually a multiple row subquery. While a query may return only one row with test data, its actual performance may return more than one row. When a planned query performs in an unanticipated way, the business logic that formed it is fatally flawed. This is the case in the example because the subquery returns two rows.
INSERT INTO item ( SELECT item_s1.nextval , 'ASIN: B0001US8F8' , (SELECT common_lookup_id FROM common_lookup WHERE common_lookup_type LIKE 'DVD%') , 'Around the World in 80 Days' , 'Two-Disc Special Edition' , 'NR' , 'MPAA' , '18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE FROM dual); |
The imbalance between the number of rows returned between columns, triggers an ORA-01427
, as noted below.
SQL> / , (SELECT common_lookup_id * ERROR at line 4: ORA-01427: single-ROW subquery RETURNS more than one ROW |
You can fix this type of problem without a great deal of difficulty. As you did in the previous query only example, you simply cross join the queries. The cross join produces a Cartesian product of the rows, and effectively balances row returns of the select list elements.
INSERT INTO item ( SELECT item_s1.nextval , 'ASIN: B0001US8F8' , il.common_lookup_id , 'Around the World in 80 Days' , 'Two-Disc Special Edition' , 'NR' , 'MPAA' , '18-MAY-2004' , 3 , SYSDATE , 3 , SYSDATE FROM dual CROSS JOIN (SELECT common_lookup_id FROM common_lookup WHERE common_lookup_type LIKE 'DVD%') il); |
When you use this technique, the query that returns one row and the query that returns many rows are matched. This happens because a cross join effectively multiplies the one side of the relationship by the number of rows on the many side of the join.
UPDATE
statement ↓
An UPDATE
statement can use a single row subquery in the SET
or WHERE
clauses. Like the scalar subquery, it uses SQL statements to look for a set of related things in the database based. Single row subqueries typically use business rules to find the filtered result set. Like their name implies, single row subqueries return a single row of data, which is also known as a structure. A subquery makes finding a row of data easier because business rules are more human friendly.
The following demonstrates a single row subquery in the context of an UPDATE
statement.
UPDATE contact c SET (c.first_name,c.last_name) = (SELECT 'Lori','Winn' FROM dual) WHERE (c.first_name,c.last_name) = (SELECT 'Randi','Winn' FROM dual); |
There are two single row subqueries in the sample UPDATE
statement. One supports the assignment of new values in the SET
clause, while the other filters the data in the WHERE
clause. Both subqueries work with assigning or comparing a set of two column values.
You could rewrite the single row subquery statement with scalar subqueries, but it means each single row subquery activity becomes two scalar subqueries. The next example shows how you could implement scalar subqueries to do it. Remember, it’s only an example of what you shouldn’t do, so you see the power of single-row subqueries.
UPDATE contact c SET c.first_name = (SELECT 'Lori' FROM dual) , c.last_name = (SELECT 'Winn' FROM dual) WHERE c.first_name = (SELECT 'Randi' FROM dual) AND c.last_name = (SELECT 'Winn' FROM dual); |
The advantage of comparing comma separated lists is a tremendous feature of single row subqueries. It is a technique often not fully understood, and seldom exploited frequently enough in real world SQL code.
DELETE
statement ↓
A DELETE
statement can use a single row subquery in the only the WHERE
clause. It uses single row subqueries SQL statements to look up a related set of things that can be returned in a single row. It allows you to leverage business rules because business rules to solve problems rather than manual inspection of the data.
You typically use a single row subquery when you lookup a set of column values from another table or collections of tables in a filtered join. The lookup follows a business rule that mere mortals can remember. An example of a scalar subquery in an DELETE
statement with table fabrication is shown below:
DELETE FROM contact c WHERE (c.first_name, c.last_name) = (SELECT 'Randi','Winn' FROM dual); |
Like the single row subquery in an UPDATE
statement, you can rewrite this deletion to use two scalar subqueries. The example is:
DELETE FROM contact c WHERE c.first_name = (SELECT 'Randi' FROM dual) AND c.last_name = (SELECT 'Winn' FROM dual); |
The single row subquery in the WHERE
clause of the DELETE
statement runs first. It returns a single row with multiple values, and is the equivalent of writing two scalar subqueries.
Correlated subquery ↓
A correlated subquery returns an aggregate table, which is a filtered against every row returned by the outer query. You can use a correlated subquery in the SELECT
clause, or in the WHERE
clause of a SELECT
, UPDATE
, and DELETE
statement.
A correlated subquery returns an aggregate table, which is a filtered against every row returned by the outer query. You can use a correlated subquery in the SELECT
clause, or in the WHERE
clause of a SELECT
, UPDATE
, and DELETE
statement.
Dummy text …
Inline view ↓
An inline view is a query placed inside the FROM
clause or inside a WITH
clause. It allows you to use a filtered data set not found in a table like a view, but only in the scope of the query or SQL statement.
The WITH
clause is the newer form of an inline query. It is called a subquery factoring clause by Oracle and a Common Table Expression (CTE) by Microsoft in SQL Server 2005 forward. It isn’t implemented in MySQL.
You can find the WITH
clause qualified in the Oracle Database 2 Day + Data Warehousing Guide 11g, Release 1. The following looks at this approach, and compares using the WITH
clause instead of the inline view to perform table fabrication.
Oracle tells us to use the WITH
clause when a query has multiple references to the same query block and there are joins and aggregations. The official name of the WITH
clause is a subquery factoring clause. Basically, the WITH
clause lets you name inline views and then reuse them inside other inline views. This behavior avoids having to reuse the same inline view twice in different parts of a query, which reduces overhead and increases view resolution. Like PL/SQL, they must be defined before they can be referenced. Unlike PL/SQL, the queries have no equivalent for forward referencing a query block.
The WITH
clause comes from the ANSI SQL:1999 specification. It is implemented with the same syntax in Microsoft SQL Server 2005 as a CTE, but it also happens to be where Microsoft embedded recursive queries.
The basic syntax is:
The first code block is assigned the inline
name. You can then reuse the inline
code block inside any subsequent code block or the master query. The idea is that this syntax is simpler than the generic syntax.
The standard way of writing this is consistent across other SQL databases, and I don’t really see it as any more complex than the WITH
syntax Oracle provides.
The WITH
clause is also capable of letting you create tables from literal values, which is known as table fabrication. The following syntax uses the with clause to fabricate a table of two columns (x
and y
) and two rows.
SQL> WITH fabricated AS 2 (SELECT 1 AS x, 2 AS y FROM dual 3 UNION ALL 4 SELECT 3 AS x, 4 AS y FROM dual) 5 SELECT x, y FROM fabricated; |
This produces the following results:
X Y ---------- ---------- 1 2 3 4 |
The next shows the traditional way of fabricating a table using an inline view:
SQL> SELECT x, y 2 FROM (SELECT 1 AS x, 2 AS y FROM dual 3 UNION ALL 4 SELECT 3 AS x, 4 AS y FROM dual) fabricated; |
This also produces the same results as before, two rows of X
and Y
variables.
You can also use this type of syntax in MySQL to fabricate a table. You can’t use the WITH
clause in MySQL because it’s not supported. You’ll notice in the example that the FROM dual
portion is omitted in MySQL. Wouldn’t it be nice if Oracle let that happen too?
SQL> SELECT x, y 2 FROM (SELECT 1 AS x, 2 AS y 3 UNION ALL 4 SELECT 3 AS x, 4 AS y) fabricated; |
A neat function that I picked up on the Oracle Technical Network is the NUMTODSINTERVAL
(number to date-stamp interval) function, which can create intervals for qualifying sales by hour or quarter hour. More or less it is a way to fabricate timing intervals. Here’s a quick example:
SQL> SELECT TO_CHAR(SYSDATE - NUMTODSINTERVAL(dist,'HOUR') 2 ,'DD-MON-YYYY HH24:MI:SS') bracket 3 FROM (SELECT 1 AS dist FROM dual 4 UNION ALL 5 SELECT 2 AS dist FROM dual 6 UNION ALL 7 SELECT 3 AS dist FROM dual) fabricated; |
The output is:
BRACKET ------------------- 22-OCT-2008 23:07:15 22-OCT-2008 22:07:15 22-OCT-2008 21:07:15 |
This has been the syntax, now I’ll have to check whether there are any performance differences. I suspect that since the execution plan is the same that there aren’t any performance differences but you never know until you test it.
More or less they were but I tripped into a performance shortfall of the WITH
clause. It was a complete accident when I was trying to convert a MySQL SQL syntax model into Oracle SQL. The smaller change to the code was to use a WITH clause but I found it didn’t work.
You can’t use the WITH
clause inside a subquery for a multiple row insert. It raises an ORA-32034
error if you attempt it, which struck me as bizare. A normal inline view works fine but the WITH
clause doesn’t.
Here’s a simple example of embedding an inline view into an INSERT
statement. It works seamlessly in Oracle 11g:
INSERT INTO contact_copy ( SELECT contact_s1.nextval , 1001 , cl.contact_type , 'Doe' , 'John' , NULL , 3 , SYSDATE , 3 , SYSDATE FROM dual CROSS JOIN (SELECT common_lookup_id AS contact_type FROM common_lookup WHERE common_lookup_type LIKE '%DAY RENTAL') cl ); |
When I switched to what appeared as the equivalent syntax using a WITH
clause, it failed and raised the ORA-32034: unsupported use of with clause
error. The following shows you how the WITH
would be used, if it could be used:
INSERT INTO contact_copy ( WITH cl AS (SELECT common_lookup_id AS contact_type FROM common_lookup WHERE common_lookup_type LIKE '%DAY RENTAL') SELECT contact_s1.nextval , 1001 , cl.contact_type , 'Doe' , 'John' , NULL , 3 , SYSDATE , 3 , SYSDATE FROM dual ); |
I thought that might be the reason why Oracle didn’t bother putting it in the SQL reference manual for Oracle Database 10g or 11g. However, Dominic Brooks provided the correct syntax. Very interesting that you simply start with the WITH
clause and exclude the enclosing parentheses. Quite a departure from the normal syntax for a multiple row insert.
The correct syntax is when the cl
subquery returns one row is like Dominic’s:
INSERT INTO contact_copy WITH cl AS (SELECT common_lookup_id AS contact_type FROM common_lookup WHERE common_lookup_type LIKE '%DAY RENTAL') SELECT contact_s1.nextval , 1001 , (SELECT cl.contact_type FROM cl) , 'Doe' , 'John' , NULL , 3 , SYSDATE , 3 , SYSDATE FROM dual; |
The correct syntax is when the cl
subquery returns more than one row differs from Dominic’s. You need a CROSS JOIN
to multiply the other static values by the number of rows returned by the subquery so that you have a multiple row insert statement.
INSERT INTO contact_copy WITH cl AS (SELECT common_lookup_id AS contact_type FROM common_lookup WHERE common_lookup_type LIKE '%DAY RENTAL') SELECT contact_s1.nextval , 1001 , cl.contact_type , 'Doe' , 'John' , NULL , 3 , SYSDATE , 3 , SYSDATE FROM dual CROSS JOIN cl; |
Code Examples of these 3 types of subqueries, such as what you have for your article on inline views ( https://blog.mclaughlinsoftware.com2008/10/19/inline-views-table-fabrication-and-the-with-clause/ ) would help me understand better what is described here.
Steve
8 Jun 09 at 10:54 am
Never mind- I just didn’t go deep enough in the expand views to get to them. Thanks.
Steve
8 Jun 09 at 10:57 am
hai,
mastan
21 Aug 12 at 1:05 am