The syntax of SELECT is complex. This topic describes the syntax of SIMPLE SELECT.
Purpose
You can use this statement to query data from a table or view.
Syntax
simple_select:
SELECT [ hint_options ] [ DISTINCT | UNIQUE | ALL] select_expr_list
FROM from_list
[WHERE condition]
[hierarchical_query_clause]
[GROUP BY group_expression_list
[{ROLLUP | CUBE | GROUPING SETS} group_expression_list]
[HAVING condition]
]
[ORDER BY order_expression_list]
[FOR UPDATE [OF column] [ {NOWAIT | WAIT integer | SKIP LOCKED } ] ]
[row_limiting_clause ]
select_expr_list:
table_name.*
| table_alias_name.*
| expr [[AS] column_alias_name]
| sequence_name.{ CURRVAL|NEXTVAL }@dblink_name
from_list:
table_reference [, table_reference...]
table_reference:
simple_table
| joined_table
| pivot_clause
| unpivot_clause
| table_name@dblink_name
simple_table:
(table_factor [partition_option])[table_alias_name]
| (select_stmt) table_alias_name
| (table_reference_list)
joined_table:
table_reference [INNER] JOIN simple_table [join_condition]
| table_reference outer_join_type JOIN simple_table join_condition
partition_option:
PARTITION (partition_name_list)
partition_name_list:
partition_name [, partition_name...]
outer_join_type:
{LEFT | RIGHT | FULL} [OUTER]
join_condition:
ON expression
condition:
expression
group_expression_list:
group_expression [, group_expression...]
group_expression:
expression [ASC | DESC]
order_expression_list:
order_expression [, order_expression...]
order_expression:
expression [ASC | DESC]
row_limiting_clause:
[ OFFSET offset { ROW | ROWS } ]
[ FETCH { FIRST | NEXT } [ { rowcount | percent PERCENT } ]
{ ROW | ROWS } { ONLY | WITH TIES } ]
pivot_clause:
PIVOT
(aggregate_function ( expr ) [[AS] alias ]
[, aggregate_function ( expr ) [[AS] alias ]... ]
pivot_for_clause
pivot_in_clause
)
pivot_for_clause:
FOR { column| ( column [, column... ]) }
pivot_in_clause
IN
( { { expr| ( expr [, expr...] ) } [ [ AS] alias]... }
[, { { expr| ( expr [, expr...] ) } [ [ AS] alias] ...} ]
)
unpivot_clause:
UNPIVOT [ {INCLUDE | EXCLUDE} NULLS ]
( { column | ( column [, column... ]) }
pivot_for_clause
unpivot_in_clause
)
unpivot_in_clause:
IN
( { column | ( column [, column... ]) }[ AS { literal | ( literal [, literal... ]) } ]
[, { column | ( column [, column... ] ) }[ AS {literal | ( literal [, literal... ]) } ]]
)
hierarchical_query_clause:
[START WITH start_expression] CONNECT BY [NOCYCLE]
{PRIOR child_expr = parent_expr
| parent_expr = PRIOR child_expr} [ORDER SIBLINGS BY ...]
Parameters
| Parameter | Description |
|---|---|
| hint_options | (Optional) The hint options. |
| DISTINCT | UNIQUE | ALL | The returned data may contain duplicate rows.
|
| select_expr_list | The expressions or column names that you want to query from the database. Separate them with commas (,). You can use an asterisk (*) to indicate all columns.
|
| FROM table_references | The object from which data is selected. |
| PARTITION(partition_list) | The partitions from which data is selected in the table, for example, partition(p0,p1...). |
| table_factor | The name of the table (base table or updatable view) or special subquery. Function-based queries are supported. |
| table_alias_name | The alias of the object from which data is selected. |
| joined_table | The join method in a multi-table query. Valid values:
|
| ON expression | The join condition in a multi-table join. |
| WHERE where_conditions | The filter condition, which is optional. Only the data that meets the condition is returned. where_conditions is an expression. |
| hierarchical_query_clause | (Optional) The hierarchical query option. For more information, see hierarchical_query_clause. |
| GROUP BY group_by_list | Groups data by one or more columns. It is usually used in combination with aggregate functions. Note In a GROUP BY query, if no column following the SELECT clause uses any aggregate function, the columns following the SELECT clause must be specified in the GROUP BY clause. |
| ROLLUP group_expression_list | Rolls up the results of the groups created by the GROUP BY clause and generates the statistics. |
| CUBE group_expression_list | Unions all combinations of the items in the expression list, uses GROUP BY to group the union results, and generates the statistics. Note:
|
| GROUPING SETS group_expression_list | Specifies two or more data groups in a query, generates statistics, and aggregates and displays the statistics of the specified group. You can specify a single field or a list of fields in GROUPING SETS. |
| HAVING search_confitions | Filters the grouped data. The HAVING clause is similar to the WHERE clause, but the HAVING clause can reference an aggregate function such as SUM() and AVG(). |
| ORDER BY order_list | Sorts the query results by one or more columns in ascending (ASC) or descending (DESC) order. If ASC and DESC are not specified, the default value ASC is used.
|
| row_limiting_clause | Restricts the rows returned by a query, to implement a pagination query. You can specify the offset and the number of rows or percentage of rows to return. This parameter can be used with the ORDER BY clause to ensure the sorting order and obtain consistent results. |
| OFFSET | The number of rows to skip before a pagination query begins. offset must be a numeric value or an expression that evaluates to a numeric value.
|
| ROW | ROWS | Indicates a single row or multiple rows. You can select the keyword based on the number of rows to ensure clear semantics. |
| FETCH | The number of rows or percentage of rows to return. If this clause is not specified, all rows starting from the (offset + 1)th row are returned. |
| FIRST | NEXT | Specifies to obtain the number or percentage of rows to return in the first or next response. |
| rowcount | percent PERCENT | Use rowcount to specify the number of rows to return. rowcount must be a numeric value or an expression that evaluates to a numeric value. If you specify a negative number, rowcount is taken as 0. If rowcount is greater than the number of available rows starting from the (rowcount + 1)th row, all available rows are returned. If rowcount contains a fraction, the fraction is truncated. If rowcount is set to NULL, 0 rows are returned. Use percent PERCENT to specify the percentage of rows to return. percent must be a numeric value or an expression that evaluates to a numeric value. If you specify a negative number, percent is taken as 0. If percent is set to NULL, 0 rows are returned. If you do not specify rowcount or percent PERCENT, one row is returned. |
| ONLY | WITH TIES | ONLY specifies to return the specified number or percentage of rows. WITH TIES specifies to return other rows that have the same sorting key as the last row obtained. If you use WITH TIES, you must specify the ORDER BY clause. If you do not specify the ORDER BY clause, no other rows will be returned. |
| FOR UPDATE | (Optional) Adds an exclusive lock on all the rows in the query results to prevent other concurrent transactions from changing or reading the rows in some transaction isolation levels.
|
| pivot_clause | Pivots a row to a column. |
| aggregate_function | The aggregate function. |
| expr | An expression that evaluates to constant values. pivot_in_clause supports only constant expressions. |
| unpivot_clause | Pivots a column to a row. |
| dblink_name | The name of the database link (DBLink) to be accessed. |
| sequence_name | The sequence in the remote database to be accessed over the DBLink. The NEXTVAL and CURRVAL values of the SEQUENCE object can be accessed. |
hierarchical_query_clause
In a hierarchical query, you can use a special pseudocolumn LEVEL in the SELECT statement to indicate the level, namely, the hierarchy of the node. Counting from the start point of a query, the level starts from 1, and so forth. This parameter is valid only in a hierarchical query. For more information about pseudocolumns in hierarchical queries, see Hierarchical query pseudocolumns.
START WITH start_expression: optional, the root row in the hierarchical query.CONNECT BY: the method to determine the parent-child relationship. An equality expression is usually used. Other expressions are also supported.NOCYCLE: If this keyword is specified, the result can still be returned even if it contains a cycle. TheCONNECT_BY_ISCYCLEpseudocolumn can be used to specify where the cycle occurs. Otherwise, an error is returned.PRIOR child_expr = parent_expr | parent_expr = PRIOR child_expr:PRIORis a unary operator, indicating that the column in the parameter comes from the parent row. It has the same precedence as the unary operators+and-.ORDER SIBLINGS BY: the sorting order for rows of siblings of the same parent.
Notice
You cannot use the FOR UPDATE clause in a hierarchical query in the following scenarios:
- A subquery uses the
DISTINCTkeyword or an aggregate function. - A Common Table Expression (CTE) is used. In other words, you cannot use the
FOR UPDATEclause in aSELECTquery if the query already contains aWITH ... AS ...clause.
For more information about hierarchical queries, see Hierarchical query.
Examples
Query the data in the
namecolumn from thetbl1table.obclient> CREATE TABLE tbl1 (id INT,name VARCHAR(10),num INT); Query OK, 0 rows affected obclient> INSERT INTO tbl1 VALUES (1, 'a',100),(2, 'b',200),(3, 'a',50); Query OK, 3 rows affected Records: 3 Duplicates: 0 Warnings: 0 obclient> SELECT * FROM tbl1; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 1 | a | 100 | | 2 | b | 200 | | 3 | a | 50 | +------+------+------+ 3 rows in set obclient> SELECT name FROM tbl1; +------+ | NAME | +------+ | a | | b | | a | +------+ 3 rows in setQuery the data in the
namecolumn from thetbl1table and deduplicate the query results.obclient> SELECT DISTINCT name FROM tbl1; +------+ | NAME | +------+ | a | | b | +------+ 2 rows in setQuery the
id,name, andnumcolumns in thetbl1table, divide values in thenumcolumn by 2, and name the output column corresponding to thenumcolumn asavg.obclient> SELECT id, name, num/2 AS avg FROM tbl1; +------+------+------+ | ID | NAME | AVG | +------+------+------+ | 1 | a | 50 | | 2 | b | 100 | | 3 | a | 25 | +------+------+------+ 3 rows in setReturn the values of the corresponding
id,name, andnumcolumns in thetbl1table based on the filtering conditionname = 'a'.obclient> SELECT id, name, num FROM tbl1 WHERE name = 'a'; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 1 | a | 100 | | 3 | a | 50 | +------+------+------+ 2 rows in setQuery the
nameandnumvalues from thetbl1table, calculate the sum of thenumvalues byname, and return the rows with a sum less than160.obclient> SELECT name,SUM(num) sum FROM tbl1 GROUP BY name HAVING SUM(num) < 160; +------+------+ | NAME | SUM | +------+------+ | a | 150 | +------+------+ 1 row in setQuery the
id,name, andnumvalues from thetbl1table, and sort the results by thenumcolumn in ascending order.obclient> SELECT * FROM tbl1 ORDER BY num ASC; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 3 | a | 50 | | 1 | a | 100 | | 2 | b | 200 | +------+------+------+ 3 rows in setQuery all columns from the
tbl1table, and return the query results sorted in descending order bynameand in ascending order bynum.obclient> SELECT * FROM tbl1 ORDER BY name DESC,num ASC; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 2 | b | 200 | | 3 | a | 50 | | 1 | a | 100 | +------+------+------+ 3 rows in setQuery the row with a specified
idfrom thetbl1table, and use theFOR UPDATEclause to lock the row in the result set./*Query and lock the row where the ID is 1 in the tbl1 table in Session 1.*/ obclient> SELECT * FROM tbl1 WHERE id=1 FOR UPDATE; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 1 | a | 100 | +------+------+------+ 1 row in set /*Query and lock the rows where the ID is 1 or 2 in the tbl1 table in Session 2.*/ obclient> SELECT * FROM tbl1 WHERE id=1 or id=2 FOR UPDATE; OBE-30006: resource busy; acquire with WAIT timeout expired obclient> SELECT * FROM tbl1 WHERE id=1 or id=2 FOR UPDATE SKIP LOCKED; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 2 | b | 200 | +------+------+------+ 1 row in setCreate a table named
group_tbl1and insert data into it. Execute aGROUP BYquery withCUBE.obclient> CREATE TABLE group_tbl1 (group_id INT,job VARCHAR2(10),name VARCHAR2(10),salary INT); Query OK, 0 rows affected obclient> INSERT INTO group_tbl1 VALUES(10,'Coding','Bruce',1000), (10,'Programmer','Clair',1000), (20,'Coding','Jason',2000), (20,'Programmer','Joey',2000), (30,'Coding','Rebecca',3000), (30,'Programmer','Rex',3000); Query OK, 6 rows affected Records: 6 Duplicates: 0 Warnings: 0 obclient> SELECT * FROM group_tbl1; +----------+------------+---------+--------+ | GROUP_ID | JOB | NAME | SALARY | +----------+------------+---------+--------+ | 10 | Coding | Bruce | 1000 | | 10 | Programmer | Clair | 1000 | | 20 | Coding | Jason | 2000 | | 20 | Programmer | Joey | 2000 | | 30 | Coding | Rebecca | 3000 | | 30 | Programmer | Rex | 3000 | +----------+------------+---------+--------+ 6 rows in set obclient> SELECT group_id, salary, SUM(salary) FROM group_tbl1 GROUP BY CUBE (group_id, salary); +----------+--------+-------------+ | GROUP_ID | SALARY | SUM(SALARY) | +----------+--------+-------------+ | NULL | NULL | 12000 | | NULL | 1000 | 2000 | | NULL | 2000 | 4000 | | NULL | 3000 | 6000 | | 10 | NULL | 2000 | | 20 | NULL | 4000 | | 30 | NULL | 6000 | | 10 | 1000 | 2000 | | 20 | 2000 | 4000 | | 30 | 3000 | 6000 | +----------+--------+-------------+ 10 rows in setQuery rows from the
tbl1table bynameandnumand calculate the number of rows in each result set.obclient> SELECT name, num, COUNT(*) from tbl1 GROUP BY GROUPING SETS(name, num); +------+------+----------+ | NAME | NUM | COUNT(*) | +------+------+----------+ | a | NULL | 2 | | b | NULL | 1 | | NULL | 100 | 1 | | NULL | 200 | 1 | | NULL | 50 | 1 | +------+------+----------+ 5 rows in set )Pivot the rows in Table
emp_phoneto columns, and then pivot the columns to rows.obclient> CREATE TABLE emp(name VARCHAR2(50), num CHAR, phone VARCHAR2(50)); Query OK, 0 rows affected obclient> INSERT INTO emp VALUES('ZhangSan', '1', '1234-5678'),('ZhangSan', '2', '3219-6066'),('ZhangSan', '3', '5365-9583'); Query OK, 3 rows affected Records: 3 Duplicates: 0 Warnings: 0 obclient> SELECT * FROM emp; +----------+------+-----------+ | NAME | NUM | PHONE | +----------+------+-----------+ | ZhangSan | 1 | 1234-5678 | | ZhangSan | 2 | 3219-6066 | | ZhangSan | 3 | 5365-9583 | +----------+------+-----------+ 3 rows in set /*Pivot the rows in the emp table to columns.*/ obclient> SELECT * FROM emp PIVOT(MAX(phone) FOR num IN (1 AS home, 2 AS office, 3 AS mobile)); +----------+-----------+-----------+-----------+ | NAME | HOME | OFFICE | MOBILE | +----------+-----------+-----------+-----------+ | ZhangSan | 1234-5678 | 3219-6066 | 5365-9583 | +----------+-----------+-----------+-----------+ 1 row in set /*Pivot the columns in the emp table to rows.*/ obclient> CREATE VIEW v_emp AS SELECT * FROM emp PIVOT(MAX(phone) FOR num IN (1 AS home, 2 AS office, 3 AS mobile)); Query OK, 0 rows affected obclient> SELECT * FROM v_emp UNPIVOT(phone FOR num IN (home AS 1, office AS 2, mobile AS 3)); +----------+-----+-----------+ | NAME | NUM | PHONE | +----------+-----+-----------+ | ZhangSan | 1 | 1234-5678 | | ZhangSan | 2 | 3219-6066 | | ZhangSan | 3 | 5365-9583 | +----------+-----+-----------+ 3 rows in setQuery the data of a table in a remote database.
/*Access a remote OceanBase database to query data.*/ obclient> SELECT ID FROM tbl2@ob_dblink; +------+ | ID | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set obclient> SELECT * FROM tbl2@ob_dblink; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 1 | a | 100 | | 2 | b | 200 | | 3 | a | 50 | +------+------+------+ 3 rows in set /*Access a remote Oracle database to query data.*/ obclient> SELECT ID FROM tbl2@ora_dblink; +------+ | ID | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set obclient> SELECT * FROM tbl2@ora_dblink; +------+------+------+ | ID | NAME | NUM | +------+------+------+ | 1 | a | 100 | | 2 | b | 200 | | 3 | a | 50 | +------+------+------+ 3 rows in set /*Query the data in the local and remote databases concurrently.*/ obclient> SELECT t4.col1,t5.col2 FROM tbl1 t4, tbl2@ob_dblink t5 WHERE t1.col3=t2.col3; /*Query the data in different remote databases concurrently.*/ obclient> SELECT * FROM tbl2@ob_dblink t_remote1,tbl2@ora_dblink t_remote2 WHERE t_remote1.col1 = t_remote2.col1;Examples of pagination queries
Query the three employees with the smallest employee IDs (
empno).obclient> CREATE TABLE emp( empno NUMBER(4,0), empname VARCHAR(10), job VARCHAR(9), mgr NUMBER(4,0), hiredate DATE, sal NUMBER(7,2), comm NUMBER(7,2), deptno NUMBER(2,0), CONSTRAINT PK_emp PRIMARY KEY (empno) ); Query OK, 0 rows affected obclient> INSERT INTO emp VALUES (1839,'KING','PRESIDENT',null,'17-DEC-81',5000,null,10) ,(1698,'BLAKE','MANAGER',1839,'01-MAY-81',2850,null,30) ,(1782,'CLARK', 'MANAGER', 1839, '09-JUN-81', 2450, null, 10) ,(1566,'JONES','MANAGER',1839, '02-APR-81',2975,null,40) ,(1788,'SCOTT','ANALYST',1566, '15-JUL-87',3000,null,20) ,(1902,'FORD','ANALYST',1566, '05-OCT-81',3000,null,40) ,(1369,'SMITH','CLERK',1902, '17-NOV-80',800,null,20) ,(1499,'ALLEN','SALESMAN',1698, '20-FEB-81',1600,300,30); Query OK, 8 rows affected Records: 8 Duplicates: 0 Warnings: 0 obclient> SELECT empno, empname FROM emp ORDER BY empno FETCH FIRST 3 ROWS ONLY; +-------+---------+ | EMPNO | EMPNAME | +-------+---------+ | 1369 | SMITH | | 1499 | ALLEN | | 1566 | JONES | +-------+---------+ 3 rows in setQuery the three employees with the smallest employee IDs (
empno) again.obclient> SELECT empno, empname FROM emp ORDER BY empno FETCH NEXT 3 ROWS ONLY;Query the 25% of employees with the lowest salaries.
obclient> SELECT empno, empname sal FROM emp ORDER BY sal FETCH FIRST 25 PERCENT ROWS ONLY; +-------+-------+ | EMPNO | SAL | +-------+-------+ | 1499 | ALLEN | | 1698 | BLAKE | +-------+-------+ 2 rows in setQuery the 25% of employees with the lowest salaries and all other employees with the same salary as the last employee returned in the previous example.
obclient> SELECT empno, empname sal FROM emp ORDER BY sal FETCH FIRST 25 PERCENT ROWS WITH TIES; +-------+-------+ | EMPNO | SAL | +-------+-------+ | 1499 | ALLEN | | 1698 | BLAKE | +-------+-------+ 2 rows in set
Use the alias of the
t1table to query data from the table.obclient> CREATE TABLE t1(c1 INT, c2 INT ); Query OK, 0 rows affected obclient> INSERT INTO t1 VALUES ('1','2'); Query OK, 1 row affected obclient> SELECT * FROM (t1) a; +------+------+ | C1 | C2 | +------+------+ | 1 | 2 | +------+------+ 1 row in setUse
RANDOM()to generate random numbers and query the results of theGENERATOR()function directly by using theFROMclause.obclient> SELECT RANDOM(4) FROM GENERATOR(3); +---------------------+ | RANDOM(4) | +---------------------+ | 5267436225003336391 | | -851690886662571060 | | 1738617244330437274 | +---------------------+ 3 rows in setAccess sequence values in a remote database.
Log in to the local OceanBase database and create a DBLink for connecting to an Oracle tenant in a remote OceanBase database.
obclient> CREATE DATABASE LINK seq_link CONNECT TO test@oracle IDENTIFIED BY test HOST '127.xxx.xxx.xxx:2828'; Query OK, 0 rows affectedCreate a sequence named
my_seqin the remote OceanBase database.obclient> CREATE SEQUENCE my_seq START WITH 1 MINVALUE 1 MAXVALUE 10 INCREMENT BY 2 NOCYCLE NOORDER CACHE 30; Query OK, 0 rows affectedAccess the values of the sequence in the remote OceanBase database from the local database.
obclient> SELECT my_seq.NEXTVAL@seq_link FROM DUAL; +---------+ | NEXTVAL | +---------+ | 1 | +---------+ 1 row in set obclient> SELECT my_seq.CURRVAL@seq_link FROM DUAL; +---------+ | CURRVAL | +---------+ | 1 | +---------+ 1 row in set obclient> SELECT my_seq.NEXTVAL@seq_link FROM DUAL; +---------+ | NEXTVAL | +---------+ | 3 | +---------+ 1 row in set