Back to blog
PrimentraPrimentra
·August 26, 2026·7 min read

Partial import: one bad row stopped costing you the whole file

Home/Blog/Partial import: one bad row stopped costing you the whole file
4,200-row file, one empty CodeBEFORE 1.2026.8.20Cannot insert the valueNULL into column 'Code'0 of 4,200 rows loadedNOW4,199 of 4,200 loadedrow 1,832: CodeMissingfix it, import it againGrid import: you tick a box to allow it. Migration wizard: on by default.usp_Data_BulkImportRows, @AllowPartial, 1.2026.8.20

A customer sent us a 4,200-row supplier file. One row had an empty Code. The import ran as one transaction, that one row broke a NOT NULL constraint, and SQL Server rolled back all 4,200 rows and returned a message that named the column, not the row. The only way to find the culprit was to bisect the file by hand: import half, see if it fails, cut it in half again.

As of 1.2026.8.20, that row gets rejected on its own, and the other 4,199 load.

Why the whole file used to die for one row

Business-rule failures were already survivable. Turn on Refuse rows that break an error rule and the procedure writes the row, checks it, and puts it back if it fails. The rest of the batch is untouched. Hard constraint violations were a different story. A missing code, a duplicate key, a value that overflowed its column: any one of those aborted the transaction before that machinery ever ran, and a constraint violation gives you a column name, not a row number.

The stored procedure that does the writing, usp_Data_BulkImportRows, is set-based: no cursor, no per-row loop. A per-row loop over tens of thousands of rows would make it correct and slow. So the fix finds the bad rows before the insert, in one set-based pass, instead of catching them one at a time after it.

What gets rejected, and why

Eight checks run before anything is written. Each one names a reason, and the reason ships back with the row's position in your file: row 1,832, not "a row".

Code missing or too long

Empty on an entity that does not auto-generate codes, or past 200 characters

Duplicate code

Two rows in the file share a code, or the code already exists and the import may not change it

Name missing or too long

Empty, or past 500 characters

Value has the wrong type

Text in a number field, an unparseable date, that kind of mismatch

Value too long

Longer than the attribute's configured width

Required field empty

A custom attribute marked Required has no value

Domain reference to nothing

The value matches no record in the linked entity

Negative where negatives are refused

A field configured to reject negative numbers got one

A ninth reason, a broken business rule, joins the same report but arrives later. Rules can only be checked against a row that has already been written, so a rule failure is caught after the insert and the row is put back to what it was before.

The grid import: you decide, every time

In the Excel/CSV import wizard, Preview checks your file against the database before anything is written. Find no problems and it says so. Find some, and a red panel lists them by row, code and reason, with a checkbox underneath: Import the other rows anyway. Import stays disabled until you tick it, so a partial load never happens by accident. You see what will be left out before you commit to anything.

Change the conflict action or the rule-handling option afterward and the check reruns and the box unticks itself, because either one can change which rows would be refused. After the import, a Rejected counter appears on the result screen, and Download rejected rows writes every one of them to an Excel file with the reason attached. Fix them in the source and run the file again.

The migration wizard: on by default, and for a different reason

Migrating out of MDS is a different situation from a routine CSV drop. A legacy export is expected to contain a row or two that will not fit the new model, say a domain reference the source system tolerated and the target does not, and stopping the whole migration for one of them is worse than the alternative. So here partial import runs without a checkbox. Every entity's rejects are collected into one report across the whole migration, by entity, with the reason and a download link, instead of the reference silently resolving to nothing the way it used to.

What this does not fix

It does not touch the 50 MB request-size ceiling on an import payload. That is a different problem, and not one we had a real complaint behind. It has nothing to do with character encoding; a file saved in the wrong code page still comes through mangled, because that happens before the row reaches any of these checks. And it does not make the rows that did load correct. A column mapped to the wrong field, or a value truncated on the way in, will not raise a single one of these eight reasons. It will just be wrong, quietly, the way it was before. That gap is what a reconciliation pass is for, and how to validate a master data migration covers the checks that catch it. Partial import tells you which rows never arrived. It was never going to tell you whether the ones that did are correct.

Common questions

What changed, in one sentence?

A row that cannot be written is rejected on its own, with a reason, and the rest of the file loads anyway. Before 1.2026.8.20, one bad row rolled back everything, because the whole import ran as a single transaction.

Do I have to turn it on?

In the grid import, yes. It is a checkbox that only appears once Preview finds a problem, and Import stays disabled until you tick it. In the MDS migration wizard it runs by default, because a legacy export is assumed to have rows that will not fit.

Where do I see which rows were rejected?

Preview shows them grouped by reason before you commit. After the import, the result screen lists them again with a Download rejected rows button that writes every reject to an Excel file, not just the ones shown on screen.

Does it catch business rule failures too?

It reports them in the same list, but it cannot check them before the write, because a rule is evaluated against a row that exists in the database. Refuse rows that break an error rule, on by default, still catches those after the fact and reverses them.

Does a clean partial-import run mean the data is correct?

It means every row that loaded satisfied its constraints. It says nothing about whether a value landed in the right column or survived a source-to-target mapping intact. That is a separate check, and skipping it is still the most common way a migration goes wrong quietly.

Try it on your own file

The 60-day trial includes the full import wizard, on your own SQL Server, with nothing held back. Drop in a messy file and see the reject list before you decide anything commits.

Start free trial →Try the demo →

More from the blog

IF/THEN rules: making a field required only when it actually matters9 min readYour MDS staging batch is not processing. Work down this list.10 min readMDS staging errors: where they hide, and what every error code actually means10 min read

Ready to migrate from Microsoft MDS?

Download Primentra and run it on your own server, or try the live demo first. All features included.

Download Free TrialTry DemoCompare MDM tools
Partial Import: Skip Bad Rows, Keep the Rest | Primentra