結果の利用¶
インラインで結果を返す¶
結果を利用する最も一般的な方法は、 connection.execute()
に complete
コールバックを渡すことです。ステートメントの実行が終了し、結果を使用できる状態になると、 complete
コールバックが呼び出され、結果行がインラインで返されます。
connection.execute({ sqlText: 'SELECT * FROM sometable', complete: function(err, stmt, rows) { if (err) { console.error('Failed to execute statement due to the following error: ' + err.message); } else { console.log('Number of rows produced: ' + rows.length); } } });
結果のストリーミング¶
また、 statement.streamRows()
メソッドを呼び出す際に、 connection.execute
で streamResult
接続パラメーターを true
に設定すると、結果を行のストリームとして消費することもできます。このパラメーターを有効にすると、メソッドはNode.js Readable
ストリームを返します。 Readable
ストリームの詳細については、 Node.jsのドキュメント をご参照ください。
重要
Nodeのデフォルトメモリを超える可能性のある結果セットについては、結果をストリーミングする際に streamResult
を true
に設定することをSnowflakeは強くお勧めします。デフォルト値(false
)では、コネクタは結果をストリーミングする前に、すべての行を配列に格納します。結果セットが小さい場合、この要素は通常問題になりません。しかし、結果セットが大きい場合に、すべての結果をメモリに格納すると OOM エラーが発生する可能性があります。
最近のバージョンのSnowflake Node.jsドライバー(1.6.23以降)には、結果を消費するときに、ストリームからデータを読み取るよりも速くデータがストリームにプッシュされないようにするために、バックプレッシャー機能が実装されています。
たとえば、次のコードは Readable
イベントを使用して結果を消費します。
var connection = snowflake.createConnection({ account: process.env.SFACCOUNT, username: process.env.SFUSER, // ... streamResult: true }); // [..rest of the code..] connection.execute({ sqlText: "select L_COMMENT from SNOWFLAKE_SAMPLE_DATA.TPCH_SF100.LINEITEM limit 100000;", streamResult: true, complete: function (err, stmt) { var stream = stmt.streamRows(); // Read data from the stream when it is available stream.on('readable', function (row) { while ((row = this.read()) !== null) { console.log(row); } }).on('end', function () { console.log('done'); }).on('error', function (err) { console.log(err); }); } });
結果のバッチ処理¶
デフォルトでは、 statement.streamRows()
メソッドは結果のすべての行を含むストリームを生成します。ただし、結果のサブセットのみを使用する場合、または結果行をバッチで使用する場合は、 start
および end
引数で streamRows()
を呼び出すことができます。これらの追加オプションを指定すると、リクエストされた範囲内の行のみがストリーミングされます。
connection.execute({ sqlText: 'SELECT * FROM sometable', streamResult: true, // prevent rows from being returned inline in the complete callback complete: function(err, stmt, rows) { // no rows returned inline because streamResult was set to true console.log('rows: ' + rows); // 'rows: undefined' // only consume at most the last 5 rows in the result rows = []; stmt.streamRows({ start: Math.max(0, stmt.getNumRows() - 5), end: stmt.getNumRows() - 1, }) .on('error', function(err) { console.error('Unable to consume requested rows'); }) .on('data', function(row) { rows.push(row); }) .on('end', function() { console.log('Number of rows consumed: ' + rows.length); }); } })
データ型のキャスト¶
結果の行が生成されると、ドライバーは対応する JavaScriptSQL 相当物にデータ型を自動的にマッピングします。たとえば、タイプ TIMESTAMP および DATE の値は JavaScript 日付オブジェクトとして返されます。
JavaScript から SQL データ型への完全なマッピングについては、以下のテーブルをご参照ください。
SQL データ型
JavaScript データ型
メモ
VARCHAR, CHAR, CHARACTER, STRING, TEXT
String
INT, INTEGER, BIGINT, SMALLINT
Number
これがデフォルトのマッピングです。セッションパラメーター JS_TREAT_INTEGER_AS_BIGINT を使用して、 JavaScript Bigintにマッピングします。
scale
= 0で、NUMBER(精度、スケール)、 DECIMAL(p、s)、 NUMERIC(p、s)Number
これがデフォルトのマッピングです。セッションパラメーター JS_TREAT_INTEGER_AS_BIGINT を使用して、 JavaScript Bigintにマッピングします。
scale
> 0で、NUMBER(精度、スケール)、 DECIMAL(p、s)、 NUMERIC(p、s)Number
FLOAT, FLOAT4, FLOAT8, DOUBLE, DOUBLE PRECISION, REAL
Number
TIMESTAMP, TIMESTAMP_LTZ, TIMESTAMP_NTZ, TIMESTAMP_TZ
Date
TIMESTAMP_NTZ 値は UTC に返されます。
DATE
Date
TIME
String
SQL の TIME データ型には JavaScript に相当するものがないため、 JavaScript 文字列にマッピングされます。
BOOLEAN
Boolean
VARIANT, ARRAY, OBJECT
JSON
Bigintとしての整数データ型のフェッチ¶
デフォルトでは、Snowflake INTEGER 列( BIGINT
、 NUMBER(p, 0)
などを含む)は JavaScript の Number
データ型に変換されます。ただし、有効なSnowflake整数の最大値は、有効な JavaScript Numberの最大値よりも大きくなります。Snowflakeの INTEGER
列を、 JavaScript Number
より大きな値を保存できる JavaScript Bigint
に変換するには、セッションパラメーター JS_TREAT_INTEGER_AS_BIGINT
を設定します。
これらのパラメーター値を設定するには、以下の方法を使用できます。
以下に示すように、ALTER SESSION ステートメントを使用します。
connection.execute( { sqlText: 'ALTER SESSION SET JS_TREAT_INTEGER_AS_BIGINT = TRUE', complete: function ... } );
接続構成情報でパラメーターを指定します。
var connection = snowflake.createConnection( { username: 'fakeusername', password: 'fakepassword', account: 'fakeaccountidentifier', jsTreatIntegerAsBigInt: true } );
文字列としてのデータ型のフェッチ¶
connection.execute()
を呼び出す際に、 fetchAsString
オプションを使用すると、 Boolean
Number
、 Date
、 Buffer
、および JSON
のデータ型を文字列として返すことができます。
このオプションは、たとえば、以下を返すために使用できます。
タイプ DATE および TIMESTAMP (またはそのバリアント)の値のフォーマットされたバージョン。
精度を損なうことなく JavaScript 数値に変換できない、数値表記の SQL 型の文字列バージョン。
次の例では、 fetchAsString
を使用して、高精度な Number
値を文字列に変換します。
connection.execute({
sqlText: 'SELECT 1.123456789123456789123456789 as "c1"',
fetchAsString: ['Number'],
complete: function(err, stmt, rows) {
if (err) {
console.error('Failed to execute statement due to the following error: ' + err.message);
} else {
console.log('c1: ' + rows[0].c1); // c1: 1.123456789123456789123456789
}
}
});
XML データの解析¶
ドライバーのバージョン1.7.0以降では、次の fast-xml-parser ライブラリ構成オプションを使用して、 XML コンテンツを持つ列をクエリするときにドライバーが XML ドキュメント属性を処理する方法をカスタマイズすることができます。これらのサポートされているオプションと、それらが XML データの解析にどのように影響するかについての詳細については、 xmlParserConfig オプション をご参照ください。
デフォルトでは、Node.jsドライバーは、クエリから XML データを返すときに XML 要素属性を無視します。たとえば、以下の XML コンテンツでは、 <piece>
要素に id
属性が含まれています。
<exhibit name="Art Show">
<piece id="000001">
<name>Mona Lisa</name>
<artist>Leonardo da Vinci</artist>
<year>1503</year>
</piece>
<piece id="000002">
<name>The Starry Night</name>
<artist>Vincent van Gogh</artist>
<year>1889</year>
</piece>
</exhibit>
デフォルトでは、Node.jsドライバーは結果セットを返すときに id
属性を無視し、以下の出力を返します。属性名と値が含まれていないことに注意してください。
{
exhibit: {
piece: [
{
"name": "Mona Lisa",
"artist": "Leonardo da Vinci",
"year": "1503",
},
{
"name": "The Starry Night",
"artist": "Vincent van Gogh",
"year": "1889",
}
]
}
}
fast-xml-parser オプションを設定するには、次の例のように xmlParserConfig
要素を作成します。
const snowflake = require('snowflake-sdk'); snowflake.configure({ xmlParserConfig: { /* Parameters that you can override * ignoreAttributes - default true, * attributeNamePrefix - default '@_', * attributesGroupName - default unset, * alwaysCreateTextNode - default false */ ignoreAttributes: false, attributesGroupName: '@', attributeNamePrefix: '' } });
これらの設定により、ドライバーは XML データを解析し、以下を生成します。
{
exhibit: {
piece: [
{
"name": "Mona Lisa",
"artist": "Leonardo da Vinci",
"year": "1503",
'@': { id: '000001' }
},
{
"name": "The Starry Night",
"artist": "Vincent van Gogh",
"year": "1889",
'@': { id: '000002' }
}
],
'@': { name: 'Art Show' }
}
重複する列名を含む結果セットを返す¶
バージョン1.8.0では、Snowflake Node.jsドライバーに新しい rowMode 構成オプションが導入され、重複した列名を含む結果セットを返す方法を指定できるようになりました。
バージョン1.8.0よりも前のSnowflake Node.jsドライバーでは、 SELECT コマンドの結果セットを常に JavaScript オブジェクトとして返していました。結果セットに重複した列名と値が含まれている場合は、 JavaScript オブジェクトが重複した名前を処理する方法によって、いくつかの要素が省略される可能性があります。
rowMode
オプションは、以下のような潜在的な情報の損失を避けるために、結果セットのデータを返す方法を指定することができます。
array
object
(デフォルト)object_with_renamed_duplicated_columns
説明のために、次のようなクエリを送信するとします。
select *
from (select 'a' as key, 1 as foo, 3 as name) as table1
join (select 'a' as key, 2 as foo, 3 as name2) as table2 on table1.key = table2.key
join (select 'a' as key, 3 as foo) as table3 on table1.key = table3.key
rowMode
の値に基づいて、ドライバーは以下のような結果セットを返します。
object
(または設定解除){KEY: 'a', FOO: 3, NAME: 3, NAME2: 3};
array
['a', 1, 3, 'a', 2, 3, 'a', 3];
object_with_renamed_duplicated_columns
{KEY: 'a', FOO: 1, NAME: 3, KEY_2: 'a', FOO_2: 2, NAME2: 3, KEY_3: 'a', FOO_3: 3};
以下に示すように、 rowMode
パラメーターは、接続またはステートメント構成レベルで設定できます。両方で設定されている場合は、ステートメントレベルの値が優先されます。
構成レベル
snowflake.createConnection({ account: account, username: username, ... rowMode: 'array'})
ステートメントレベル
connection.execute({ sqlText: sql, rowMode: 'array', ... )}
結果セットが JSON と XML データを処理する方法のカスタマイズ¶
Snowflake Node.jsドライバーは、結果セットの JSON と XML データを処理するために、以下のデフォルトのパーサーを提供します。
JSON: 新しい
Function
オブジェクトの結果を返します。XML: fast-xml-parser です。
デフォルトの
fast-xml-parser
モジュールは、 XML データの解析 で説明したように、機能のサブセットをサポートしています。
カスタムパーサーを使用する場合は、以下の例を使用して構成できます。
ドライバーがバージョン1.6.21より前に使用していたeval JSON パーサーを使用します。
const snowflake = require('snowflake-sdk'); snowflake.configure({ jsonColumnVariantParser: rawColumnValue => JSON.parse(rawColumnValue) })
fast-xml-parser
パーサーを使用すると、 すべてのオプションをカスタマイズする: ことができますconst snowflake = require('snowflake-sdk'); snowflake.configure({ xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue) })
同じ宣言内で、両方のカスタムパーサーを構成します。
const snowflake = require('snowflake-sdk'); snowflake.configure({ jsonColumnVariantParser: rawColumnValue => JSON.parse(rawColumnValue), xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue) })