Author SHA1 Message Date
dc 2fe1bc2eea blog: add formula, range-value and NOTNULL-constraint caveats to the special missings post
Verified on a real SAS estate:
- a physical NOT NULL (or primary key) constraint rejects a special missing,
  and getdata merges that constraint into a frontend NOTNULL rule - which
  passes a special missing, so the editor is more lenient than the constraint
- a special missing as a MINVAL rule value fails every cell; as a MAXVAL rule
  value it fails every real number
- a HARDFORMULA/SOFTFORMULA reading a special-missing cell returns #VALUE!
- PRX and the JS engine agree on the value; SAS pads the numeric-to-character
  conversion, so an anchored pattern re-used in SAS needs strip()
2026-09-23 07:29:04 +00:00
dc 3e39339332 blog: scope the validation section to Data Controller rules and note the MISSING= gotcha
- heading + intro now say these are Data Controller's MPE_VALIDATIONS rules,
  applied in the browser
- new paragraph on options MISSING: a regular missing prints as . unless the
  option changes it (eg to blank); special missings are never affected
- CASE split out of the rule list: it is a character rule, and a special
  missing always reaches the browser as an uppercase letter
- HARDREGEX and SOFTREGEX separated (SOFTREGEX warns rather than blocks, and
  is ignored when the column also has a HARDREGEX)
- LinkedIn copy kept in sync

Behaviour confirmed against the deployed services on a real Viya estate.
2026-09-23 07:17:46 +00:00
dc 6d554930be docs(feed): add the cover image to the special missings post
Cover art for "28 Ways to Be Missing in SAS": a pack of wolves on a snow plain
at dusk with one animal standing apart, carrying the post's opening line - a
numeric missing is not a lone wolf, there are 28 of them.

Source image was 4:3, so it is cropped to 1.91:1 (1200x627) to match the other
feed covers and double as the LinkedIn share card. The crop was chosen to keep
both the lone wolf on the left and the full pack on the right in frame, losing
only sky above the clouds and foreground snow.

Sets previewImg in the front matter; the template renders it, so it is not
embedded in the body as well. Verified through gatsby build - the image
pipeline emits 300/600/1200-wide variants.
2026-09-22 21:36:39 +00:00
dc a56c2f8d48 docs(feed): record the final cover image prompt on the special missings post
Replaces the placeholder one-liner with the prompt actually intended for the
cover: the pack of wolves on a snow plain at dusk with one animal standing
apart, plus the negative constraints, the output spec (./cover.jpeg at
1200x627, previewImg in front matter) and a fallback single-subject variant.

Two deliberate choices are recorded so they are not lost on a regenerate:

- No text, letters or numbers anywhere in the image. The subject is letters
  standing in for numbers, so a stray glyph undercuts the cover.
- No exact head count of 28. Generators cannot count, and a crowded pack reads
  worse than a dozen clear animals; the number belongs in the headline.

Links to the full prompt and variants on paste.4gl.io.
2026-09-22 21:17:41 +00:00
dc 27ff40b739 blog: correct the video label on the v4.0 special missings post
The special missings video was labelled "Retain Formulas when Loading Excel
to SAS", which is a different video. Restores the one-line correction that
was in the earlier revision of this branch, kept separate from the feed post.
2026-09-22 18:10:28 +00:00
dc 4937edd7f4 feed: add a special missings post, and revert the blog edits
The special missings material that was added to the v4.0 blog post belongs in
its own post: that article is a release announcement for v4.0, not a teaching
piece. This reverts the blog change and publishes the content under /feed/.

The new post covers what SAS special missings are, how the SASjs Adapter
carries them between the browser and SAS, and how Data Controller's validation
rules treat them - NOTNULL passes, MINVAL fails, MAXVAL passes, and the regex
rules apply as they would to any other value.

The LinkedIn version of the copy, and the image prompt for the cover, are
recorded in a comment at the foot of the file.
2026-09-22 17:53:55 +00:00
3 changed files with 136 additions and 1 deletions
@@ -61,7 +61,7 @@ Did you know that, in addition to a regular missing value in SAS (`.`), there ar
These values can now be both viewed and edited in Data Controller following an update to the [SASjs Adapter](https://github.com/sasjs/adapter#variable-types). These values can now be both viewed and edited in Data Controller following an update to the [SASjs Adapter](https://github.com/sasjs/adapter#variable-types).
`video: [Retain Formulas when Loading Excel to SAS](https://www.youtube-nocookie.com/embed/ggrcNr23Jzw)` `video: [Managing Special Missing Values with Data Controller for SAS](https://www.youtube-nocookie.com/embed/ggrcNr23Jzw)`
There is nothing extra to configure for special SAS numerics - they are simply available by default, for numeric cells. There is nothing extra to configure for special SAS numerics - they are simply available by default, for numeric cells.
Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

+135
View File
@@ -0,0 +1,135 @@
---
title: '28 Ways to Be Missing in SAS'
description: A SAS numeric missing is not a lone wolf - there are 28 of them, and Data Controller has handled all of them since v4. How they work, and what the validation rules do with them.
date: '2026-09-22 09:00:00'
author: 'Allan Bowe'
authorLink: https://www.linkedin.com/in/allanbowe/
tags:
- Special Missings
- Data Quality
previewImg: './cover.jpeg'
---
# 28 Ways to Be Missing in SAS
A numeric missing value in SAS is not a lone wolf. The ordinary missing (`.`) is one of **28** distinct missing values available for a numeric variable - the other 27 are written with a single character, the letters `A` to `Z` or an underscore (`._`).
They exist because "missing" is usually not the whole story. A survey question that was never reached, a reading that was illegible, a value the respondent refused to give - in a well run process those are different facts, and a lone `.` throws the difference away. Special missings record *why* the value is missing.
## They are numbers, not text
- Arithmetic on them yields missing - `.A + 1` is `.`
- `proc means`, `proc summary` and friends exclude them, exactly as they exclude `.`
- They sort below every non-missing number, in the order `._`, then `.`, then `.A` to `.Z`
- The `NMISS()` and `CMISS()` functions count them as missing
- Converting one to text drops the period - `cats(.A)` is the string `A`
A display gotcha worth knowing: a regular missing prints as `.`, unless `options MISSING=` changes that character - set it to blank and a regular missing renders as an empty cell. The option affects only the regular missing; `._` and `.A`-`.Z` always print as their own letter. So a blank cell in a SAS listing is still unambiguously a regular missing, and a lone letter is still a special one. Data Controller itself is unaffected either way - it sends a regular missing to the browser as `null` and a special missing as its letter.
In a SAS dataset they are written with a leading period (`.A`, `.B` ... `._`). In Data Controller you type just the letter or the underscore - no period - and the letter is not case sensitive. Two letters, or a letter mixed with a number, are refused rather than guessed.
## Carrying them between the browser and SAS
The Data Controller frontend and the SAS backend exchange data as JSON, and JSON has no way to express a letter as a numeric value - `A` is a string. The conversion is handled in the open source [SASjs Adapter](https://github.com/sasjs/adapter#variable-types):
- The adapter infers each column's SAS type from the values it is given. All numeric values means numeric; all strings means character; and a column holding a single character (`a`-`z`, `_` or `.`) alongside numeric values is numeric, with the lone characters written to SAS as special missings.
- `null` becomes `.` or an empty string, according to the type derived for that column.
- Two cases cannot be inferred from the values alone: a numeric column containing *only* special missings looks like a single character column, and a character column containing only nulls looks numeric. For those, the adapter accepts an explicit format for the column - and Data Controller sends one automatically, because it already knows each column's SAS format from the metadata returned by the backend.
- A value that is neither a number nor a single valid character is refused rather than guessed (for example `aaaa`, or `!` in a numeric column), and a literal `.` is refused in favour of `null` for a regular missing.
There is nothing to configure. Special missings are available by default, for numeric cells.
Once in SAS they are ordinary values, so they are what the approval DIFF screen compares, and the DIFF's formatted / unformatted switch shows either the formatted representation or the raw value - useful for confirming exactly which missing was set.
## What the Data Controller validation rules do with them
These are Data Controller's own rules, configured per column in the `MPE_VALIDATIONS` table and applied in the browser as you edit and submit. A special missing is a value somebody deliberately set, so the rules treat it as a value - it is not blank, and it is not exempt:
- `NOTNULL` - passes, because it is not null
- `MINVAL` - fails, because a special missing sorts below every number and so is below any minimum
- `MAXVAL` - passes, for the same reason it is below any maximum
- `HARDREGEX` - checked against the pattern like any other value; unlike blanks and the plain `.`, special missings are **not** exempt, so a numeric column that carries them needs a pattern which allows for a single letter
- `SOFTREGEX` - the same check, but a failure is only a warning rather than a block, and it is ignored entirely if the column also has a `HARDREGEX` rule
- `HARDFORMULA` / `SOFTFORMULA` - a formula that reads a special-missing cell does not compute; the grid's spreadsheet engine returns `#VALUE!` for that row, where the plain `.` contributes 0
One mismatch to be aware of: the `NOTNULL` rule above is the editor's, and it is more forgiving than the SAS constraint it mirrors. A real NOT NULL - or primary key - constraint on the target table rejects a special missing, so on a column carrying one the editor will let the value through and the load will then fail on the constraint.
Two other sharp edges. A range rule's *value* has to be a number: put a special missing in `MINVAL` and every cell in the column fails, put one in `MAXVAL` and every real number fails. And although the regex pattern is written in SAS PRX syntax, the check itself runs in the browser - SAS only parses the pattern (`PRXPARSE`) when the rule is saved - so the two engines can disagree on exotic patterns, and SAS pads a numeric-to-character conversion, so a pattern re-used in SAS needs `strip()` for an anchored match.
`CASE` (`UPCASE` / `LOWCASE`) is a character rule and does not come into it: SAS hands the browser a special missing as an uppercase letter, so there is no case to enforce, and a `CASE` rule on a numeric column would reject the column's numbers rather than the missing.
That is usually what you want - a value recorded as "not collected" should not quietly satisfy a completeness or range rule. It does mean a range rule on a column that uses special missings will warn on those rows, which is the signal to either accommodate them in the rule or not use special missings on that column.
`video: [Managing Special Missing Values with Data Controller for SAS](https://www.youtube-nocookie.com/embed/ggrcNr23Jzw)`
The recording shows the full cycle: typing special missings into numeric cells, the rejections, submitting the changes, approving them, and reviewing the DIFF - including a change from one special missing to another, and the formatted / unformatted switch on date and datetime columns.
<!--
LinkedIn version of this post - publish it with the "Managing Special Missing
Values with Data Controller for SAS" video attached. Kept in sync with the copy
above: same points, same claims, same order.
A SAS numeric missing is not a lone wolf. It is 28.
The ordinary missing (.) is just the most common of them. The other 27 are single characters - the letters A to Z, or an underscore (._) - and they let you record WHY a value is missing: the question was never reached, the reading was illegible, the respondent refused.
They are real numeric values, not text:
- .A + 1 is .
- PROC MEANS excludes them, like any other missing
- they sort below every number: ._ then . then .A to .Z
- NMISS() and CMISS() count them as missing
- a regular missing prints as . unless options MISSING changes it - and that option never touches a special missing
The hard part is every tool that is not SAS. JSON has no way to say "this number is a letter" - A is just a string. So a value that is perfectly legal in a SAS dataset quietly breaks in the browser, in Excel, in an API.
We solved that in the open source SASjs Adapter, and it has been in Data Controller for SAS since v4:
- the adapter infers the column type from the values, so a column of numbers that also holds a lone letter is written to SAS as numeric, with the letter as a special missing
- where the type cannot be inferred - a numeric column holding ONLY special missings - Data Controller passes the column format explicitly
- you type the letter on its own: a or A, no period, and case does not matter
And they behave like values in Data Controller's validation rules, which is usually what you want:
- NOTNULL passes - it is not null (though a real SAS NOT NULL constraint is stricter, and does reject a special missing)
- MINVAL fails - it sorts below every number; MAXVAL passes for the same reason
- HARDREGEX checks it against the pattern, unlike blanks and plain .
- SOFTREGEX warns instead of blocking (and is ignored if the column also has a HARDREGEX)
- a HARDFORMULA/SOFTFORMULA that reads a special-missing cell returns #VALUE! rather than a number
- CASE is a character rule, so it has no place on a numeric column
So a value recorded as "not collected" cannot quietly satisfy a completeness or range rule.
The video shows the whole cycle - entering them, the rejections, submit, approve, and the DIFF.
#sas #datacapture #mdm #dataquality
Image prompt: Cinematic editorial cover illustration: a large pack of wolves, a
dozen or more, spread wide and moving together across a vast snow plain at dusk,
seen from a low wide angle. One wolf stands apart from the group on the left of
frame, turned back toward the pack. The pack is rendered in cool blue-grey and
silver; the lone wolf catches the only warm light in the scene, a single low
amber sun. Overcast dusk sky, faint falling snow, long soft shadows, generous
empty sky and snow to the upper third so the wide crop breathes. Painterly
digital illustration, muted desaturated palette, soft depth of field with the
distant wolves falling out of focus, no text, no letters, no numbers, no logos,
no watermark.
Constraints: no text, no letters, no numbers, no digits, no symbols, no
captions, no logos, no watermark, no signature, no border, no frame, no collar,
no harness, no humans, no buildings, and do not ask for an exact head count of
28 - a crowded pack reads worse than a dozen clear animals, and the number
belongs in the headline, not the artwork. The no-text rule matters more than
usual here: the subject is letters standing in for numbers, so a stray glyph
anywhere undercuts the cover.
Output: save as ./cover.jpeg at 1.91:1 (1200x627, matching the other feed
covers and doubling as the LinkedIn share card), and set
previewImg: './cover.jpeg' in the front matter above. Do not also embed the
image in the markdown body. Full prompt, variants and rationale:
https://paste.4gl.io/?513711f5bde3610e#9LZCA9xzQHDy3CycXmsWU5AoVNtjBZmPotUHHtHTAerF
Fallback variant if the pack composition comes back muddled: a single wolf
standing alone on a snow plain at dusk, its shadow stretching toward a distant
pack reduced to small silhouettes on the horizon.
-->