Skip to main content

Variables and Data Types: Matching SQL Columns – C# | Lesson 31

Published / updated

Introduction​

A program works with values: a student's name, a fee, a date of birth. While the program runs, it keeps each value in a variable: a named box in memory. Every variable has a type, which decides what the box can hold.

You already chose types once, for the SchoolDb columns in Lesson 8. In this lesson, you choose the matching C# types, so data moves between the database and C# without surprises.

By the end of this lesson, you will be able to:

  • Declare variables with the C# type that matches each SQL Server column type.
  • Explain why money must be decimal, never double.
  • Use var and const correctly.
  • Convert between types with casts, Parse, and TryParse.
  • Read a number typed by the user.

Why this matters​

In Module 9, C# reads rows from SchoolDb into variables and objects. If the C# type does not match the column, you get errors, or worse, quietly wrong values.

The classic example is money. A fee system that adds payments with double can show a balance of 0.0000000001 instead of zero. The invoice then never becomes "Paid", and a parent receives a reminder for a fee they cleared.

The concept​

Example: declaring a variable​

Example
decimal invoiceTotal = 45500.00m;
PartMeaning
decimalThe type: what kind of value fits
invoiceTotalThe name, in camelCase: first word lowercase, later words capitalized
=Puts the value into the variable
45500.00mThe value; the m suffix marks it as a decimal
;Ends the statement

Think of the school's store room. Every shelf has a label, such as "chalk boxes" or "exam papers", and you cannot put a football on the exam-paper shelf.

A type is that label, and the compiler checks it before the program even runs.

Example: SQL Server types and their C# partners​

SchoolDb columnSQL Server typeC# typeExample
Students.IdINTint2
AttendanceRecords.IdBIGINTlong600036
Enrollments.RollNumberSMALLINTshort2
Sections.CapacityTINYINTbyte40
FeeInvoices.TotalAmountDECIMAL(10,2)decimal45500.00m
Students.FirstNameNVARCHAR(50)string"Diya"
Students.GenderCHAR(1)char or string'F'
Students.DateOfBirthDATEDateOnlynew DateOnly(2011, 8, 23)
FeePayments.PaidOnDATETIME2DateTimeDateTime.UtcNow (stored in UTC, Lesson 8)
AcademicYears.IsCurrentBITbooltrue

Text goes in double quotes, a single character in single quotes. A C# string is always Unicode, so it holds both VARCHAR and NVARCHAR values.

Example: value types and reference types​

Numbers, bool, char, DateOnly, and DateTime are value types: the variable holds the value itself. string is a reference type: the variable holds a reference to text stored elsewhere in memory.

For now, the difference shows in one place: a value type always has a value, such as 0, while a reference type can be "nothing", called null. Lessons 35 and 37 go deeper.

Apply it to the SMS​

The program stores Diya's record and fee invoice in variables, then demonstrates money, var, conversions, overflow, and keyboard input:

Show src/SchoolApp.Console/Program.cs
src/SchoolApp.Console/Program.cs
// Lesson 31: variables and data types, using Diya's record and fee invoice from SchoolDb.

// 1. One variable per column, with the C# type that matches the SQL Server type.
int studentId = 2; // INT
string admissionNumber = "ADM-2017-0002"; // VARCHAR(15)
string firstName = "Diya"; // NVARCHAR(50)
char gender = 'F'; // CHAR(1)
DateOnly dateOfBirth = new DateOnly(2011, 8, 23); // DATE
bool isCurrentYear = true; // BIT
short rollNumber = 2; // SMALLINT
byte sectionCapacity = 40; // TINYINT
long lastAttendanceRecordId = 600036; // BIGINT
decimal invoiceTotal = 45500.00m; // DECIMAL(10,2); m marks a decimal value

Console.WriteLine($"{firstName} ({admissionNumber}), student {studentId}, gender {gender}, roll {rollNumber}");
Console.WriteLine($"Born {dateOfBirth:d MMMM yyyy}; current year: {isCurrentYear}");
Console.WriteLine($"Section capacity {sectionCapacity}; last attendance record {lastAttendanceRecordId}");

// 2. Money uses decimal: the fee balance.
decimal discount = 0.00m;
decimal paid = 20000.00m;
decimal balance = invoiceTotal - discount - paid;
Console.WriteLine($"Invoice {invoiceTotal}, paid {paid}, balance {balance}");

// 3. Why not double for money?
double doubleSum = 0.1 + 0.2;
decimal decimalSum = 0.1m + 0.2m;
Console.WriteLine($"double: {doubleSum} decimal: {decimalSum}");
Console.WriteLine($"Equal to 0.3? double: {doubleSum == 0.3} decimal: {decimalSum == 0.3m}");

// 4. var lets the compiler choose the type; const can never change.
var tuition = 42000.00m;
const int PassMark = 33;
Console.WriteLine($"tuition is a {tuition.GetType().Name}; pass mark is {PassMark}");

// 5. Conversions.
long widened = studentId; // implicit: every int fits in a long
int cutDown = (int)92.75m; // explicit cast: the fraction is cut off
int parsed = int.Parse("45500"); // text to number
bool isValid = int.TryParse("45500 only", out int notParsed);
Console.WriteLine($"widened {widened}, cut down {cutDown}, parsed {parsed}");
Console.WriteLine($"TryParse(\"45500 only\") -> {isValid}, value {notParsed}");

// 6. Overflow: a byte holds 0 to 255, and wraps around silently.
byte seats = 255;
seats++;
Console.WriteLine($"255 + 1 in a byte: {seats}");

// 7. Input from the keyboard is always text.
Console.Write("Enter a payment amount: ");
string? input = Console.ReadLine(); // the ? is explained in Lesson 35
bool isAmount = decimal.TryParse(input, out decimal payment);
Console.WriteLine($"Valid amount: {isAmount}; balance after payment: {balance - payment}");

Run it, and type 5000 when it asks for an amount:

Output
Diya (ADM-2017-0002), student 2, gender F, roll 2
Born 23 August 2011; current year: True
Section capacity 40; last attendance record 600036
Invoice 45500.00, paid 20000.00, balance 25500.00
double: 0.30000000000000004 decimal: 0.3
Equal to 0.3? double: False decimal: True
tuition is a Decimal; pass mark is 33
widened 2, cut down 92, parsed 45500
TryParse("45500 only") -> False, value 0
255 + 1 in a byte: 0
Enter a payment amount: 5000
Valid amount: True; balance after payment: 20500.00

These values match Diya's invoice in SchoolDb: 45,500 in total, 20,000 already paid.

Example: money is decimal​

double stores numbers in binary, base 2, the way the processor likes. Most decimal fractions, such as 0.1, cannot be stored exactly in binary, so tiny errors appear.

decimal stores numbers in base 10, like a person writing on paper. 0.1 is exactly 0.1. The program shows the result: in double, 0.1 + 0.2 is not equal to 0.3.

TypeUse forExample in the SMS
decimalMoney, and anything that must be exactFees, payments, fines
doubleMeasurements where tiny errors do not matterAverage attendance in a chart
intWhole numbersCounts, Ids, days late

DECIMAL columns in SQL Server always map to decimal in C#.

Example: var and const​

var tuition = 42000.00m; lets the compiler work out the type from the value, here decimal. The type is still fixed; var only saves typing. Use it when the type is obvious from the right-hand side.

const int PassMark = 33; creates a constant: a value that can never change while the program runs. Constants are named in PascalCase. Rules like "33 is the pass mark" belong in constants, so the number is written once.

Example: converting between types​

KindExampleResult
Implicit: always safe, no code neededlong widened = studentId;2
Explicit cast: may lose data, so you must ask(int)92.75m92; the fraction is cut, not rounded
Parse: text to numberint.Parse("45500")45500
TryParse: text to number, safelyint.TryParse("45500 only", out int n)false, and n is 0

int.Parse stops the program with an error when the text is not a number (Lesson 36). TryParse returns true or false instead, and puts the number in the out variable when it works. Use TryParse for anything a person types.

Example: overflow​

A byte holds 0 to 255, like the TINYINT capacity column. Adding 1 to 255 does not fail. It wraps around to 0, silently.

This is why Ids that can grow big, like attendance record Ids, are long. Pick a type large enough for the biggest value the data can ever reach.

Example: keyboard input is text​

Console.ReadLine() always returns text, even when the user types digits. You must convert it with TryParse. When the user typed five thousand, the last line printed:

Output
Valid amount: False; balance after payment: 25500.00

TryParse failed, payment stayed 0, and the balance did not change. Lesson 32 shows how to react with an error message instead.

Common mistakes​

  1. Forgetting the m on decimal values. decimal tuition = 42000.50; fails with error CS0664: Literal of type double cannot be implicitly converted to type 'decimal'. Fix: write 42000.50m.
  2. Putting a decimal into an int. int mark = 92.75m; fails with error CS0266: Cannot implicitly convert type 'decimal' to 'int'. Fix: cast if you mean it, or use decimal.
  3. Using double for money. Balances end up as 0.0000000001. Fix: always decimal.
  4. int.Parse on user input. One wrong key stops the program. Fix: TryParse.
  5. A type that is too small. A byte wraps from 255 to 0. Fix: choose int or long for counts and Ids that can grow.

In real projects​

From 16 years of .NET projects

On one fee system, a few invoices stayed "Partially paid" forever, although families had paid in full. The code summed payments in double, and the balance came out as 0.00000000003 instead of 0.

The fix was one word per variable, double to decimal, plus a script to correct the stuck invoices.

Since then, I search every code review for double near words like fee, amount, price, or balance. It takes five seconds and has caught the same bug several times.

Best practices​

  • Match each C# type to its SQL Server column type, using the table in this lesson.
  • Use decimal for money, always.
  • Use TryParse for text from people, files, or the network.
  • Give variables clear camelCase names, and constants PascalCase names.
  • Use var only when the type is obvious from the value.

Recap​

  • A variable is a named box; its type decides what fits.
  • int, long, decimal, string, DateOnly, and bool match the SchoolDb column types.
  • decimal is exact for money; double is not.
  • var lets the compiler pick the type; const values never change.
  • Casts may lose data; TryParse converts text without crashing.
  • Small types such as byte silently wrap around when they overflow.

Homework​

Library module task: In a single-file app, store one late book issue in variables of the right types: the issue Id, the book title, the issue date, and the return date.

Use constants for the loan period (14 days) and the fine (2 per day). Calculate the due date, the days late, and the fine.

A correct result looks like: Malgudi Days, issued 10 July 2026 and returned 28 July, was due on 24 July, is 4 days late, and has a fine of 8.

Show the solution
library-assignment/031-library-fine.cs
// Library homework, Lesson 31: one late book issue, stored in variables of the right types.
// Run with: dotnet run library-assignment/031-library-fine.cs
int bookIssueId = 7;
string title = "Malgudi Days";
DateOnly issuedOn = new DateOnly(2026, 7, 10);
const int LoanDays = 14;
const decimal FinePerDay = 2m;

DateOnly dueOn = issuedOn.AddDays(LoanDays);
DateOnly returnedOn = new DateOnly(2026, 7, 28);
int daysLate = returnedOn.DayNumber - dueOn.DayNumber;
decimal fine = daysLate * FinePerDay;

Console.WriteLine($"Issue {bookIssueId}: {title}");
Console.WriteLine($"Issued {issuedOn:d MMM yyyy}, due {dueOn:d MMM yyyy}, returned {returnedOn:d MMM yyyy}");
Console.WriteLine($"Days late: {daysLate}; fine: {fine}");

DayNumber counts days from a fixed starting date, so subtracting two of them gives the days between the dates. The fine is decimal, because it is money.

Quiz​

  1. Which C# type matches a DECIMAL(10,2) fee column?

    1. double
    2. float
    3. decimal
    4. int
    Show answer

    Answer: C. decimal decimal stores base-10 numbers exactly, which money needs.

  2. What does (int)92.75m give?

    1. 93
    2. 92
    3. 92.75
    4. An error
    Show answer

    Answer: B. 92 A cast to int cuts the fraction off; it does not round.

  3. What does int.TryParse with the text '45500 only' return?

    1. true, and n is 45500
    2. false, and n is 0
    3. It stops the program
    4. true, and n is 0
    Show answer

    Answer: B. false, and n is 0 The text is not a whole number, so it returns false and sets n to 0.

  4. A byte variable holds 255. What happens after adding 1?

    1. It becomes 256
    2. It becomes 0
    3. The program always stops
    4. It becomes -1
    Show answer

    Answer: B. It becomes 0 By default, the value wraps around silently.

  5. Which C# type matches a SQL Server DATE column?

    1. string
    2. DateTime only
    3. DateOnly
    4. int
    Show answer

    Answer: C. DateOnly DateOnly holds a date without a time, like a DATE column.

Interview questions​

Questions are grouped by experience level. Click a question to see a model answer. All questions: C# interview questions.

Fresher (0-2 yrs)​

F Which C# types match the SQL Server types INT, BIGINT, DECIMAL, NVARCHAR, DATE, and BIT? concept

int, long, decimal, string, DateOnly, and bool. Matching the types avoids conversion errors and silently wrong values when data moves between the database and C#.

See the example · Permalink

F What does the var keyword do? concept

It lets the compiler work out the variable's type from the value on the right, for example var tuition = 42000.00m; is a decimal. The type is still fixed at compile time; var only saves typing, so use it when the type is obvious.

See the example · Permalink

F What is the difference between Parse and TryParse? concept

int.Parse converts text to a number and throws an exception if it cannot. int.TryParse returns true or false and gives the number through an out parameter, so bad input never crashes the program. Use TryParse for anything a person types.

See the example · Permalink

Mid (3-5 yrs)​

M Why should money use decimal instead of double? concept

double stores numbers in binary, so most decimal fractions are approximations: 0.1 + 0.2 gives 0.30000000000000004. decimal stores base-10 numbers exactly, so sums of fees and payments are exact. double is for measurements where tiny errors do not matter.

See the example · Permalink

M What is the difference between an implicit conversion and an explicit cast? concept

An implicit conversion is always safe and needs no code, such as int to long. An explicit cast may lose data, so you must write it, such as (int)92.75m, which gives 92 because the fraction is cut off, not rounded.

See the example · Permalink

M What is the difference between value types and reference types? concept

A value type variable holds the value itself (numbers, bool, char, DateOnly, structs). A reference type variable holds a reference to an object stored elsewhere (string, classes, arrays), and can be null. Copying a value type copies the value; copying a reference copies only the reference.

See the example · Permalink

Senior (6-9 yrs)​

S Several invoices stay 'Partially paid' although families paid in full. What would you check first? scenario

The types used for money. If payments are summed in double, the balance can be a tiny non-zero number such as 0.00000000003, so the status check never sees zero. Change money variables to decimal, then correct the stuck invoices with a one-off script.

How to answer: Connect the symptom to floating-point rounding, show how you would prove it (print the exact balance), fix the root cause, repair existing data, and prevent it with code review rules.

See the example · Permalink

S What happens when an integer overflows in C#, and how do you guard against it? concept

By default it wraps around silently: a byte at 255 plus 1 becomes 0. Choose types large enough for the data, such as long for Ids that can grow, and use a checked block or the project's overflow-checking setting where a wrong value would be dangerous, so overflow throws an exception instead.

How to answer: Show the default behavior, the risk (wrong totals or Ids), and two defenses: correct type sizing and checked arithmetic. Mention matching SQL types such as TINYINT and BIGINT.

See the example · Permalink

Architect / Lead (10+ yrs)​

A How would you keep C# types consistent with the database across a large team? design

Document one type mapping (for example DECIMAL to decimal, DATE to DateOnly), generate or scaffold entity classes from the database where possible, and check mismatches in code review and integration tests that round-trip real data.

How to answer: Combine a written standard, tooling (scaffolding, analyzers), and tests. Mention that the database stays the source of truth for column types, as the SMS reference file does.

See the example · Permalink

Resources​

  • Code for this lesson: git tag lesson-31-data-types (code repository coming soon)
  • Files: src/SchoolApp.Console/Program.cs, library-assignment/031-library-fine.cs
  • Video: coming soon

What's next​

Your program can store values, but it treats every student the same. Next, it makes decisions, such as which grade a mark earns, and repeats work for every day of the week. Lesson 32: Conditions and loops

Want live sessions, code reviews, and interview preparation? Join the next batch at Sahasra Technologies.