Variables and Data Types: Matching SQL Columns – C# | Lesson 31
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, neverdouble. - Use
varandconstcorrectly. - Convert between types with casts,
Parse, andTryParse. - 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
decimal invoiceTotal = 45500.00m;
| Part | Meaning |
|---|---|
decimal | The type: what kind of value fits |
invoiceTotal | The name, in camelCase: first word lowercase, later words capitalized |
= | Puts the value into the variable |
45500.00m | The 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 column | SQL Server type | C# type | Example |
|---|---|---|---|
Students.Id | INT | int | 2 |
AttendanceRecords.Id | BIGINT | long | 600036 |
Enrollments.RollNumber | SMALLINT | short | 2 |
Sections.Capacity | TINYINT | byte | 40 |
FeeInvoices.TotalAmount | DECIMAL(10,2) | decimal | 45500.00m |
Students.FirstName | NVARCHAR(50) | string | "Diya" |
Students.Gender | CHAR(1) | char or string | 'F' |
Students.DateOfBirth | DATE | DateOnly | new DateOnly(2011, 8, 23) |
FeePayments.PaidOn | DATETIME2 | DateTime | DateTime.UtcNow (stored in UTC, Lesson 8) |
AcademicYears.IsCurrent | BIT | bool | true |
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
// 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:
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.
| Type | Use for | Example in the SMS |
|---|---|---|
decimal | Money, and anything that must be exact | Fees, payments, fines |
double | Measurements where tiny errors do not matter | Average attendance in a chart |
int | Whole numbers | Counts, 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
| Kind | Example | Result |
|---|---|---|
| Implicit: always safe, no code needed | long widened = studentId; | 2 |
| Explicit cast: may lose data, so you must ask | (int)92.75m | 92; the fraction is cut, not rounded |
| Parse: text to number | int.Parse("45500") | 45500 |
| TryParse: text to number, safely | int.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:
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
- Forgetting the
mon decimal values.decimal tuition = 42000.50;fails witherror CS0664: Literal of type double cannot be implicitly converted to type 'decimal'. Fix: write42000.50m. - Putting a decimal into an int.
int mark = 92.75m;fails witherror CS0266: Cannot implicitly convert type 'decimal' to 'int'. Fix: cast if you mean it, or usedecimal. - Using
doublefor money. Balances end up as 0.0000000001. Fix: alwaysdecimal. int.Parseon user input. One wrong key stops the program. Fix:TryParse.- A type that is too small. A
bytewraps from 255 to 0. Fix: chooseintorlongfor counts and Ids that can grow.
In real 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
decimalfor money, always. - Use
TryParsefor text from people, files, or the network. - Give variables clear camelCase names, and constants PascalCase names.
- Use
varonly 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, andboolmatch the SchoolDb column types.decimalis exact for money;doubleis not.varlets the compiler pick the type;constvalues never change.- Casts may lose data;
TryParseconverts text without crashing. - Small types such as
bytesilently 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 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
Which C# type matches a DECIMAL(10,2) fee column?
Show answer
Answer: C. decimal decimal stores base-10 numbers exactly, which money needs.
What does (int)92.75m give?
Show answer
Answer: B. 92 A cast to int cuts the fraction off; it does not round.
What does int.TryParse with the text '45500 only' return?
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.
A byte variable holds 255. What happens after adding 1?
Show answer
Answer: B. It becomes 0 By default, the value wraps around silently.
Which C# type matches a SQL Server DATE column?
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#.
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.
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.
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.
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.
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.
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.
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.
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.
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.