結果の利用

インラインで結果を返す

結果を利用する最も一般的な方法は、 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);
    }
  }
});
Copy

結果のストリーミング

また、 statement.streamRows() メソッドを呼び出す際に、 connection.executestreamResult 接続パラメーターを true に設定すると、結果を行のストリームとして消費することもできます。このパラメーターを有効にすると、メソッドはNode.js Readable ストリームを返します。 Readable ストリームの詳細については、 Node.jsのドキュメント をご参照ください。

重要

Nodeのデフォルトメモリを超える可能性のある結果セットについては、結果をストリーミングする際に streamResulttrue に設定することを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);
    });
  }
});
Copy

結果のバッチ処理

デフォルトでは、 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);
    });
  }
})
Copy

データ型のキャスト

結果の行が生成されると、ドライバーは対応する 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 列( BIGINTNUMBER(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 ...
                        }
                      );
    
    Copy
  • 接続構成情報でパラメーターを指定します。

    var connection = snowflake.createConnection(
          {
          username: 'fakeusername',
          password: 'fakepassword',
          account: 'fakeaccountidentifier',
          jsTreatIntegerAsBigInt: true
          }
        );
    
    Copy

文字列としてのデータ型のフェッチ

connection.execute() を呼び出す際に、 fetchAsString オプションを使用すると、 Boolean NumberDateBuffer、および 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
    }
  }
});
Copy

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>
Copy

デフォルトでは、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: ''
  }
});
Copy

これらの設定により、ドライバーは 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
Copy

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'})
    
    Copy
  • ステートメントレベル

    connection.execute({
      sqlText: sql,
      rowMode: 'array',
      ...
      )}
    
    Copy

結果セットが 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 => eval("(" + rawColumnValue + ")")
    })
    
    Copy
  • fast-xml-parser パーサーを使用すると、 すべてのオプションをカスタマイズする: ことができます

    const snowflake = require('snowflake-sdk');
    snowflake.configure({
      xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue)
    })
    
    Copy
  • 同じ宣言内で、両方のカスタムパーサーを構成します。

    const snowflake = require('snowflake-sdk');
    snowflake.configure({
      jsonColumnVariantParser: rawColumnValue => JSON.parse(rawColumnValue),
      xmlColumnVariantParser: rawColumnValue => new (require("fast-xml-parser")).XMLParser().parse(rawColumnValue)
    })
    
    Copy