14 minutes Read

Published On

N/query vs N/search in NetSuite: Unveiling the Dynamics of Data Retrieval

Key Takeaways

  • N/search is the default choice for most SuiteScript data retrieval: It works in every script type, returns results with minimal setup, integrates with saved searches, and has predictable governance costs. Use it unless you have a specific reason not to.
  • N/query (SuiteQL) is the right tool for cross-record joins, aggregations, and GROUP BY logic: Operations that require SQL-style multi-join queries or result sets grouped by a field cannot be expressed cleanly in N/search.
  • N/query has execution context restrictions: It cannot be used in Client Scripts. N/search works in all script types including Client, User Event, Scheduled, Map/Reduce, and Restlet.
  • Governance costs differ: Both use governance units, but N/query’s query.runPaged() and N/search’s search.runPaged() handle large datasets differently. For high-volume reads, Map/Reduce with N/search is the more common pattern.
  • Neither module is universally superior: The choice is determined by the structure of the data you need, the script context you are in, and whether the query exists as a saved search you want to reuse.

Introduction

Most SuiteScript developers start with N/search. It is the module that all the examples use; it integrates with saved searches you can build in the UI, and it works in every script context. Eventually, you hit a data requirement that N/search cannot express: you need to join three record types, or you need a GROUP BY on a field that is not available as a summary type in a saved search, or you need a WHERE clause with subquery logic.

That is where N/query and its underlying SuiteQL syntax become relevant. N/query lets you write SQL-style queries against NetSuite data, including multi-table joins, aggregate functions, HAVING clauses, and result ordering that N/search cannot produce.

This guide covers how both modules work at the code level, where each one is the right tool, where each one has hard limitations, and how to make the decision between them for a specific data retrieval problem.

N/search: How It Works

N/search is the older and more widely used of the two modules. A search is defined by three components: a record type, an array of filter conditions (criteria), and an array of result columns. The search is executed and returns a result set that you iterate.

Basic N/search Structure

require(['N/search'], (search) => {

  const results = search.create({

    type: search.Type.SALES_ORDER,

    filters: [

      ['status', search.Operator.ANYOF, 'SalesOrd:B'],  // Pending Fulfillment

      'AND',

      ['amount', search.Operator.GREATERTHAN, '10000']

    ],

    columns: [

      search.createColumn({ name: 'tranid' }),

      search.createColumn({ name: 'entity', label: 'Customer' }),

      search.createColumn({ name: 'amount' }),

      search.createColumn({

        name: 'entityid',

        join: 'customer',   // Single-level join supported

        label: 'Customer ID'

      })

    ]

  }).run();

  results.each((result) => {

    log.debug('SO', result.getValue('tranid'));

    return true;  // Continue iteration

  });

});

Loading a Saved Search

N/search can execute a saved search built in the UI, which separates configuration from code:

const savedSearch = search.load({ id: ‘customsearch_open_orders’ });

savedSearch.run().each((result) => {

  // process each result

  return true;

});

This is one of N/search’s most practical features. Business users can modify the saved search criteria in the UI without a developer changing the script. For the full patterns around building and exporting saved searches to SuiteScript, see our guide on creating CSV files from saved search results in SuiteScript.

N/search Limitations

  • Single-level joins only: You can join from a Sales Order to its Customer, but you cannot chain joins (Customer > Address > Country) in one search
  • No GROUP BY: Summary types (COUNT, SUM, MIN, MAX) exist but are not equivalent to SQL GROUP BY with arbitrary grouping logic
  • No subqueries: You cannot nest a search result as a filter condition in another search natively
  • No HAVING clause: You cannot filter on aggregate results (e.g., only customers with more than 5 open orders)
  • Limited to NetSuite record types as defined in search.Type: Not all NetSuite data is exposed through N/search record types

N/query (SuiteQL): How It Works

N/query exposes SuiteQL, a SQL-like query language that runs against NetSuite’s internal data model. You write a query string with standard SQL constructs, SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, and execute it against the NetSuite database.

Basic N/query Structure

require(['N/query'], (query) => {

  const results = query.runSuiteQL({

    query: `

      SELECT

        t.tranId,

        c.entityId   AS customerId,

        c.companyName,

        t.amount

      FROM

        transaction t

      JOIN

        customer c ON c.id = t.entity

      WHERE

        t.type = 'SalesOrd'

        AND t.status = 'SalesOrd:B'

        AND t.amount > 10000

      ORDER BY

        t.amount DESC

    `

  });

  results.asMappedResults().forEach((row) => {

    log.debug('SO', JSON.stringify(row));

  });

});

Multi-Join Query

The scenario where N/query is clearly the right tool: retrieving data that requires chained joins that N/search cannot express.

const results = query.runSuiteQL({

  query: `

    SELECT

      t.tranId,

      c.companyName,

      addr.city,

      addr.country

    FROM

      transaction t

    JOIN customer c ON c.id = t.entity

    JOIN address addr ON addr.nkey = c.defaultbillingaddress

    WHERE t.type = 'SalesOrd'

  `

});

Aggregation With GROUP BY

Counting open orders per customer is not cleanly achievable in N/search:

const results = query.runSuiteQL({

  query: `

    SELECT

      c.companyName,

      COUNT(t.id) AS openOrderCount,

      SUM(t.amount) AS totalValue

    FROM transaction t

    JOIN customer c ON c.id = t.entity

    WHERE t.type = 'SalesOrd'

      AND t.status = 'SalesOrd:B'

    GROUP BY c.companyName

    HAVING COUNT(t.id) > 3

    ORDER BY totalValue DESC

  `

});

N/query Hard Limitations

  • Not available in Client Scripts: query.runSuiteQL() throws an error when called from a Client Script context. Use N/search in Client Scripts
  • Table and column names are internal: SuiteQL uses internal table names (transaction, customer, entity) not the display names. You need to know the correct SuiteQL schema names
  • Not all data is exposed: Some NetSuite data is only accessible through N/search record types and is not available as a SuiteQL table
  • No integration with saved searches: You cannot load a saved search built in the UI into N/query. The query must be written entirely in code
  • Pagination differs: For large result sets, use query.runSuiteQLPaged() rather than runSuiteQL() to avoid memory issues

Governance Considerations

Both N/search and N/query consume governance units. The key governance points:

N/search

  • Each search.run().each() iteration does not consume a unit per record, the governance cost is per execution block
  • For large result sets (thousands of records), use search.runPaged() with a page size of 1,000 to avoid memory errors and governance spikes
  • In Map/Reduce scripts, the reduce phase is well-suited for processing large N/search result sets because each key gets its own governance allocation

N/query

  • query.runSuiteQL() is appropriate for result sets that fit in memory
  • For large result sets, use query.runSuiteQLPaged() which mirrors search.runPaged() behavior
  • Complex JOIN queries with large intermediate result sets can hit governance limits faster than equivalent N/search queries on the same data

For the broader context of how to manage governance across script types and when to use Map/Reduce versus Scheduled Scripts for high-volume data operations see the in-depth SuiteScript automation and customization guide.

Decision Framework: Which Module to Use

Work through these questions in order:

  • Are you in a Client Script context? → Use N/search. N/query is not available.
  • Does your query need to load or extend an existing saved search? → Use N/search. N/query cannot interact with saved searches.
  • Does your query require chained joins (more than one level deep)? → Use N/query.
  • Does your query need GROUP BY with a HAVING filter on the aggregate? → Use N/query.
  • Does your query involve a simple filter on a single record type with a join to one related record? → Use N/search. It is simpler, more maintainable, and familiar to other NetSuite developers.
  • Does your data requirement involve a table not exposed in N/search record types? → Use N/query if that table exists in the SuiteQL schema.

Default rule: Start with N/search. Move to N/query only when you hit a specific structural limitation that N/search cannot resolve. This keeps your codebase consistent and your scripts accessible to developers who know NetSuite but are not fluent in SuiteQL schema names.

Finding the Right Field Names for Each Module

One practical challenge when working with either module is knowing the correct field identifiers. N/search uses field IDs as you see them on custom fields (e.g., custbody_region) and standard field names as defined in the N/search API (e.g., tranid, entity). N/query uses SuiteQL column names, which sometimes differ from both the UI label and the N/search field ID.

For N/search field IDs, the NetSuite Field Explorer Chrome extension identifies field IDs, types, and join paths directly on the record form.

For saved search development best practices, see our guide to best practices for creating saved searches in NetSuite.

Conclusion

N/search and N/query serve different query structures. N/search handles the majority of data retrieval needs in SuiteScript: filtering records, joining to one related record, retrieving specific fields, loading saved searches, and iterating results. It works in every script context and integrates with the saved search UI.

N/query handles what N/search cannot: chained multi-table joins, GROUP BY with HAVING filters, aggregations across joined record sets, and SQL-style subquery logic. It requires SuiteQL schema knowledge and cannot be used in Client Scripts.

For most scripts, N/search is the right starting point. When you hit a structural limitation, a join chain N/search cannot express, or an aggregation that summary types do not support, switch to N/query for that specific query. There is no rule that prevents using both in the same script for different queries.

Meet the Author

Sahal Tariq

Lead Software Engineer

Sahal Tariq is a skilled Lead Software Engineer with expertise in JavaScript and Netsuite integrations. He has a proven track record of developing high-quality solutions and is known for his keen eye for detail and problem-solving abilities. During his free time, Sahal enjoys engaging in a variety of activities, including working out, gaming, and exploring a wide range of music genres.

Table of Contents

Contact Us

By submitting this form, you agree to our privacy policy and terms of service.

Related resources you might be interested in

We'd love to help you with all your NetSuite needs

Folio3 Your Top Choice:

Middle East Partner 2025
education award 2025
Winner Award
Software and IT Services 2024
Financial-Services-2023
SuiteCommerce 2023

Let's discuss your NetSuite needs

Hello, How can we help you?

Get a 45-Minute
NetSuite Consulting Session

Worth $2,000 for Free

Grab the opportunity to speak with one of our top-rated consultants to get expert guidance on your NetSuite needs.