[关闭]
@WrRan 2016-11-17T08:59:13.000000Z 字数 13468 阅读 2051

node-oracledb

9. SQL Execution

A SQL or PL/SQL statement may be executed using the Connection
execute() method. Either the callback style shown
below, or promises may be used.

After all database calls on the connection complete, the application
should use the connection.close() call to
release the connection.

Queries may optionally be streamed using the Connection
queryStream() method.

9.1 SELECT Statements

9.1.1 Fetching Rows

By default, query results are returned all at once in the rows
property of result parameter to the Connection
execute() callback. The number of rows returned is
restricted to maxRows:

  1. connection.execute(
  2. "SELECT department_id, department_name "
  3. + "FROM departments "
  4. + "WHERE department_id = :did",
  5. [180],
  6. { maxRows: 10 }, // a maximum of 10 rows will be returned. Default limit is 100
  7. function(err, result)
  8. {
  9. if (err) { console.error(err.message); return; }
  10. console.log(result.rows); // print all returned rows
  11. });

9.1.2 Result Set Handling

When the number of query rows is relatively big, or can't be
predicted, it is recommended to use a ResultSet
with callbacks, as described in this section, or via the ResultSet
stream wrapper, as described later. This
prevents query results being unexpectedly truncated by the
maxRows limit and removes the need to oversize
maxRows to avoid such truncation. Otherwise, for queries that
return a known small number of rows, non-result set queries may have
less overhead.

A result set is created when the execute() option property
resultSet is true. Result set rows can be
fetched using getRow() or getRows() on the
execute() callback function's result.resultSet parameter property.

For result sets the maxRows limit is ignored. All
rows can be fetched.

When all rows have been fetched, or the application does not want to
continue getting more rows, then the result set should be freed using
close().

REF CURSORS returned from a PL/SQL block via an oracledb.CURSOR OUT
bind are also available as a ResultSet. See
REF CURSOR Bind Parameters.

The format of each row will be an array or object, depending on the
value of outFormat.

See resultset1.js,
resultset2.js
and refcursor.js
for full examples.

To fetch one row at a time use getRow() :

  1. connection.execute(
  2. "SELECT employee_id, last_name FROM employees ORDER BY employee_id",
  3. [], // no bind variables
  4. { resultSet: true }, // return a result set. Default is false
  5. function(err, result)
  6. {
  7. if (err) { . . . }
  8. fetchOneRowFromRS(connection, result.resultSet);
  9. });
  10. });
  11. function fetchOneRowFromRS(connection, resultSet)
  12. {
  13. resultSet.getRow( // get one row
  14. function (err, row)
  15. {
  16. if (err) {
  17. . . . // close the result set and release the connection
  18. } else if (!row) { // no rows, or no more rows
  19. . . . // close the result set and release the connection
  20. } else {
  21. console.log(row);
  22. fetchOneRowFromRS(connection, resultSet); // get next row
  23. }
  24. });
  25. }

It is generally more efficient to fetch multiple rows at a time using getRows():

  1. var numRows = 10; // number of rows to return from each call to getRows()
  2. connection.execute(
  3. "SELECT employee_id, last_name FROM employees ORDER BY employee_id",
  4. [], // no bind variables
  5. { resultSet: true }, // return a result set. Default is false
  6. function(err, result)
  7. {
  8. if (err) { . . . }
  9. fetchRowsFromRS(connection, result.resultSet, numRows);
  10. });
  11. });
  12. function fetchRowsFromRS(connection, resultSet, numRows)
  13. {
  14. resultSet.getRows( // get numRows rows
  15. numRows,
  16. function (err, rows)
  17. {
  18. if (err) {
  19. . . . // close the result set and release the connection
  20. } else if (rows.length > 0) { // got some rows
  21. console.log(rows); // process rows
  22. if (rows.length === numRows) // might be more rows
  23. fetchRowsFromRS(connection, resultSet, numRows);
  24. else // got fewer rows than requested so must be at end
  25. . . . // close the result set and release the connection
  26. } else { // else no rows
  27. . . . // close the result set and release the connection
  28. }
  29. });
  30. }

9.1.3 Streaming Query Results

Streaming query results allows data to be piped to other streams, for
example when dealing with HTTP responses.

Use connection.queryStream() to create a stream from
a top level query and listen for events. You can also call
connection.execute() and use
toQueryStream() to return a stream from the
returned ResultSet or OUT bind REF CURSOR
ResultSet.

With streaming, each row is returned as a data event. Query
metadata is available via a metadata event. The end event
indicates the end of the query results.

The connection must remain open until the stream is completely read.

The query stream implementation is a wrapper over the
ResultSet Class. In particular, calls to
getRows() are made internally to fetch each successive
subset of data, each row of which will generate a data event. The
number of rows fetched from the database by each getRows() call is
set to the value of oracledb.maxRows. This value
does not alter the number of rows returned by the stream since
getRows() will be called each time more rows are needed. However
the value can be used to tune performance, as also can the value of
oracledb.prefetchRows.

Query results must be fetched to completion to avoid resource leaks.
The ResultSet close() call for streaming query results will be
executed internally when all data has been fetched. If you need to be
able to stop a query before retrieving all data, use a
ResultSet with callbacks. (Note: An
experimental querystream._close() method exists to terminate a
stream early. It is under evaluation, may changed or be removed, and
should not be used in production.)

An example of streaming query results is:

  1. var stream = connection.queryStream('SELECT employees_name FROM employees');
  2. stream.on('error', function (error) {
  3. // handle any error...
  4. });
  5. stream.on('data', function (data) {
  6. // handle data row...
  7. });
  8. stream.on('end', function () {
  9. // release connection...
  10. });
  11. stream.on('metadata', function (metadata) {
  12. // access metadata of query
  13. });
  14. // listen to any other standard stream events...

See
selectstream.js
for a runnable example using connection.queryStream().

The REF CURSOR Bind Parameters section shows using
toQueryStream() to return a stream for a REF CURSOR.

9.1.4 Query Output Formats

Query rows may be returned as an array of column values, or as
Javascript objects, depending on the values of
outFormat.

The default format for each row is an array of column values.
For example:

  1. var oracledb = require('oracledb');
  2. . . .
  3. connection.execute(
  4. "SELECT department_id, department_name " +
  5. "FROM departments " +
  6. "WHERE manager_id < :id",
  7. [110], // bind value for :id
  8. function(err, result)
  9. {
  10. if (err) { console.error(err.message); return; }
  11. console.log(result.rows);
  12. });

If run with Oracle's sample HR schema, the output is:

  1. [ [ 60, 'IT' ], [ 90, 'Executive' ], [ 100, 'Finance' ] ]

Using this format is recommended for efficiency.

Alternatively, rows may be fetched as JavaScript objects. To do so,
specify the outFormat option to be OBJECT:

  1. oracledb.outFormat = oracledb.OBJECT;

The value can also be set as an execute() option:

  1. connection.execute(
  2. "SELECT department_id, department_name " +
  3. "FROM departments " +
  4. "WHERE manager_id < :id",
  5. [110], // bind value for :id
  6. { outFormat: oracledb.OBJECT },
  7. function(err, result)
  8. {
  9. if (err) { console.error(err.message); return; }
  10. console.log(result.rows);
  11. });

The output is:

  1. [ { DEPARTMENT_ID: 60, DEPARTMENT_NAME: 'IT' },
  2. { DEPARTMENT_ID: 90, DEPARTMENT_NAME: 'Executive' },
  3. { DEPARTMENT_ID: 100, DEPARTMENT_NAME: 'Finance' } ]

In the preceding example, each row is a JavaScript object that
specifies column names and their respective values. Note the property
names follow Oracle's standard name-casing rules. They will commonly
be uppercase, since most applications create tables using unquoted,
case-insensitive names.

9.1.5 Query Column Metadata

The column names of a query are returned in the execute() callback's
result.metaData attribute:

  1. connection.execute(
  2. "SELECT department_id, department_name " +
  3. "FROM departments " +
  4. "WHERE manager_id < :id",
  5. [110], // bind value for :id
  6. function(err, result)
  7. {
  8. if (err) { console.error(err.message); return; }
  9. console.log(result.metaData); // show the metadata
  10. });

When using a ResultSet, metadata is also
available in result.resultSet.metaData.

The metadata is an array of objects, one per column. By default each
object has a name attribute:

  1. [ { name: 'DEPARTMENT_ID' }, { name: 'DEPARTMENT_NAME' } ]

The names are in uppercase. This is the default casing behavior for
Oracle client programs when a database table is created with unquoted,
case-insensitive column names.

Extended Metadata

More metadata is included when the Oracledb
extendedMetaData or execute() option
extendedMetaData is true. For
example:

  1. connection.execute(
  2. "SELECT department_id, department_name " +
  3. "FROM departments " +
  4. "WHERE manager_id < :id",
  5. [110], // bind value for :id
  6. { extendedMetaData: true },
  7. function(err, result)
  8. {
  9. if (err) { console.error(err.message); return; }
  10. console.log(result.metaData); // show the extended metadata
  11. });

The output is:

  1. [ { name: 'DEPARTMENT_ID',
  2. fetchType: 2002,
  3. dbType: 2,
  4. precision: 4,
  5. scale: 0,
  6. nullable: false },
  7. { name: 'DEPARTMENT_NAME',
  8. fetchType: 2001,
  9. dbType: 1,
  10. byteSize: 30,
  11. nullable: false } ]

Description of the properties is given in the
result.metaData description.

9.1.6 Result Type Mapping

Oracle character, number and date columns can be selected. Datatypes
that are currently unsupported give a "datatype is not supported"
error.

The default query result type mappings for Oracle Database types to JavaScript types are:

Fetching as String

The global fetchAsString property can be
used to force all number or date columns queried by an application to
be fetched as strings instead of in native format. Allowing data to
be fetched as strings helps avoid situations where using JavaScript
types can lead to numeric precision loss, or where date conversion is
unwanted.

For example, to force all dates and numbers used by queries in an
application to be fetched as strings:

  1. var oracledb = require('oracledb');
  2. oracledb.fetchAsString = [ oracledb.DATE, oracledb.NUMBER ];

Only number and date columns can be mapped to strings with fetchAsString.

The maximum length of a string created can be 200 bytes.

Individual queries can use the execute() option
fetchInfo to map individual number or date columns
to strings without affecting other columns or other queries. Any
global fetchAsString setting can be overridden to allow specific
columns to have data returned in native format:

  1. var oracledb = require('oracledb');
  2. oracledb.fetchAsString = [ oracledb.NUMBER ]; // any number queried will be returned as a string
  3. oracledb.getConnection(
  4. {
  5. user : "hr",
  6. password : "welcome",
  7. connectString : "localhost/XE"
  8. },
  9. function(err, connection)
  10. {
  11. if (err) { console.error(err.message); return; }
  12. connection.execute(
  13. "SELECT last_name, hire_date, salary, commission_pct FROM employees WHERE employee_id = :id",
  14. [178],
  15. {
  16. fetchInfo :
  17. {
  18. "HIRE_DATE": { type : oracledb.STRING }, // return the date as a string
  19. "COMMISSION_PCT": { type : oracledb.DEFAULT } // override oracledb.fetchAsString and fetch as native type
  20. }
  21. },
  22. function(err, result)
  23. {
  24. if (err) { console.error(err.message); return; }
  25. console.log(result.rows);
  26. });
  27. });

The output is:

  1. [ [ 'Grant', '24-MAY-07', '7000', 0.15 ] ]

The date and salary columns are returned as strings, but the
commission is a number. The date is mapped using the current session
date format, which was DD-MON-YY in this example. The default date
format can be set, for example, with the environment variable
NLS_DATE_FORMAT. Note this variable will only be read if NLS_LANG
is also set.

Without the mapping capabilities provided by fetchAsString and
fetchInfo the hire date would have been a JavaScript date in the
local time zone, and both numeric columns would have been
represented as numbers:

  1. [ [ 'Grant', Thu May 24 2007 00:00:00 GMT+1000 (AEST), 7000, 0.15 ] ]

To map columns returned from REF CURSORS, use fetchAsString. The
fetchInfo settings do not apply.

Mapping Custom Types

Datatypes such as an Oracle Locator SDO_GEOMETRY, or your own custom
types, cannot be fetched directly in node-oracledb. Instead, utilize
techniques such as using an intermediary PL/SQL procedure to map the
type components to scalar values, or use a pipelined table.

For example, consider a CUSTOMERS table having a CUST_GEO_LOCATION
column of type SDO_GEOMETRY, as created in this example
schema
:

  1. CREATE TABLE customers (
  2. customer_id NUMBER,
  3. last_name VARCHAR2(30),
  4. cust_geo_location SDO_GEOMETRY);
  5. INSERT INTO customers VALUES
  6. (1001, 'Nichols', SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE (-71.48923,42.72347,NULL), NULL, NULL));
  7. COMMIT;

Instead of attempting to get CUST_GEO_LOCATION by directly calling a
PL/SQL procedure that returns an SDO_GEOMETRY parameter, you could
instead get the scalar coordinates by using an intermediary PL/SQL
block that decomposes the geometry:

  1. . . .
  2. var sql =
  3. "BEGIN " +
  4. " SELECT t.x, t.y" +
  5. " INTO :x, :y" +
  6. " FROM customers, TABLE(sdo_util.getvertices(customers.cust_geo_location)) t" +
  7. " WHERE customer_id = :id;" +
  8. "END; ";
  9. var bindvars = {
  10. id: 1001,
  11. x: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT },
  12. y: { type: oracledb.NUMBER, dir : oracledb.BIND_OUT }
  13. }
  14. connection.execute(
  15. sql,
  16. bindvars,
  17. function (err, result) {
  18. if (err) { console.error(err.message); return; }
  19. console.log(result.outBinds);
  20. });

The output is:

  1. { x: -71.48922999999999, y: 42.72347 }

Note the JavaScript precision difference. In this particular example,
you may want to bind using type: oracledb.STRING. Output would be:

  1. { x: '-71.48923', y: '42.72347' }

9.1.7 Row Prefetching

Prefetching is a query tuning feature allowing resource usage to be
optimized. It allows multiple rows to be returned in each network
trip from Oracle Database to node-oracledb when a
ResultSet is used for query or REF CURSOR data.
The prefetch size does not affect when, or how many, rows are returned
by node-oracledb to the application. The buffering of rows is handled
by Oracle's underlying client libraries.

By default prefetchRows is 100 for
ResultSet fetches. The application can choose a
different default prefetch size or change it for each query, as
determined by user benchmarking.

The default prefetch size was heuristically chosen to give decent
performance for developers who do not read documentation. Skilled
developers should benchmark their applications and adjust the prefetch
value of each query for optimum performance, memory use, and network
utilization.

For queries returning small sets of rows, reduce the default prefetch
to avoid unnecessary memory allocation and initialization. For
queries that return only a single row the minimum recommended prefetch
value is 2. This value lets node-oracledb fetch one row and check for
end-of-fetch at the same time.

The value of prefetchRows size is ignored when not using a
ResultSet.

Prefetching from REF CURSORS requires Oracle Database 11gR2 or
greater.

Prefetching can be disabled by setting prefetchRows to 0.

The prefetch size can be changed for the whole application:

  1. var oracledb = require('oracledb');
  2. oracledb.prefetchRows = 2;

Alternatively the prefetch size can be changed for individual queries
in the execute() options parameter:

  1. connection.execute(
  2. "SELECT last_name FROM employees",
  3. [],
  4. {resultSet: true, prefetchRows: 2},
  5. function(err, result)
  6. {
  7. . . .
  8. });
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注