The month-end search that never came back

On the second working day of the month the controller opens the transaction search that feeds the close pack, and the page sits there until the session gives up. Nothing changed that week. Nobody edited the search, nobody touched the role, and it ran fine in August.

That search was built during implementation, against a company with eighteen months of transactions in it. It has been widening ever since. Every month it reads a little more than the month before, and because month-end is the only time anyone runs it, nobody watched it get worse. They watched it stop working.

Nearly every slow saved search in NetSuite fails for the same reason. It retrieves far more records than the reader needs and then discards most of them. The useful question is never how many rows come back. It is how many rows the platform had to touch to produce them.

Filters that cut the set before anything is retrieved

A criterion that narrows the record set at the source does a different order of work from one applied to rows that have already been assembled. In the search definition the two look identical. One of them means the platform reads less. The other means it reads everything and then throws away what did not match.

So put the sharpest filters on the record the search is built on, using standard field criteria rather than formulas, and make them as specific as the requirement allows. Transaction type, posting status and subsidiary each remove whole classes of record before any other work happens. A search restricted to posted vendor bills in one subsidiary is a different size of problem from a search across all transactions.

Criteria that live on a joined record, or inside a formula, cannot narrow anything until that data has been fetched. Move back to the base record whatever you can. Where the requirement genuinely needs a joined field to cut the set, pair it with a base-record filter that already removes most of the volume, so the join runs over something small.

The date range nobody has looked at since go-live

The most common single cause I find is a transaction search with no bound on date at all. It was correct the day it shipped, because the account held one year of data. At year three it reads three times as much and returns the same twelve rows the controller actually uses.

Put a bounded date filter on every transaction search, including the ones where somebody insists they want everything. Relative ranges hold their shape while the account grows, so a search limited to the last two fiscal quarters stays roughly the same size forever, while an open-ended one gets slower every night without anyone doing a thing.

The awkward requirements are the genuine ones. Aging, lifetime revenue, first order date. Split those into a bounded operational search people run during the day and a heavier historical one that runs overnight. Ask what window the reader actually acts on, which is part of making the analytics question a requirement rather than a preference. The answer is almost always shorter than the window the search covers.

Joins and formulas multiply the cost of every row

Every join to a related record adds work for each row the search touches. A transaction search that reaches the customer, then the sales rep on that customer, then the subsidiary on the rep, is doing several lookups per line, and a line-level transaction search can hold many lines per document. Cost grows with the size of the set multiplied by the number of joins.

Formula columns are evaluated for each row once the set exists. A plain concatenation is cheap. A formula carrying conditional logic that reaches across joined records is the most expensive thing in most slow searches, because it pays the join cost and the evaluation cost on every row, including the rows nobody will read.

When a formula is computing something the platform could store, move it. A field populated at save time by a workflow or a script costs nothing at search time and can be filtered on directly. That trade pushes complexity into the write path, so make it on purpose, and it is the same argument as where business logic should live on any platform. For a search hundreds of people run daily it pays back fast.

Totals, extra columns and sorting cost more than people expect

A search that groups by customer and sums amount does one piece of work. A search that returns every line and also shows a grand total does that work twice, once to assemble the rows and once to aggregate them. Plenty of searches ask for both because somebody wanted a number at the bottom of a detail report, and nobody costed it.

Decide which question the search answers. If the reader wants one number per customer, build the summary and let them drill into the detail. If they want the lines, give them the lines and put the total somewhere else. How much of that work the platform can skip varies by release and by how the search is built, so build two versions and time them rather than trusting the theory.

Result columns nobody reads still cost retrieval, and columns that reach into joined records cost the most, so strip the results back to what the reader uses and wait to see who complains. Sorting deserves its own thought, because a sort forces the whole result set to be assembled before the first row can come back. A search with no sort can start returning rows while it is still working. A sorted one cannot start until it has finished.

What to check when someone reports a slow search

Resist the urge to rebuild it. Copy the search, remove one thing, time it, and put the thing back. The difference between two runs tells you more than any amount of staring at the definition, and it gives you something concrete to show the person who complained.

Run it with the access the person who reported it has, and never with full administrator rights. Role restrictions change which records a search can see, and that filtering is part of the work the search performs. A search that returns in four seconds for an administrator can take minutes for a role restricted across many subsidiaries or departments, and testing with full access hides the exact behaviour you were asked to explain. The same trap shows up in Workday custom report performance, for the same reason.

The order I work through, roughly by how often each one turns out to be the answer.

  • The date filter, whether it is bounded, and when anyone last looked at it.
  • Where each criterion sits, base record first and joined records last.
  • Formula criteria and formula columns, counted, then removed one at a time.
  • Every related record the search reaches into, including ones used only by a column.
  • Result columns the reader never opens.
  • Summary and detail in one search, which pays the expensive cost twice.
  • The sort, removed, to see what the search does without it.
  • The role of the person who reported the problem.
  • How the search is delivered, interactive at month end or scheduled and emailed.

Schedule the heavy ones and move the extracts out

Anything heavy belongs on a schedule with the result delivered by email, rather than in a browser at month end with somebody watching a spinner. The search runs when the account is quiet, and the controller opens a result that was already waiting. Scheduling and delivery options vary by account, role and edition, so confirm what yours supports before you promise it to finance.

There is a point where a saved search is the wrong tool. Where the requirement is thousands of rows extracted on a fixed schedule and loaded somewhere else, that is an extract, and it belongs in an integration rather than in the user interface. Saved searches are built to answer a question for a person looking at a screen. Pushing a full data set down that path runs slower, fails quietly, and leaves you with no retry and no record of what was sent.

Move that work to whatever query and extract tooling your account supports, run it from a scheduled job, and give it logging and a failure path, because integration error handling that pages someone applies to a nightly export the same way it applies to an order feed.

Start with the search people complain about most. Copy it, bound the date filter, cut the columns nobody opens, and run it under the role that reported the problem. If it is still slow after those four changes, the answer is in the joins and the formulas, and you already know which ones to pull out first.