# Supported data types (SQL Server)

## Writing: .NET to SQL Server

| .NET value | Column | Stored as |
|---|---|---|
| `string` | `nvarchar(n)`, `varchar(n)`, `nchar(n)`, `nvarchar(max)` | as written (`nchar(n)` pads with spaces) |
| `byte` / `short` / `int` / `long` | `tinyint` / `smallint` / `int` / `bigint` | `200` / `32000` / `2000000000` / `9000000000000` |
| `decimal` | `decimal(p, s)`, `money` | `12345.678901` |
| `float` / `double` | `real` / `float` | `3.25` / `2.718281828459045` |
| `bool` | `bit` | `1` |
| `Guid` | `uniqueidentifier` | `6F9619FF-8B86-D011-B42D-00C04FC964FF` |
| `DateTimeOffset` | `datetimeoffset` | the moment, in UTC: `22:30 +08:00` is stored as `2026-11-07 14:30:00 +00:00`. The same with a hand-written catalog, a preload or none: like PostgreSQL and MySQL, ARX keeps the moment, not the offset (store the offset in a column of its own if the application needs it) |
| `DateTimeOffset` | `datetime2`, `datetime` | the moment in UTC (`2026-11-07 14:30:00`), with or without a catalog (SQL Server alone would drop the offset and keep `22:30`, eight hours off) |
| `DateTime` | `datetime2`, `datetime` | as written: `2026-11-07 09:00:00` (`datetime` rounds to 1/300 of a second) |
| `DateOnly` / `TimeOnly` | `date` / `time` | `2026-11-07` / `17:45:30` |
| `byte[]`, `ArxBytea` | `varbinary(n)`, `varbinary(max)` | `0xCAFE` |
| `ArxJsonB` | `nvarchar(max)`, with `CHECK (ISJSON(column) = 1)` | the JSON text (invalid JSON fails the check: `ARX3004`) |
| `ArxEntityReference` | the lookup column | the referenced id |
| `ArxSelectionSetValue` | the set's column | the option's code |
| `null` | any | `NULL` |
| `sbyte` / `ushort` / `uint` / `ulong` | converted to `smallint` / `int` / `bigint` / `decimal` | `-100` / `65000` / `4000000000` / `18446744073709551615` |
| `char` | text | `X` |
| a plain `enum` | refused (ARX1007) | write `(int)value` or `value.ToString()`, or use a selection set, which stores the code its set declares |
| `DateTime` with Kind `Utc` | `datetimeoffset` | the moment, offset `+00:00` |
| `DateTime` with Kind `Local` | `datetime2`, `datetimeoffset` | converted to UTC first |

Refused before anything is sent: any other type (`ARX1007 UnsupportedValueType`: write `uri.ToString()` rather than a
`Uri`); arrays (`ARX8001`: SQL Server has no array columns, so store a list as delimited text, JSON or rows of its own);
`NaN` and the infinities, and a `TimeSpan` below zero or of 24 hours or more (`ARX4001`: `time` holds a time of day, so
store a duration as a number).

With the column types preloaded (`ArxPreLoadMode.DataTypes`), a number with a fraction written to a whole-number column is refused before SQL Server (`ARX4001 ValueOutOfRange`) on a create, an update and a write by a filter, where SQL Server would round or truncate it; a condition in a read (`qty < 7.5`) isn't refused. A `NaN` or an infinity written to a declared `decimal` column is refused the same way.

SQL Server converts some values silently that other databases refuse: `"42"` into an `int`, a number into a `bit`, a
`decimal` into an `int` by cutting off the fraction. A preload with column types (`ArxPreLoadMode.DataTypes`) refuses
those before the database. `RealCase2_EveryDataType` writes every row of this table and prints what SQL Server stored.

## Dates and times

| You have | Use | Column |
|---|---|---|
| A moment (an order was placed) | `DateTimeOffset` | `datetimeoffset` |
| A moment, in a column without an offset | `DateTime` with Kind `Utc` | `datetime2` (store UTC, read it back as UTC) |
| A wall-clock time with no zone | `DateTime` with Kind `Unspecified` | `datetime2` |
| A calendar date | `DateOnly` | `date` |
| A time of day | `TimeOnly` | `time` |
| A duration | a number (seconds, minutes) | `int`, `bigint` |

SQL Server keeps no time zone: `datetime2` stores the clock time it is given, and `datetimeoffset` the clock time and
its offset. ARX writes a `DateTimeOffset` as its UTC instant and converts a local `DateTime` to UTC first, so a moment is
never stored hours off. Refused: `default(DateTime)` (`ARX1013`: it means "not set",
and SQL Server would store `0001-01-01`; use `null`). ARX never parses a string as a date, but SQL Server converts
one it can read, by the session's date format (`01/07/1992` is January 7 under `us_english`), and refuses one it
can't (`ARX4003`); with a schema preloaded with data types, ARX refuses every date string first (`ARX4004`). Parse
dates yourself. Full rules: [Data types](/csharp/sql/data-types).

## Reading: SQL Server to .NET

| SQL Server column | Reads back as | Notes |
|---|---|---|
| `nvarchar(n)`, `varchar(n)`, `nchar(n)`, `nvarchar(max)` | `string` | `nchar(n)` keeps its padding spaces; `""` stays `""` |
| `tinyint` / `smallint` / `int` / `bigint` | `byte` / `short` / `int` / `long` | |
| `decimal(p, s)`, `money` | `decimal` | More than 28 significant digits: `ARX4001 ValueOutOfRange` |
| `real` / `float` | `float` / `double` | |
| `bit` | `bool` | |
| `uniqueidentifier` | `Guid` | |
| `datetimeoffset` | `DateTimeOffset` | The clock time and the offset it was stored with |
| `datetime2`, `datetime`, `smalldatetime` | `DateTime`, Kind `Unspecified` | The clock time as stored: SQL Server keeps no zone |
| `date` / `time` | `DateOnly` / `TimeOnly` | |
| `varbinary(n)`, `varbinary(max)`, `rowversion` | `byte[]` | |
| JSON in `nvarchar(max)` | `string` | SQL Server 2022 has no JSON type: the text as stored |
| `sql_variant` | the value's own type | |
| An alias type (`CREATE TYPE … FROM nvarchar(320)`) | Its base type | |
| A declared lookup (`.Lookup(...)`) | `ArxEntityReference` | `Name` from the referenced table's `.DisplayName(...)` |
| A selection set column | Its stored code | `entity.GetSelectionSet(column, set, catalog)` gives the option |
| NULL | `null` | The attribute is present, with a null value |

`RealCase1_EveryDataTypeReadBack` writes one row of every type with Create and prints each value's .NET type as it
reads back.
