Chapter 03 · T-SQL Foundations, Data Types, NULL, Conversion, and Expressions

Exact/Approximate Numerics, Strings, Unicode, Dates, Times, GUIDs, Binary, XML, and JSON

Choose SQL Server data types from domain semantics—precision, Unicode, time, identifiers, binary/XML/JSON behavior—and keep preview SQL Server 2025 JSON features out of mandatory production guidance.

Intermediate100–125 minutesType-system + JSON feature-status labSQL Server 2025 · native json currently previewDeveloper/Express · stable mandatory pathLast reviewed: August 2026

Learning outcomes

ServiceHub’s schema already contains integers, text, datetime2, bit, and rowversion. A new integration now proposes storing money in float, multilingual technician notes in varchar, local appointment times without offsets, GUIDs as 36-character text, and request payloads in whatever JSON feature happens to be newest. Those choices can work syntactically while still create precision, collation, storage, interoperability, or upgrade problems.

01

Choose exact versus approximate numeric types based on business meaning rather than display formatting.

02

Distinguish varchar/nvarchar, Unicode, collation, length units, and binary payloads.

03

Choose among date/time types and explain when datetimeoffset preserves information that datetime2 does not.

04

Use uniqueidentifier, varbinary, and XML deliberately rather than encoding every value as text.

05

Separate stable nvarchar-based JSON support from the SQL Server 2025 native json type, which Microsoft currently documents as preview for SQL Server 2025.

Feature-status rule

The mandatory lab does not require SQL Server 2025 preview features. Microsoft currently documents the native json data type as preview for SQL Server 2025 (17.x), even though long-standing JSON functions over string data are stable. Re-check this status when the lesson is generated or updated.

1. Exact numerics model quantities; approximate numerics model ranges

tinyint, smallint, int, and bigint are exact integral types. decimal(p,s)/numeric(p,s) are exact fixed-precision types where precision p is the total digit count and scale s is digits to the right of the decimal point. float and real are approximate binary floating-point types.

Approximate does not mean “bad.” It is appropriate for scientific/measurement workloads where range and floating-point semantics are accepted. It is usually the wrong default for money, invoice totals, quotas, or identifiers where equality and exact decimal arithmetic are business requirements.

sql · compare exact decimal and approximate float
USE ServiceHubLab;GODECLARE @exact decimal(19,4) = 0.1,        @approx float = 0.1;SELECT    @exact * 10 AS exact_times_ten,    @approx * 10 AS approx_times_ten,    SQL_VARIANT_PROPERTY(@exact, 'BaseType') AS exact_type,    SQL_VARIANT_PROPERTY(@approx, 'BaseType') AS approx_type;GOSELECT    CAST(0.1 AS float) + CAST(0.2 AS float) AS approximate_sum,    CAST(0.1 AS decimal(10,4)) + CAST(0.2 AS decimal(10,4)) AS exact_sum;GO

Do not turn a single displayed result into a universal floating-point demonstration; client formatting can hide binary representation differences. The contractual lesson is that float is approximate by definition. Choose the type from required arithmetic/rounding behavior, then test boundary values.

2. varchar and nvarchar carry different encoding/collation assumptions

varchar stores non-Unicode character data according to collation/code-page rules; UTF-8 collations can change that storage model. nvarchar stores Unicode data using UTF-16 encoding semantics. The N'...' literal prefix creates a Unicode string literal. Choosing a string type is therefore not merely a maximum-character-count question—it affects representation, implicit conversion, collation, indexes, and driver parameter metadata.

sql · observe Unicode literals, length and storage bytes
USE ServiceHubLab;GODECLARE @v varchar(20) = 'ServiceHub',        @n nvarchar(20) = N'ServiceHub';SELECT    LEN(@v) AS varchar_chars,    DATALENGTH(@v) AS varchar_bytes,    LEN(@n) AS nvarchar_chars,    DATALENGTH(@n) AS nvarchar_bytes;GOSELECT    c.name AS column_name,    TYPE_NAME(c.user_type_id) AS data_type,    c.max_length,    c.collation_nameFROM sys.columns AS cWHERE c.object_id = OBJECT_ID(N'ops.Technician')ORDER BY c.column_id;GO

Notice that max_length is stored in bytes in catalog metadata, so an nvarchar(100) column commonly reports 200. Chapter 02 already established that collation is a comparison/sort contract; data type and collation must be considered together.

3. Date/time types preserve different information

Use date for calendar dates, time for time-of-day, datetime2 for a date/time value without an offset, and datetimeoffset when the offset is part of the stored value. Legacy datetime and smalldatetime remain supported but have older ranges/precision semantics; new designs should justify them rather than choose them by habit.

A timestamp that means “the instant this work order was opened” is often normalized to UTC and stored in datetime2, with the application’s business time-zone context stored separately. If the original offset matters (for example, an appointment entered as 09:00 at +04:00), datetimeoffset can preserve that offset. It still does not store a named time zone or daylight-saving rule set.

sql · preserve instant versus offset
SELECT    CAST('2026-08-22T13:30:00' AS datetime2(0)) AS no_offset_value,    CAST('2026-08-22T13:30:00+04:00' AS datetimeoffset(0)) AS offset_value,    SWITCHOFFSET(CAST('2026-08-22T13:30:00+04:00' AS datetimeoffset(0)), '+00:00') AS same_instant_utc;GO

4. GUID, binary and XML are first-class types—not text conventions

uniqueidentifier stores a 16-byte GUID value with SQL Server comparison rules. It avoids wasting space on the textual punctuation and prevents invalid “GUID-shaped” strings from entering the column. It can still be a poor clustered-key choice under some insertion patterns; key/index design is Chapter 9 material, not a reason to encode GUIDs as strings.

varbinary stores bytes: hashes, compressed payloads, encrypted blobs, or small binary artifacts. xml stores XML with XML-aware validation/query/index capabilities. The rule is not “use special types for everything”; it is “do not erase semantics into strings unless the system deliberately wants opaque text.”

sql · typed identifiers, binary and XML
USE ServiceHubLab;GOSELECT    NEWID() AS sample_guid,    HASHBYTES('SHA2_256', CONVERT(varbinary(max), N'ServiceHub')) AS sample_hash,    CAST(N'<workOrder id="1001"><status>assigned</status></workOrder>' AS xml) AS sample_xml;GO

5. JSON in SQL Server 2025: stable functions, preview native type

SQL Server has supported JSON functions over string storage since SQL Server 2016. A robust stable pattern is nvarchar(max) plus validation such as CHECK (ISJSON(payload)=1), then JSON_VALUE/OPENJSON for access. SQL Server 2025 adds a native binary json type, but Microsoft currently labels that type preview for SQL Server 2025 itself. A course that requires free reproducible labs should not silently require preview features.

sql · stable JSON storage pattern used by the mandatory lab
USE ServiceHubLab;GOIF SCHEMA_ID(N'lab03') IS NULL EXEC(N'CREATE SCHEMA lab03 AUTHORIZATION dbo;');GODROP TABLE IF EXISTS lab03.PayloadProbe;GOCREATE TABLE lab03.PayloadProbe(    payload_id int IDENTITY PRIMARY KEY,    payload nvarchar(max) NOT NULL,    CONSTRAINT CK_PayloadProbe_IsJson CHECK (ISJSON(payload) = 1));GOINSERT lab03.PayloadProbe(payload)VALUES (N'{"workOrderId":1001,"source":"mobile","ok":true}');GOSELECT    payload_id,    JSON_VALUE(payload, '$.workOrderId') AS work_order_id_text,    JSON_VALUE(payload, '$.source') AS source_nameFROM lab03.PayloadProbe;GO

The scalar return from classic JSON_VALUE without a SQL Server 2025 RETURNING clause is text. Convert deliberately when the downstream value is numeric/date typed. If you later adopt the native json type, re-check its preview/GA status, driver support, conversion restrictions, indexing options, and deployment prerequisites first.

6. Deliberately wrong approach: “store everything as nvarchar(max)”

A hurried integration stores amount, timestamp, work-order ID, boolean, GUID, and JSON payload all as nvarchar(max). The schema now accepts 'twelve dollars', malformed timestamps, arbitrary GUID text, and inconsistent boolean spellings. Every query must parse values repeatedly, parameter types are easy to mismatch, and useful constraints/indexes become harder to express.

Repair

Keep opaque payloads as text only when they are genuinely opaque. Promote business fields into types that enforce their domain: decimal for exact decimal money, datetime2/datetimeoffset for temporal values, bit for booleans, uniqueidentifier for GUIDs, and explicit length-bounded string columns for bounded identifiers. JSON can remain validated text on the stable lab path.

7. Hands-on lab: build a type-contract probe

sql · create and inspect a typed integration row
USE ServiceHubLab;GODROP TABLE IF EXISTS lab03.TypeContract;GOCREATE TABLE lab03.TypeContract(    event_id uniqueidentifier NOT NULL CONSTRAINT DF_TypeContract_Id DEFAULT NEWID(),    work_order_id bigint NOT NULL,    billable_amount decimal(19,4) NULL,    measurement float NULL,    event_time_utc datetime2(3) NOT NULL,    source_time datetimeoffset(3) NULL,    is_verified bit NOT NULL,    note nvarchar(200) NULL,    payload nvarchar(max) NULL,    payload_hash varbinary(32) NULL,    metadata xml NULL,    CONSTRAINT PK_TypeContract PRIMARY KEY (event_id),    CONSTRAINT CK_TypeContract_Payload CHECK (payload IS NULL OR ISJSON(payload)=1));GOINSERT lab03.TypeContract(work_order_id, billable_amount, measurement, event_time_utc, source_time, is_verified, note, payload, payload_hash, metadata)VALUES(1001, 125.7500, 0.125, '2026-08-22T09:30:00.000', '2026-08-22T13:30:00.000+04:00', 1, N'Unicode note: café', N'{"source":"mobile","retry":false}', HASHBYTES('SHA2_256', CONVERT(varbinary(max), N'1001|mobile')), CAST(N'<source name="mobile" />' AS xml));GOSELECT * FROM lab03.TypeContract;GOSELECT    c.name, TYPE_NAME(c.user_type_id) AS type_name,    c.max_length, c.precision, c.scale, c.is_nullable, c.collation_nameFROM sys.columns AS cWHERE c.object_id = OBJECT_ID(N'lab03.TypeContract')ORDER BY c.column_id;GO

Verification checklist

  • Money-like data uses an exact decimal type.
  • The Unicode note round-trips correctly.
  • You can explain what information datetimeoffset preserves beyond datetime2.
  • Invalid JSON text is rejected by the check constraint on the stable path.
  • You did not enable a preview feature merely to finish the mandatory lab.
sql · cleanup
USE ServiceHubLab;GODROP TABLE IF EXISTS lab03.TypeContract;DROP TABLE IF EXISTS lab03.PayloadProbe;GO

8. Production judgment and next bridge

Type design is an application contract. Record precision/scale/length, Unicode needs, collation, time-zone policy, identifier semantics, binary size, nullability, and feature status in schema/API documentation. “It fits today” is not enough; types influence constraints, parameter metadata, index keys, sorting, hashing, serialization, and future migrations.

Lesson 3 shows why type choices also become performance choices. When operands differ, SQL Server follows data-type precedence and may introduce implicit conversions. A conversion on the wrong side of an indexed predicate can change an access path from a seek into a scan.

Check your understanding

  1. Why is float usually a poor default for exact monetary amounts?
  2. What does the N prefix on a string literal communicate?
  3. What information does datetimeoffset preserve that datetime2 does not?
  4. Why is uniqueidentifier preferable to varchar(36) when the value is semantically a GUID?
  5. Why does this lesson keep native SQL Server 2025 json out of the mandatory lab?
Review the answers

float is approximate binary floating point; exact financial rules generally need fixed decimal precision/scale and defined rounding behavior.

It marks the literal as Unicode (nvarchar-family) text rather than a non-Unicode varchar literal.

It stores a numeric UTC offset with the local date/time value. It does not store a named time zone/rule set.

The type validates/stores the 16-byte GUID value directly and avoids treating identifier semantics as arbitrary text, though its index placement still needs separate design.

Microsoft currently documents the native json type as preview for SQL Server 2025. Mandatory course labs avoid requiring preview features; stable nvarchar + JSON functions remain reproducible.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.