Ausführen von Anweisungen¶
Anweisungen können durch Aufruf der Methode connection.execute()
ausgeführt werden. Die execute()
-Methode akzeptiert ein options
-Objekt, mit dem der SQL-Text und ein complete
-Callback angegeben werden können. Der complete
-Callback wird aufgerufen, wenn eine Anweisung die Ausführung beendet hat und das Ergebnis zur Weiterverarbeitung bereit steht:
var statement = connection.execute({ sqlText: 'CREATE DATABASE testdb', complete: function(err, stmt, rows) { if (err) { console.error('Failed to execute statement due to the following error: ' + err.message); } else { console.log('Successfully executed statement: ' + stmt.getSqlText()); } } });
Bemerkung
Die maximale Größe der Nutzlast einer einzelnen Anfrage beträgt 16 MB.
Asynchrones Ausführen von Abfragen¶
Der Snowflake-Node.js-Treiber unterstützt asynchrone Abfragen (d. h. Abfragen, die dem Benutzer die Kontrolle zurückgeben, bevor die Abfrage abgeschlossen ist). Sie können eine Abfrage starten und dann durch Abrufen feststellen, wann die Abfrage abgeschlossen ist. Nach Abschluss der Abfrage können Sie das Resultset lesen.
Sie aktivieren asynchrone Abfragen, indem Sie asyncExec: true
in die Methode connection.execute
aufnehmen.
Das folgende Beispiel zeigt, wie Sie Abfragen asynchron mit einem Promise
ausführen können.
let queryId;
let statement;
// 1. Execute query with asyncExec set to true
await new Promise((resolve) =>
{
connection.execute({
sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')',
asyncExec: true,
complete: async function (err, stmt, rows)
{
queryId = stmt.getQueryId(); // Get the query ID
resolve();
}
});
});
// 2. Get results using the query ID
const statement = await connection.getResultsFromQueryId({ queryId: queryId });
await new Promise((resolve, reject) =>
{
var stream = statement.streamRows();
stream.on('error', function (err)
{
reject(err);
});
stream.on('data', function (row)
{
console.log(row);
});
stream.on('end', function ()
{
resolve();
});
});
Sie können auch Callbacks verwenden, um asynchrone Abfragen zu überwachen, wie im folgenden Beispiel gezeigt.
Ermöglichen Sie asynchrone Abfragen, indem Sie
asyncExec: true
in die Methodeconnection.execute
aufnehmen.// 1. Execute query with asyncExec set to true connection.execute({ sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')', asyncExec: true, complete: async function (err, stmt, rows) { let queryId = stmt.getQueryId(); // 2. Get results using the query ID connection.getResultsFromQueryId({ queryId: queryId, complete: async function (err, _stmt, rows) { console.log(rows); } }); } });
Überprüfen Sie den Status der Abfrage, die zur asynchronen Ausführung übermittelt wurde.
let queryId; // 1. Execute query with asyncExec set to true await new Promise((resolve, reject) => { statement = connection.execute({ sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')', asyncExec: true, complete: async function (err, stmt, rows) { queryId = statement.getQueryId(); resolve(); } }); }); // 2. Check query status until it's finished executing const seconds = 2; let status = await connection.getQueryStatus(queryId); while (connection.isStillRunning(status)) { console.log(`Query status is ${status}, timeout for ${seconds} seconds`); await new Promise((resolve) => { setTimeout(() => resolve(), 1000 * seconds); }); status = await connection.getQueryStatus(queryId); } console.log(`Query has finished executing, status is ${status}`);
Batch mit SQL-Anweisungen ausführen (Unterstützung von mehreren Anweisungen)¶
Ab Version 1.6.18 des Node.js-Konnektors können Sie einen Batch mit SQL-Anweisungen (durch Semikolons getrennt) senden, der mit einer einzigen Anforderung ausgeführt wird.
Bemerkung
Die Ausführung mehrerer Anweisungen in einer einzigen Abfrage setzt voraus, dass ein gültiges Warehouse in einer Sitzung verfügbar ist.
Standardmäßig gibt Snowflake bei Abfragen mit mehreren Anweisungen einen Fehler zurück, um Angriffe durch Einschleusen von SQL-Befehlen zu verhindern. Die Ausführung mehrerer Anweisungen in einer einzigen Abfrage erhöht das Risiko einer SQL-Einschleusung. Snowflake empfiehlt, es sparsam zu verwenden. Sie können das Risiko verringern, indem Sie mit dem Parameter
MULTI_STATEMENT_COUNT
die Anzahl der auszuführenden Anweisungen angeben. Dies erschwert das Einschleusen einer Anweisung durch Anhängen.
Weitere Informationen zu diesen Typen von Angriffen finden Sie unter SQL-Injection.
Sie können mehrere Anweisungen als Batch auf die gleiche Weise ausführen wie Abfragen mit einzelnen Anweisungen, mit dem Unterschied, dass die Abfragezeichenfolge mehrere durch Semikolon getrennte Anweisungen enthält. Beachten Sie, dass mehrere Anweisungen sequenziell und nicht parallel ausgeführt werden. Der Parameter MULTI_STATEMENT_COUNT
gibt die genaue Anzahl der Anweisungen an, die der Batch enthält.
Wenn Sie zum Beispiel MULTI_STATEMENT_COUNT=3
einstellen, muss eine Batch-Anweisung genau drei Anweisungen umfassen. Wenn Sie eine Batch-Anweisung mit einer anderen Anzahl von Anweisungen übermitteln, lehnt der Node.js-Treiber die Anfrage ab. Sie können MULTI_STATEMENT_COUNT=0
einstellen, damit Batch-Abfragen eine beliebige Anzahl von Anweisungen enthalten können. Beachten Sie jedoch, dass die Verwendung dieses Wertes den Schutz vor Angriffen durch Einschleusung von SQL-Befehlen verringert.
Sie können diesen Parameter auf Sitzungsebene mit dem folgenden Befehl einstellen oder den Wert bei jeder Abfrage separat festlegen.
ALTER SESSION SET multi_statement_count = <n>
Wenn Sie den Wert auf Sitzungsebene einstellen, müssen Sie ihn nicht bei jeder Ausführung einer Batch-Anweisung neu einstellen. Im folgenden Beispiel wird die Anzahl auf Sitzungsebene auf drei Anweisungen festgelegt, und danach werden drei SQL-Anweisungen ausgeführt:
var statement = connection.execute({ sqlText: "ALTER SESSION SET multi_statement_count=0", complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { testMulti(); } } }); function testMulti() { console.log('select bind execute.'); var selectStatement = connection.execute({ sqlText: "create or replace table test(n int); insert into test values(1), (2); select * from test order by n", complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { console.log('==== complete'); console.log('==== sqlText=' + stmt.getSqlText()); if(stmt.hasNext()) { stmt.NextResult(); } else { // do something else, e.g. close the connection } } } }); }
Sie können auch jedes Mal, wenn Sie eine Abfrage mit mehreren Anweisungen ausführen, die Anzahl der Anweisungen im Batch festlegen, indem Sie MULTI_STATEMENT_COUNT
als Parameter für die Funktion connection.execute
festlegen. Im folgenden Beispiel wird die Anzahl der Anweisungen im Batch auf drei festgelegt, und es werden drei SQL-Anweisungen in die Batch-Abfrage aufgenommen:
var selectStatement = connection.execute({ sqlText: "CREATE OR REPLACE TABLE test(n int); INSERT INTO test values(1), (2); SELECT * FROM test ORDER BY n", parameters: { MULTI_STATEMENT_COUNT: 3 }, complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { console.log('==== complete'); console.log('==== sqlText=' + stmt.getSqlText()); if(stmt.hasNext()) { stmt.NextResult(); } else { // do something else, e.g. close the connection } } } });
Binden von Anweisungsparametern¶
Gelegentlich kann es sinnvoll sein, Daten in einer Anweisung mit einem Platzhalter zu binden. Das Ausführen von Anweisungen auf diese Weise ist nützlich, um Angriffe durch Einschleusung von SQL-Befehlen zu verhindern. Verwenden Sie ggf. die folgende Anweisung:
connection.execute({ sqlText: 'SELECT c1 FROM (SELECT 1 AS c1 UNION ALL SELECT 2 AS c1) WHERE c1 = 1;' });
Das gleiche Ergebnis erzielen Sie mit den folgenden Bindungen:
connection.execute({ sqlText: 'SELECT c1 FROM (SELECT :1 AS c1 UNION ALL SELECT :2 AS c1) WHERE c1 = :1;', binds: [1, 2] });
Die ?
-Syntax für Bindungen wird ebenfalls unterstützt:
connection.execute({ p sqlText: 'SELECT c1 FROM (SELECT ? AS c1 UNION ALL SELECT ? AS c1) WHERE c1 = ?;', binds: [1, 2, 1] });
Bemerkung
Es gibt eine Obergrenze für die Datengröße, die Sie binden oder in einem Batch kombinieren können. Weitere Details dazu finden Sie unter Begrenzung der Abfragetextgröße.
Binden eines Arrays für Masseneinfügen¶
Das Binden eines Arrays von Daten wird für INSERT-Massenoperationen unterstützt. Übergeben Sie ein Array von Arrays wie folgt:
connection.execute({ sqlText: 'INSERT INTO t(c1, c2, c3) values(?, ?, ?)', binds: [[1, 'string1', 2.0], [2, 'string2', 4.0], [3, 'string3', 6.0]] });
Bemerkung
Das Binden eines großen Arrays wirkt sich auf die Leistung aus und kann abgelehnt werden, wenn die Datenmenge zu groß ist, um vom Server verarbeitet zu werden.
Abbrechen von Anweisungen¶
Eine Anweisung kann durch Aufruf der Methode statement.cancel()
abgebrochen werden:
statement.cancel(function(err, stmt) { if (err) { console.error('Unable to abort statement due to the following error: ' + err.message); } else { console.log('Successfully aborted statement'); } });
Erneutes Übermitteln von Anforderungen¶
Wenn Sie sich nicht sicher sind, ob Snowflake eine SQL-Anweisung erfolgreich ausgeführt hat, z. B. aufgrund eines Netzwerkfehlers oder eines Timeouts, können Sie dieselbe Anweisung unter Verwendung der Anforderungs-ID erneut übermitteln. Angenommen, Sie senden einen INSERT-Befehl zum Hinzufügen von Daten, erhalten aber nicht rechtzeitig eine Bestätigung, sodass Sie nicht wissen, was mit dem Befehl passiert ist. In diesem Szenario möchten Sie nicht einfach denselben Befehl als neuen Befehl ausführen, da dies dazu führen könnte, dass der Befehl zweimal ausgeführt wird, was zu einer Verdoppelung der Daten führt.
Indem Sie die Anforderungs-ID in die SQL-Anweisung einbeziehen, können Sie eine mögliche Duplizierung der Daten vermeiden. Das erneute Übermitteln der Anforderung unter Verwendung der Anforderung-ID der ursprünglichen Anforderung stellt sicher, dass der erneut übermittelte Befehl nur ausgeführt wird, wenn die ursprüngliche Anforderung fehlgeschlagen ist. Weitere Informationen dazu finden Sie unter Erneutes Übermitteln von Anforderungen zum Ausführen von SQL-Anweisungen.
Bemerkung
Um eine Abfrage unter Verwendung einer Anfrage-ID erneut zu übermitteln, müssen Sie dieselbe Verbindung verwenden, die die Anfrage-ID generiert hat. Wenn Sie das Ergebnis einer Abfrage von einer anderen Verbindung abrufen möchten, finden Sie entsprechende Informationen unter RESULT_SCAN.
Die folgenden Codebeispiele zeigen, wie Sie eine Anforderungs-ID speichern und verwenden können, um eine Anweisung erneut zu übermitteln. Wenn Sie eine Anweisung ausführen, können Sie mit der Funktion getRequestId()
die ID der übermittelten Anforderung abrufen. Sie können diese ID dann verwenden, um die gleiche Anweisung zu einem späteren Zeitpunkt auszuführen. Im folgenden Beispiel wird eine INSERT-Anweisung ausgeführt und deren Anforderungs-ID in der Variablen requestId
gespeichert.
var requestId; connection.execute({ sqlText: 'INSERT INTO testTable VALUES (1);', complete: function (err, stmt, rows) { var stream = stmt.streamRows(); requestId = stmt.getRequestId(); // Retrieves the request ID stream.on('data', function (row) { console.log(row); }); stream.on('end', function (row) { console.log('done'); }); } });
Wenn Sie keine Bestätigung erhalten, dass der Befehl erfolgreich ausgeführt wurde, können Sie die Anforderung unter Verwendung der gespeicherten Anfrage-ID wie unten gezeigt erneut übermitteln.
connection.execute({ sqlText: 'INSERT INTO testTable VALUES (1);', // optional requestId: requestId, // Uses the request ID from before complete: function (err, stmt, rows) { var stream = stmt.streamRows(); stream.on('data', function (row) { console.log(row); }); stream.on('end', function (row) { console.log('done'); }); } });
Wenn Sie sich entscheiden, eine Anforderung erneut mit requestId
und sqlText
zu übermitteln, müssen Sie folgende Wechselwirkungen beachten:
Wenn die Anforderungs-ID
requestId
bereits vorhanden ist, d. h. mit einer früheren Anforderung übereinstimmt, ignoriert der Befehl diesqlText
-Abfrage und übermittelt erneut die Abfrage aus dem ursprünglichen Befehl.Wenn die Anforderungs-ID
requestId
nicht vorhanden ist, d. h. mit keiner früheren Anforderung übereinstimmt, führt der Befehl diesqlText
-Abfrage aus.