Skip to content
SuiteQL

SuiteQL vs saved search: which one should you use?

SuiteQL vs saved search in NetSuite: what each does better, a side-by-side table, where saved searches still win, and how to port one to SQL.

ERPray teamUpdated 7 min read
Short answer

A saved search is NetSuite's UI-driven report builder; SuiteQL is a SQL query over the same account data. Use a saved search when a non-technical user needs an interactive, shareable, emailable list. Use SuiteQL when you need multi-hop joins, subqueries, real aggregation, or a query you can version-control. Both run under the calling role's permissions.

Key takeaways

  • Saved searches win on distribution: role-based sharing, dashboard portlets, scheduled email and drilldown links to records. SuiteQL has none of that out of the box.
  • SuiteQL wins on expressiveness: subqueries, CTEs, set operations, self-joins and aggregation the saved-search UI cannot express in one object.
  • The two use different schemas. A saved search field ID is not guaranteed to be the SuiteQL column name, so porting is a translation, not a rename.
  • Neither bypasses permissions — both run as the calling role, so the same query can return different row counts for different users.
  • Most accounts should keep both: saved searches for people, SuiteQL for systems and for the questions the UI refuses to answer.

The SuiteQL vs saved search argument usually starts when someone asks for a number the saved-search UI cannot produce — margin by item with cost from a different transaction, or the second join hop that the join dropdown simply does not offer. At that point SuiteQL stops being a developer curiosity and becomes the only route to the answer. That does not make saved searches obsolete. They still do several things SuiteQL cannot do at all.

SuiteQL
NetSuite's SQL-92-flavoured query language over the account's analytics tables and views. It runs through the SuiteTalk REST query/v1/suiteql endpoint, through the N/query module in SuiteScript, and through Suitelet-based query tools. It is read-only: you select, you do not update.

Two clarifications before the comparison, because the terminology causes real confusion. NetSuite's native point-and-click analytics tool is SuiteAnalytics Workbook, which builds datasets — it is not a place you type SQL. SuiteAnalytics Connect is the ODBC/JDBC driver, and it exposes a different schema with different table names from SuiteQL. So "can I write SQL against NetSuite?" has three answers, and they are not interchangeable.

SuiteQL vs saved search: the short version

DimensionSaved searchSuiteQL
Where you build itNetSuite UI, no codeText editor, REST client, SuiteScript
Who can build itAny user with the permissionSomeone who writes SQL
JoinsPredefined join paths, usually one hop from the base recordAny join you can express, including self-joins and multi-hop
AggregationSummary types (sum/count/avg/min/max) per columnFull GROUP BY, HAVING, subqueries, WITH clauses
Two aggregates at different grainsAwkward or impossible in one searchStraightforward with a derived table
FormulasFormula (Numeric/Text/Date) fields, Oracle syntaxNative SQL expressions anywhere in the statement
Row outputPaginated UI results, CSV/PDF/XLS exportJSON from REST, result objects in SuiteScript
Drilldown to the recordYes, every row links to the recordNo — you get an internal id and build the link yourself
Scheduled email deliveryBuilt in, with attachmentYou write the script
Dashboard portlet / KPIYes, nativeOnly via a custom portlet script
Inline edit and mass updateYes, from the results pageNo, SuiteQL is read-only
Version control and code reviewSearch definitions live in the accountPlain text — diffable, reviewable, deployable
Reuse by other featuresWorkflows, N/search, groups, bulk emailScripts and integrations only
PermissionsRuns as the user, with an audience/sharing modelRuns as the calling role or integration user
Neither column is the winner. They fail in different places.

Where saved searches genuinely win

This is the part most SuiteQL advocacy skips. A saved search is not just a query — it is a query plus a delivery mechanism plus a permissions model plus a UI. Rewriting one in SQL throws away three of those four.

  • A controller can change it without you. Adding a column or moving a filter is a two-minute UI edit. The SuiteQL equivalent is a ticket.
  • Distribution is solved. Publish to a role's dashboard, share with a group, schedule a Monday 07:00 email with the CSV attached. All of that is configuration.
  • Drilldown. Every result row links to the record. Collections staff working an aging report need that link far more than they need a faster query.
  • Available filters. You can expose a date range and a subsidiary selector to the user and let them re-run it themselves.
  • It plugs into the rest of NetSuite. Workflow conditions, saved-search-driven groups, sublist views, reminders, KPI scorecards. SuiteQL plugs into nothing by default.
  • Inline editing and mass update. Fixing 300 bad terms records from a results page is a saved-search feature with no SuiteQL analogue.

Output format is part of this. A saved search exports to CSV, PDF and XLS with one click, which is what most consumers of the data actually want. SuiteQL gives you JSON, and somebody has to convert it — the JSON to CSV converter handles the one-off case, but if the destination is always a spreadsheet, you have chosen the harder route for no benefit.

Where SuiteQL wins

SuiteQL wins whenever the shape of the question is more complicated than "list records matching filters, with subtotals".

  • Joins the UI does not offer. The saved-search join dropdown exposes a curated set of paths. SuiteQL lets you join transactionaccountingline to transactionline to account to accountingperiod in one statement.
  • Two grains in one result. Revenue this year next to revenue last year, per customer, in one row — a derived table each side, joined. In saved searches this becomes two searches and a spreadsheet.
  • Set operations and anti-joins. "Customers with no invoice in the last 12 months" is a NOT EXISTS in SQL and a genuine nuisance in the UI.
  • Deterministic text. The query is a string. It goes in Git, gets reviewed in a pull request, and deploys with the SDF project. Nobody edits it by accident on a Friday.
  • Integrations. If an external system needs the number, N/query or the REST endpoint is a far cleaner contract than parsing a saved search export.
  • GL-accurate maths. Getting revenue from posting accounting lines rather than transaction totals is much easier to express as SQL, and it reconciles.

Here is a query that has no clean single-search equivalent — revenue per customer by fiscal year, pivoted, straight from posting accounting lines:

WITH rev AS (
    SELECT
        t.entity                                        AS customer_id,
        TO_CHAR(ap.startdate, 'YYYY')                   AS fy,
        SUM(NVL(tal.credit, 0) - NVL(tal.debit, 0))     AS revenue
    FROM transactionaccountingline tal
    JOIN transaction      t  ON t.id  = tal.transaction
    JOIN account          a  ON a.id  = tal.account
    JOIN accountingperiod ap ON ap.id = t.postingperiod
    WHERE tal.posting  = 'T'
      AND a.accttype   = 'Income'
      AND ap.startdate >= TO_DATE('2025-01-01', 'YYYY-MM-DD')
    GROUP BY t.entity, TO_CHAR(ap.startdate, 'YYYY')
)
SELECT
    BUILTIN.DF(rev.customer_id)                              AS customer,
    SUM(CASE WHEN fy = '2025' THEN revenue ELSE 0 END)       AS fy2025,
    SUM(CASE WHEN fy = '2026' THEN revenue ELSE 0 END)       AS fy2026
FROM rev
GROUP BY BUILTIN.DF(rev.customer_id)
ORDER BY 3 DESC
Two years of revenue per customer in one result set

Note the posting = 'T' filter. Without it you pick up non-posting lines and the total stops matching the income statement. If Multi-Book Accounting is enabled, add an accounting book filter too, or every figure doubles.

Free calculator
SuiteQL formatter

Paste a one-line query and get clause breaks, keyword casing and indented joins — useful when you inherit someone else's SuiteQL.

Porting a saved search to SuiteQL

The mapping is mechanical in outline. Criteria become WHERE. Results columns become SELECT. Summary type becomes GROUP BY. Joined columns become explicit JOIN clauses. Sort becomes ORDER BY. Write the first draft on one line if you like, then run it through the SuiteQL formatter before it goes anywhere near a code review. A saved search over invoices with open balance over 60 days becomes this:

SELECT
    BUILTIN.DF(t.entity)              AS customer,
    t.tranid                          AS invoice,
    t.trandate,
    t.duedate,
    t.foreignamountunpaid             AS open_amount,
    TRUNC(SYSDATE) - t.duedate        AS days_overdue
FROM transaction t
WHERE t.type = 'CustInvc'
  AND t.foreignamountunpaid > 0
  AND t.duedate < TRUNC(SYSDATE) - 60
ORDER BY t.foreignamountunpaid DESC
Open AR more than 60 days past due, largest first

One genuine piece of good news: saved search Formula (Numeric) fields are already Oracle SQL expressions. A CASE WHEN {amount} > 10000 THEN 1 ELSE 0 END formula ports almost verbatim — you swap the {fieldid} braces for the analytics column name and it works. Once the rows come back, the arithmetic on top of them is the same either way: feed the balances into the AR aging calculator and you get the bucket mix, weighted average days overdue and a reserve suggestion without building any of that into the query.

Performance: nobody wins by default

"SuiteQL is faster" is half true. A well-filtered SuiteQL aggregation usually beats exporting 40,000 saved search rows and summing them in Excel, because the database does the arithmetic and only the result crosses the wire. But a SuiteQL query with no selective filter, a function wrapped around a date column, or BUILTIN.DF() in the WHERE clause can be slower than the equivalent search. The engine paths differ; the discipline is the same. Filter on native columns, aggregate server-side, and page results. There is more on this in SuiteQL performance tips.

Permissions, and the answer that changes per user

Both mechanisms run under the permissions of whoever is asking. A SuiteQL query that returns 12,400 rows for an administrator can return 900 for a sales role restricted to its own customers, with no error and no warning. That is correct behaviour and a frequent source of "the report is broken" tickets. When you validate a query, validate it as the role that will run it, not as an administrator.

The REST SuiteQL endpoint also needs the account to have REST Web Services available and the integration role to hold the right permissions. If a query works in a Suitelet tool and fails over REST, suspect the role and the request headers before you suspect the SQL. Common failures are catalogued in common SuiteQL errors explained.

How to decide, in four rules

  1. 1.A human will read it interactively, on a schedule, with drilldown → saved search. Do not rebuild distribution you get for free.
  2. 2.A system will consume it → SuiteQL. A typed query is a better contract than a parsed export.
  3. 3.The UI cannot express the join or the aggregation → SuiteQL, and stop fighting the dropdown. The real ceilings are catalogued in NetSuite saved search limitations.
  4. 4.A non-technical owner needs to change it monthly → saved search, even if the SQL would be cleaner. Maintainability is who can maintain it.

In practice most accounts end up with both, and the split settles along a predictable line: saved searches for the operational lists people work from, SuiteQL for anything with real maths in it. The wider trade-off between native reports, searches, BI tools and conversational interfaces is laid out in ERP reporting tools compared. If you are starting from zero, the SuiteQL tutorial for beginners is the shorter path in.

The third option nobody has time to build

There is a category of question that deserves neither a saved search nor a hand-written query: the one-off. "Which customers over $50,000 open balance also have an order stuck in pending approval?" Nobody is building a saved search for that, and writing the SQL takes twenty minutes you do not have. This is the gap is built for — ask in plain English, get the number computed live from your own account, with the SuiteQL it ran shown underneath so you can check the join before you quote the figure. When the query turns out to be worth keeping, you already have the text.

Frequently asked questions

Is SuiteQL faster than a saved search?

Often, for aggregations over large volumes, because the database does the maths and only totals cross the wire. But a poorly filtered SuiteQL query can be slower than a well-built search. Speed comes from selective filters on native columns and server-side aggregation, not from choosing SQL.

Can SuiteQL do everything a saved search can do?

No. SuiteQL cannot deliver scheduled email, act as a dashboard portlet or KPI, provide drilldown links to records, drive workflow conditions, or support inline editing and mass update. Those are saved search features. SuiteQL returns data; everything around the data you build yourself.

Do you need a developer to run SuiteQL?

You need someone who writes SQL and has a way to run it — the SuiteTalk REST query endpoint, N/query in a script, or a Suitelet query tool. NetSuite has no native page where an end user types SuiteQL and gets a shareable report, which is why saved searches remain the default for non-technical users.

Can you schedule a SuiteQL query to email results?

Not directly. You write a scheduled or map/reduce script that runs the query, formats the output, and sends it with N/email. A saved search does the same job through configuration. If email delivery on a schedule is the whole requirement, the saved search is the cheaper answer.

Does SuiteQL bypass NetSuite role permissions?

No. SuiteQL runs under the permissions of the calling role or integration user, so restricted roles see fewer rows. The same query can legitimately return different totals for different users. Always test a query as the role that will run it in production, not as an administrator.

Should you rewrite existing saved searches in SuiteQL?

Only the ones that are already broken — hitting join limits, needing two aggregation grains, or feeding an integration through a fragile export. Working saved searches with human owners should stay. A rewrite trades a UI-editable object for code, and that only pays off when the query has real complexity.

Your ERP already knows. Start asking.

ERPray computes answers like these live from your own ERP account and shows the exact query behind every number. Early access is open for NetSuite teams — free plan at launch.