Developer ToolsFree generatorPDF, Excel, Word exportNetSuiteAll NetSuite tools

NetSuite SuiteQL in SuiteScript: runSuiteQL and runSuiteQLPaged Generator

This generator wraps a SuiteQL statement in working SuiteScript 2.1 N/query code: a single runSuiteQL call, a runSuiteQLPaged loop over every page, or the promise form for client scripts. Parameters are bound with ? placeholders and the paged version checks remaining governance before each page.

Fill in

Use single quotes inside the SQL. Keep a unique ORDER BY for paged runs.

One value per ? in order. Strings, numbers or booleans only.

Generated code
Download:

Option A: runSuiteQL (one call, small result sets)

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/query', 'N/runtime', 'N/log'], (query, runtime, log) => {
  const SQL = "SELECT t.id, t.tranid, BUILTIN.DF(t.entity) AS customer, t.foreigntotal FROM transaction t WHERE t.type = ? AND t.trandate >= TO_DATE(?, 'YYYY-MM-DD') ORDER BY t.id";
  const PARAMS = ['CustInvc', '2026-01-01'];

  const execute = (context) => {
    const resultSet = query.runSuiteQL({
      query: SQL,
      params: PARAMS,
      customScriptId: 'netray_open_invoices_2026'
    });
    const rows = resultSet.asMappedResults();
    log.audit({ title: 'runSuiteQL rows', details: rows.length });
    rows.forEach((row) => {
      // keys are the column aliases, returned in lowercase
      log.debug({ title: 'row ' + row.id, details: JSON.stringify(row) });
    });
    log.audit({ title: 'remaining usage', details: runtime.getCurrentScript().getRemainingUsage() });
  };

  return { execute };
});

Option B: runSuiteQLPaged (all pages)

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/query', 'N/runtime', 'N/log'], (query, runtime, log) => {
  const SQL = "SELECT t.id, t.tranid, BUILTIN.DF(t.entity) AS customer, t.foreigntotal FROM transaction t WHERE t.type = ? AND t.trandate >= TO_DATE(?, 'YYYY-MM-DD') ORDER BY t.id";
  const PARAMS = ['CustInvc', '2026-01-01'];
  const MIN_UNITS = 200;

  const execute = (context) => {
    const script = runtime.getCurrentScript();
    const paged = query.runSuiteQLPaged({
      query: SQL,
      params: PARAMS,
      pageSize: 1000,
      customScriptId: 'netray_open_invoices_2026'
    });
    log.audit({ title: 'total rows / pages', details: paged.count + ' / ' + paged.pageRanges.length });

    let processed = 0;
    for (const range of paged.pageRanges) {
      if (script.getRemainingUsage() < MIN_UNITS) {
        log.audit({ title: 'stopping early', details: 'page index ' + range.index + ', rows done ' + processed });
        break;
      }
      const page = paged.fetch({ index: range.index });
      page.data.asMappedResults().forEach((row) => {
        // process row here, for example row.tranid, row.customer, row.foreigntotal
        processed += 1;
      });
    }
    log.audit({ title: 'rows processed', details: processed });
  };

  return { execute };
});

Option C: runSuiteQL.promise (SuiteScript 2.1 client script)

/**
 * @NApiVersion 2.1
 * @NScriptType ClientScript
 */
define(['N/query'], (query) => {
  const pageInit = (context) => {
    query.runSuiteQL.promise({
      query: "SELECT t.id, t.tranid, BUILTIN.DF(t.entity) AS customer, t.foreigntotal FROM transaction t WHERE t.type = ? AND t.trandate >= TO_DATE(?, 'YYYY-MM-DD') ORDER BY t.id",
      params: ['CustInvc', '2026-01-01']
    }).then((resultSet) => {
      const rows = resultSet.asMappedResults();
      console.log('SuiteQL rows', rows.length, rows[0]);
    }).catch((e) => {
      console.error('SuiteQL failed', e.name, e.message);
    });
  };

  return { pageInit };
});

Verify for your account: runSuiteQL and runSuiteQLPaged are 10 usage units per call in Oracle help; check the cost of PagedData.fetch on your release before tuning MIN_UNITS. Keep a unique ORDER BY when paging or rows can repeat or go missing. If the SQL contains double quotes, replace them with single-quoted literals before pasting.

Send us a slow SuiteQL query

Share the SQL and the script type it runs in and we will suggest paging, joins or a map/reduce split, with the expected usage per run.

Optional. The tool and every export work without this. No spam.

Was this tool useful?

How to use it

Paste a SuiteQL statement you have already tested in a query tool or the SuiteQL workbook, replace every literal that changes with ? and list the values in the parameters array in the same order. Pick Option A when you expect a few thousand rows or fewer, Option B for anything that may grow, and Option C only for a client script that must not block the form. Upload the file to the File Cabinet, create the script record and a deployment, then run it from the deployment with Save and Execute. The default query reads invoices from 1 January 2026 with the customer display name through BUILTIN.DF.

  • params accepts strings, numbers and booleans; pass dates as strings and convert them with TO_DATE in the SQL.
  • pageSize must be between 5 and 1000; the default when omitted is 50.
  • customScriptId is an optional label Oracle uses to trace slow queries; make it unique per query.

Row limits: what the help says and what developers see

SuiteScript developers widely report that a single ResultSet from runSuiteQL holds at most 5,000 rows, whatever the query would return. Treat 5,000 as the ceiling for Option A and use the paged loop, or keyset paging on t.id, for anything larger. The paged API has its own total result limit, so check the runSuiteQLPaged entry in the help for your release before relying on it for very large extracts. On governance, the runSuiteQL or runSuiteQLPaged call itself costs 10 units and each page fetch also consumes usage, so a run of 50 or more pages can spend hundreds of units on fetches alone in a 10,000-unit scheduled script.

  • Without a unique ORDER BY, paging can return duplicate or missing rows.
  • Read values by the aliases you wrote in the SELECT list; asMappedResults returns them as lowercase keys.

Gotchas that break SuiteQL in scripts

Never build the WHERE clause by string concatenation: bound parameters avoid quoting bugs with names such as O'Brien and stop injection when values come from a Suitelet or RESTlet. Dates passed as plain strings are interpreted in the running user's date format preference, so the template uses TO_DATE(?, 'YYYY-MM-DD') to make the parameter format explicit. asMappedResults keys come back lowercase, so an alias written as CustomerName is read as row.customername. Records or fields the executing role has no permission for make the query fail or return fewer rows, which is a common reason a query that works for an administrator fails in a scheduled deployment running as another role.

How to check the result in NetSuite

Open Customization, Scripting, Script Execution Logs (or the deployment's Execution Log subtab) and compare the rows and pages audit entry against the same SQL in your query tool. Spot-check two or three tranid values against the transaction screens. If the remaining usage figure drops faster than expected, the cost is in your per-row processing (record.load and save), not in the query, and the work belongs in a map/reduce script instead.

Frequently Asked Questions

How do I use SuiteQL in NetSuite scripts?

Load the N/query module and call query.runSuiteQL({ query, params }) for a ResultSet, or query.runSuiteQLPaged({ query, params, pageSize }) for PagedData. Read rows with asMappedResults() to get objects keyed by column alias, or with the results property to get arrays of values in column order. Both are available in SuiteScript 2.x server scripts; 2.1 is recommended.

Why does runSuiteQL stop at 5,000 rows?

Developers consistently see a single runSuiteQL ResultSet capped at 5,000 rows, with no error raised when more rows match. The reliable approach is runSuiteQLPaged with a unique ORDER BY, fetching each page range, or keyset paging where each call asks for id greater than the last id seen, which also keeps each call cheap.

How many governance units does runSuiteQL use?

Oracle help lists 10 usage units for query.runSuiteQL and 10 for query.runSuiteQLPaged. Fetching pages also consumes usage, so check the PagedData.fetch entry for your release. Usage limits depend on the script type: 10,000 units for a scheduled script, 5,000 for a RESTlet and 1,000 for a user event or Suitelet.

Should I use runSuiteQL or runSuiteQLPaged?

Use runSuiteQL when the result is small and bounded, such as a lookup or a summary with GROUP BY, because it is one call and returns a ResultSet directly. Use runSuiteQLPaged with a unique ORDER BY when the row count can grow past 5,000, and fetch each page range in a loop while checking remaining usage. For very large extracts, a map/reduce script with a SuiteQL input stage spreads the work.

If you want planners and finance users to ask these SuiteQL questions in plain English against NetSuite, with the generated query shown and role permissions respected, we can set that up on your own infrastructure.

ArchRay builder
Design the on-prem AI stack for NetSuite: model, GPUs, RAG pipeline, guardrails
See memory, speed and cost as you swap parts, then export the diagram. Free, in your browser.
Open the builder

Keep exploring

Free tools

ERP fixes and how-tos

AI on your ERP

Integrations

Software