Skip to content

Commit b2be99e

Browse files
authored
docs: document populating NOT NULL columns from bulk procedures (#833)
* docs: document populating NOT NULL columns from bulk procedures Add guidance under 'Adding a column to a table' on keeping a NOT NULL column backwards compatible during rolling deployments when it is populated through an OPENJSON/TVP bulk procedure, using ISNULL at each insert/update site. * Remove TVP callout
1 parent 386dcf4 commit b2be99e

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

  • docs/contributing/code-style

docs/contributing/code-style/sql.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,54 @@ variants), as this can lead to unnecessary storage overhead and performance issu
766766

767767
:::
768768

769+
##### Populating a `NOT NULL` column from a bulk JSON procedure
770+
771+
When a `NOT NULL` column is populated through a bulk procedure that reads from `OPENJSON` (e.g.
772+
`OrganizationUser_CreateMany`, `OrganizationUser_UpdateMany`), guard the value with `ISNULL` at
773+
every insert and update site so that a rolling deployment stays backwards compatible.
774+
775+
A scalar procedure parameter stays backwards compatible through its own default value
776+
(`@Column BIT = 0`), so an old server that calls the procedure without the new argument still works.
777+
A JSON field has no equivalent per-field default: during a rolling deployment an old server sends a
778+
payload that omits the field and `OPENJSON` yields `NULL`. The value you fall back to differs
779+
between inserts and updates.
780+
781+
For **inserts**, fall back to the column's default, since the row is new.
782+
783+
This breaks during a rolling deployment:
784+
785+
```sql
786+
INSERT INTO [dbo].[Table] ([Column])
787+
SELECT
788+
OUI.[Column] -- NULL when the payload predates the column
789+
FROM
790+
OPENJSON(@jsonData) WITH ([Column] BIT '$.Column') OUI
791+
```
792+
793+
This is safe:
794+
795+
```sql
796+
INSERT INTO [dbo].[Table] ([Column])
797+
SELECT
798+
ISNULL(OUI.[Column], 0)
799+
FROM
800+
OPENJSON(@jsonData) WITH ([Column] BIT '$.Column') OUI
801+
```
802+
803+
For **updates**, fall back to the existing value, not the default. Falling back to the default lets
804+
an old server that omits the field overwrite a value that a new server has already written:
805+
806+
```sql
807+
UPDATE
808+
OU
809+
SET
810+
[Column] = ISNULL(OUI.[Column], OU.[Column])
811+
FROM
812+
[dbo].[Table] OU
813+
INNER JOIN OPENJSON(@jsonData) WITH ([Id] UNIQUEIDENTIFIER '$.Id', [Column] BIT '$.Column') OUI
814+
ON OU.[Id] = OUI.[Id]
815+
```
816+
769817
#### Changing a column data type
770818

771819
You must wrap the `ALTER TABLE` statement in a conditional block, so that subsequent runs of the

0 commit comments

Comments
 (0)