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

Implicit vs Explicit Conversion, Data-Type Precedence, SARGability, and Conversion Pitfalls

Connect data-type precedence and implicit conversion to observable plans, logical reads, and SARGable predicates; repair parameter/column mismatches without cargo-cult indexing.

Intermediate105–130 minutesImplicit conversion + plan evidence labSQL Server 2025 · compatibility 170 baselineDeveloper/Express · actual-plan observation recommendedLast reviewed: August 2026

Learning outcomes

A ServiceHub query looks logically simple: find one work order by a code. After an application update, CPU rises and the plan begins scanning an indexed table. No index was dropped. The only change is parameter metadata: the application now sends Unicode text to a varchar key. This lesson connects SQL Server’s type-precedence rules to correctness and access paths, while avoiding the equally dangerous slogan that “every implicit conversion is slow.”

01

Explain SQL Server data-type precedence and predict which operand is converted when types differ.

02

Distinguish harmless value-side conversions from conversions that can affect an indexed column and access path.

03

Recognize CONVERT_IMPLICIT/plan-affecting conversion evidence in execution plans rather than guessing from query text alone.

04

Explain SARGability as the optimizer’s ability to use a search argument for an access path, not as a universal ban on functions.

05

Rewrite common date/string conversion predicates so the indexed column can remain directly searchable.

Measurement rule

Plans and I/O depend on data distribution, collation, statistics, cache state, and optimizer version. The lesson never invents a fixed “X times faster” result. Run the paired queries on your lab, capture the actual/estimated plan and STATISTICS IO, and describe what your instance actually did.

1. Precedence decides conversion direction

When SQL Server combines expressions of different types, the lower-precedence type is converted toward the higher-precedence type when an implicit conversion exists. The current precedence list places int above character types and nvarchar above varchar. This means int_column = '42' can convert the string literal/parameter to integer, while varchar_column = N'ABC' can require conversion toward Unicode.

sql · observe result types and conversion behavior
SELECT    SQL_VARIANT_PROPERTY(1 + CAST(2 AS bigint), 'BaseType') AS integer_result_type,    SQL_VARIANT_PROPERTY(CAST(1 AS decimal(10,2)) + CAST(2 AS int), 'BaseType') AS decimal_result_type;GOSELECT    CASE WHEN 42 = '42' THEN 'equal' ELSE 'different' END AS numeric_text_comparison;GO-- This raises a conversion error because int has higher precedence than varchar.-- SELECT 1 WHERE 42 = 'not-an-integer';GO

Implicit conversion is not automatically a problem. Converting a constant/parameter once can be cheap and preserve an index seek. The important question is what expression gets converted and how that affects the predicate the optimizer can use.

2. Build a repeatable indexed lookup probe

The existing ServiceHub dataset is intentionally small. To make plan differences visible without polluting it, create a disposable lookup table with 5,000 rows and a clustered primary key on varchar(16). This is still a tiny lab, so treat the observed plan/reads as local evidence, not a production benchmark.

sql · create a 5,000-row indexed varchar lookup
USE ServiceHubLab;GOIF SCHEMA_ID(N'lab03') IS NULL EXEC(N'CREATE SCHEMA lab03 AUTHORIZATION dbo;');GODROP TABLE IF EXISTS lab03.CodeLookup;GOCREATE TABLE lab03.CodeLookup(    code varchar(16) NOT NULL CONSTRAINT PK_CodeLookup PRIMARY KEY,    payload char(100) NOT NULL);GOWITH n AS(    SELECT TOP (5000)           ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn    FROM sys.all_objects AS a    CROSS JOIN sys.all_objects AS b)INSERT lab03.CodeLookup(code, payload)SELECT    CONCAT('C', RIGHT(CONCAT('000000', rn), 6)),    REPLICATE('x', 100)FROM n;GOUPDATE STATISTICS lab03.CodeLookup WITH FULLSCAN;GOSELECT COUNT(*) AS row_count FROM lab03.CodeLookup;GO

3. Compare type-aligned and type-mismatched predicates with evidence

First query the key with a varchar parameter whose type and length match the column. Then repeat with an nvarchar parameter. In many common collations/plans, the Unicode mismatch can introduce a CONVERT_IMPLICIT around the varchar column and can affect seekability. Exact plan behavior is not guaranteed across every collation and optimizer build, which is why the exercise requires observation.

sql · paired parameter tests—capture actual plans and STATISTICS IO
USE ServiceHubLab;GOSET STATISTICS IO ON;GODECLARE @code_v varchar(16) = 'C004321';SELECT code, payloadFROM lab03.CodeLookupWHERE code = @code_v;GODECLARE @code_n nvarchar(16) = N'C004321';SELECT code, payloadFROM lab03.CodeLookupWHERE code = @code_n;GOSET STATISTICS IO OFF;GO

In SSMS enable the Actual Execution Plan, or inspect plans with another supported tool. Look for the access operator (seek versus scan), the predicate, warnings, and CONVERT_IMPLICIT. Compare logical reads from your session. The correct conclusion is “this parameter metadata changed the observed access path on this lab,” not “nvarchar is bad.” If the column is semantically Unicode, redesign the column; if it is intentionally varchar, bind a matching parameter.

4. SARGability is about search arguments, not moral judgments about functions

A predicate is commonly called SARGable when the optimizer can use it as a search argument for an index access path. Wrapping the indexed column in a scalar expression often hides the original ordering from a direct seek. But “functions are slow” is an inaccurate rule: functions on constants/parameters can be harmless, and the optimizer can simplify some expressions.

sql · non-SARGable column conversion versus range predicate
USE ServiceHubLab;GO-- The column is transformed for every candidate row.SELECT work_order_id, opened_atFROM ops.WorkOrderWHERE CONVERT(date, opened_at) = '2026-08-10';GO-- Preserve the column and express the same day as a half-open range.DECLARE @d date = '2026-08-10';SELECT work_order_id, opened_atFROM ops.WorkOrderWHERE opened_at >= @d  AND opened_at < DATEADD(day, 1, @d);GO

The range form is robust for timestamps because it does not depend on “end of day” fractional precision. If an index exists on opened_at, it gives the optimizer a directly ordered predicate. Again, verify the plan when performance matters.

5. Explicit conversion makes intent visible but does not guarantee performance

CAST/CONVERT are valuable when you need a specific target type, format style, precision, or overflow boundary. Explicit conversion can also move work to the parameter/value side so the indexed column remains untouched. But writing CONVERT is not a tuning spell: converting the column explicitly can be just as non-SARGable as an implicit column conversion.

sql · convert the input once, not the indexed column
USE ServiceHubLab;GODECLARE @raw_code nvarchar(50) = N'C004321';DECLARE @typed_code varchar(16) = CONVERT(varchar(16), @raw_code);SELECT code, payloadFROM lab03.CodeLookupWHERE code = @typed_code;GODECLARE @raw_id nvarchar(30) = N'1001';DECLARE @typed_id bigint = TRY_CONVERT(bigint, @raw_id);SELECT work_order_id, statusFROM ops.WorkOrderWHERE work_order_id = @typed_id;GO

Application code should usually bind the correct type before the SQL is sent, rather than converting every parameter inside T-SQL. The server-side pattern is useful at explicit ingestion boundaries where raw text is intentionally being parsed.

6. Deliberately wrong approach: index every slow-looking conversion

An operator sees CONVERT_IMPLICIT in a plan and immediately adds another index. This may add write/storage cost while leaving the root cause—the parameter/column type mismatch—untouched. Another operator rewrites every function in the query without checking whether it was on a constant or whether the plan changed at all.

Diagnosis

Start with the actual predicate, parameter metadata, column definition, collation, estimates/actual rows, operator shape, warnings, and I/O. Determine whether the conversion is on the column and whether it affects cardinality or access. SQL Server also exposes the plan_affecting_convert Extended Event for focused investigations, but do not run broad tracing casually.

Repair

Align schema and parameter types when they represent the same domain. Rewrite transformed indexed-column predicates as ranges when semantics allow. Re-measure before adding an index or hint. The minimal change that removes the causal mismatch is preferable to a cargo-cult tuning bundle.

7. Hands-on lab: prove one conversion regression and repair it

Use the lab03.CodeLookup table above. Capture an actual plan and I/O for the matching varchar parameter, the nvarchar parameter, and the repaired typed parameter. Record your collation and engine build because implicit character conversion behavior is collation-sensitive.

sql · evidence manifest
SELECT    SERVERPROPERTY('ProductVersion') AS engine_version,    DATABASEPROPERTYEX(N'ServiceHubLab','Collation') AS database_collation;GOUSE ServiceHubLab;GOSELECT    c.name, TYPE_NAME(c.user_type_id) AS type_name,    c.max_length, c.collation_nameFROM sys.columns AS cWHERE c.object_id = OBJECT_ID(N'lab03.CodeLookup')ORDER BY c.column_id;GO

Verification checklist

  • You capture parameter types, not only visible string values.
  • You compare actual/estimated plan operators and logical reads locally.
  • You can point to which side of the predicate is converted.
  • You can explain why CONVERT(date, opened_at) and a date range are not equivalent access-path expressions.
  • You do not claim a universal speedup from one 5,000-row lab.
sql · cleanup the lookup probe
USE ServiceHubLab;GODROP TABLE IF EXISTS lab03.CodeLookup;GO

8. Production judgment and next bridge

Type compatibility is part of query design and API design. Review column types together with stored-procedure parameter definitions and driver parameter metadata. In incident analysis, capture plan XML/Query Store evidence before changing schema. Prefer domain alignment over compensating indexes.

Lesson 4 turns conversion from a performance hazard into a controlled data-quality tool: CASE, COALESCE, NULLIF, and TRY_CONVERT can express resilient parsing, but their result types and evaluation rules still need deliberate handling.

Check your understanding

  1. When two expressions have different SQL Server types, what determines the implicit conversion direction?
  2. Why can varchar_column = nvarchar_parameter be more concerning than int_column = varchar_parameter?
  3. What does SARGability describe?
  4. Why is CONVERT(date, timestamp_column) often less index-friendly than a half-open range?
  5. Why should you measure a conversion before adding an index?
Review the answers

SQL Server data-type precedence: the lower-precedence expression is converted toward the higher-precedence type when an implicit conversion is supported.

With int above character types, SQL Server can often convert the parameter value to int and preserve the integer column. With nvarchar above varchar, the varchar column may need conversion, which can affect an index search depending on plan/collation.

It describes whether a predicate can act as a search argument that lets the optimizer exploit an ordered access path; it is not a blanket statement that every function is slow.

The conversion transforms the indexed column value. A range leaves the column directly comparable to lower/upper bounds and handles timestamp precision cleanly.

An index has permanent write/storage/maintenance cost. The conversion may be harmless, on the value side, or not causal. Plans and I/O establish whether the mismatch is actually affecting the workload.

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.