Lab 03: Single-Row Functions¶
Objectives:¶
- Use character (string) functions to transform and extract text data.
- Use number functions to round, truncate, and manipulate numeric values.
- Use date functions to compute intervals and shift dates.
- Use conversion functions to change a value's data type.
- Use general functions (
CASE,ISNULL/COALESCE) to handle conditional logic and NULL values.
Activity Outcomes:¶
- Reformat and clean up text columns using character functions.
- Perform numeric calculations using number functions.
- Compute durations and shifted dates using date functions.
- Convert values between data types explicitly.
- Build conditional, NULL-safe expressions in a SELECT list.
Tools / Software Required:
- SQL Server 2019 (Developer or Express edition)
- SQL Server Management Studio (SSMS) 18 or later
- The HR sample database, restored and attached to the local SQL Server instance
Instructor Note: As pre-lab activity, read the chapter on "Single-Row Functions" (character, number, date, and conversion functions) from James R. Groff and Paul N. Weinberg's "SQL: The Complete Reference".
1) Useful Concepts¶
Character functions:
| Function | Description |
|---|---|
UPPER(str) / LOWER(str) |
Converts a string to upper-case / lower-case |
LTRIM(str) / RTRIM(str) / TRIM(str) |
Removes leading / trailing / both whitespace |
SUBSTRING(str, start, length) |
Extracts a substring starting at position start for length characters |
LEN(str) |
Returns the number of characters in a string |
REPLACE(str, old, new) |
Replaces every occurrence of old with new |
CONCAT(str1, str2, ...) |
Concatenates strings, treating NULL as an empty string |
Number functions:
| Function | Description |
|---|---|
ROUND(num, decimals) |
Rounds a number to the given number of decimal places |
CEILING(num) |
Rounds up to the nearest integer |
FLOOR(num) |
Rounds down to the nearest integer (T-SQL equivalent of TRUNC for whole numbers) |
ABS(num) |
Returns the absolute (non-negative) value |
num % divisor |
Modulo operator — the remainder after integer division (T-SQL has no MOD() function) |
Date functions:
| Function | Description |
|---|---|
GETDATE() |
Returns the current server date and time |
DATEDIFF(unit, start, end) |
Returns the difference between two dates, in the given unit (e.g. YEAR, MONTH, DAY) |
DATEADD(unit, number, date) |
Adds (or subtracts, with a negative number) an interval to a date |
FORMAT(date, 'format') |
Formats a date (or number) as a string using a .NET-style format string, e.g. 'yyyy-MM-dd' |
Conversion functions:
| Function | Description |
|---|---|
CAST(expr AS type) |
Converts expr to the given data type (ANSI-standard syntax) |
CONVERT(type, expr [, style]) |
Converts expr to the given data type; the optional style controls date/number formatting (T-SQL specific) |
General functions:
| Function | Description |
|---|---|
CASE WHEN cond1 THEN val1 WHEN cond2 THEN val2 ELSE val3 END |
Conditional expression, evaluated top to bottom |
ISNULL(expr, replacement) |
Returns replacement if expr is NULL, otherwise expr (T-SQL specific, 2 arguments only) |
COALESCE(expr1, expr2, ...) |
Returns the first non-NULL expression in the list (ANSI-standard, any number of arguments) |
2) Solved Lab Activities¶
| Sr. No. | Allocated Time | Level of Complexity | CLO Mapping |
|---|---|---|---|
| Activity 1 | 10 Minutes | Low | CLO-1 |
| Activity 2 | 15 Minutes | Medium | CLO-1 |
| Activity 3 | 20 Minutes | Medium | CLO-1 |
| Activity 4 | 20 Minutes | Medium | CLO-1 |
Activity 1: Formatting employee names¶
Display each employee's last_name in all upper-case, first_name in all lower-case, and a column showing the length of last_name.
Solution:
SELECT UPPER(last_name) AS "Last Name (Upper)",
LOWER(first_name) AS "First Name (Lower)",
LEN(last_name) AS "Last Name Length"
FROM EMPLOYEES;
Output / Expected behaviour:
| Last Name (Upper) | First Name (Lower) | Last Name Length |
|---|---|---|
| KING | steven | 4 |
| KOCHHAR | neena | 7 |
| HUNOLD | alexander | 6 |
| ERNST | bruce | 5 |
Activity 2: Computing years of service¶
Display last_name, hire_date, and a computed column "Years of Service" showing how many full years each employee has worked, based on hire_date and the current date.
Solution:
SELECT last_name,
hire_date,
DATEDIFF(YEAR, hire_date, GETDATE()) AS "Years of Service"
FROM EMPLOYEES
ORDER BY "Years of Service" DESC;
Output / Expected behaviour:
| last_name | hire_date | Years of Service |
|---|---|---|
| King | 2013-06-17 | 13 |
| De Haan | 2016-01-13 | 10 |
| Hunold | 2016-01-03 | 10 |
| Ernst | 2017-05-21 | 9 |
(Years of Service values are computed relative to the date the query is run, so your results will differ from the sample above.)
Activity 3: Salary-grade classification with CASE¶
Display last_name, salary, and a computed column "Salary Grade" that classifies each employee as 'A' if salary >= 15000, 'B' if salary >= 8000, 'C' if salary >= 4000, and 'D' otherwise.
Solution:
SELECT last_name,
salary,
CASE
WHEN salary >= 15000 THEN 'A'
WHEN salary >= 8000 THEN 'B'
WHEN salary >= 4000 THEN 'C'
ELSE 'D'
END AS "Salary Grade"
FROM EMPLOYEES
ORDER BY salary DESC;
Output / Expected behaviour:
| last_name | salary | Salary Grade |
|---|---|---|
| King | 24000.00 | A |
| Kochhar | 17000.00 | A |
| Hunold | 9000.00 | B |
| Ernst | 6000.00 | C |
| Lorentz | 4200.00 | C |
Activity 4: Handling NULL commission with ISNULL, rounding, and date formatting¶
Display last_name, salary, commission_pct, a computed column "Effective Commission" that substitutes 0 for employees with no commission, a computed column "Commission Amount" (salary × effective commission, rounded to 2 decimal places), and hire_date formatted as 'dd-MMM-yyyy'.
Solution:
SELECT last_name,
salary,
commission_pct,
ISNULL(commission_pct, 0) AS "Effective Commission",
ROUND(salary * ISNULL(commission_pct, 0), 2) AS "Commission Amount",
FORMAT(hire_date, 'dd-MMM-yyyy') AS "Hire Date"
FROM EMPLOYEES
ORDER BY "Commission Amount" DESC;
Output / Expected behaviour:
| last_name | salary | commission_pct | Effective Commission | Commission Amount | Hire Date |
|---|---|---|---|---|---|
| Russell | 14000.00 | 0.40 | 0.40 | 5600.00 | 01-Oct-2014 |
| Partners | 10000.00 | 0.30 | 0.30 | 3000.00 | 05-Jan-2015 |
| King | 24000.00 | NULL | 0.00 | 0.00 | 17-Jun-2013 |
| Ernst | 6000.00 | NULL | 0.00 | 0.00 | 21-May-2017 |
3) Graded Lab Tasks¶
Note: The instructor may adjust these tasks to the level of difficulty and complexity of the solved activities. Tasks should be evaluated in the same lab session.
Lab Task 1: Masked email directory
Write a query against EMPLOYEES that displays last_name and a computed column "Masked Email" that replaces every occurrence of "@hr.com" in the email column with "@company.internal", using REPLACE.
Lab Task 2: Days until next anniversary
Write a query against EMPLOYEES that displays last_name, hire_date, and a computed column "Days To Next Review" showing the number of days between today's date and the date exactly 6 months (use DATEADD) after each employee's most recent hire-date anniversary.
Lab Task 3: Rounded bonus eligibility report
Write a query against EMPLOYEES that displays last_name, salary, and a computed column "Bonus" using CASE: employees with salary > 10000 get a bonus of salary * 0.05 (rounded to the nearest whole number using ROUND), employees with salary BETWEEN 5000 AND 10000 get salary * 0.03 (rounded), and everyone else gets 0. Use COALESCE instead of ISNULL anywhere commission_pct is involved in a tie-break ORDER BY.