TL;DR
SELECT specific columns with comma-separated list, not SELECT *. WHERE filters rows based on conditions (=, !=, >, <, BETWEEN, IN, LIKE).
NULL is "missing value" — use IS NULL / IS NOT NULL, not = NULL. AND requires all conditions true; OR requires any one. ORDER BY sorts; LIMIT/TOP limits result rows.
Real ICM examples: find active participants in a region, filter results above a threshold, check for NULL quotas that might block calculations.

Picking Specific Columns with SELECT

In Lesson 01, we used SELECT * to grab everything. But SELECT * is lazy and inefficient. In production queries, always pick exactly the columns you need.

SQL — SAP SuccessFactors IM
-- Instead of this:
SELECT * FROM CSC_PARTICIPANT;

-- Do this:
SELECT
  PARTICIPANT_ID,
  NAME,
  EMAIL,
  STATUS
FROM CSC_PARTICIPANT;

Benefits of explicit column selection:

  • Clarity: Readers see exactly what data you want
  • Performance: The database retrieves only what you need, not every column
  • Stability: If someone adds a new column to the table, your query doesn't break

Column order matters only to you, not the database. Put the most important columns first.

The WHERE Clause: Filtering Data

WHERE filters rows. It comes after FROM and before ORDER BY. Here's the pattern:

SQL — Basic WHERE syntax
SELECT PARTICIPANT_ID, NAME, STATUS, COUNTRY
FROM   CSC_PARTICIPANT
WHERE  STATUS = 'ACTIVE';

The WHERE condition STATUS = 'ACTIVE' means: only return rows where the STATUS column equals the text 'ACTIVE'. Rows with STATUS = 'INACTIVE' or STATUS = 'SUSPENDED' don't appear.

Comparison Operators

Operator Meaning Example
= equals (exact match) STATUS = 'ACTIVE'
!= or <> not equal STATUS != 'INACTIVE'
> greater than RESULT_AMOUNT > 5000
< less than RESULT_AMOUNT < 1000
>= greater than or equal RESULT_AMOUNT >= 5000
<= less than or equal RESULT_AMOUNT <= 10000

Practical ICM example: find all results above $5,000 in Q1 2026:

SQL — SAP SuccessFactors IM
SELECT PARTICIPANT_ID, RESULT_AMOUNT, PERIOD_ID
FROM   CSC_RESULTS
WHERE  RESULT_AMOUNT > 5000
  AND    PERIOD_ID = '2026-Q1';

BETWEEN: Range Queries

BETWEEN is shorthand for "greater than or equal AND less than or equal":

SQL — BETWEEN for date ranges
-- Find all results between $1,000 and $5,000
SELECT PARTICIPANT_ID, RESULT_AMOUNT
FROM   CSC_RESULTS
WHERE  RESULT_AMOUNT BETWEEN 1000 AND 5000;

This is equivalent to: RESULT_AMOUNT >= 1000 AND RESULT_AMOUNT <= 5000

IN: Match Multiple Values

IN checks if a value matches any item in a list:

SQL — IN for multiple values
-- Find participants in specific regions
SELECT PARTICIPANT_ID, NAME, COUNTRY
FROM   CSC_PARTICIPANT
WHERE  COUNTRY IN ('US', 'CA', 'MX');

This returns participants from USA, Canada, or Mexico. Without IN, you'd write:

SQL — Same logic without IN
SELECT PARTICIPANT_ID, NAME, COUNTRY
FROM   CSC_PARTICIPANT
WHERE  COUNTRY = 'US'
  OR     COUNTRY = 'CA'
  OR     COUNTRY = 'MX';

IN is cleaner and faster.

LIKE: Text Matching

LIKE matches partial text patterns. Use % as a wildcard:

SQL — LIKE for partial matches
-- Find all participants whose name starts with 'John'
SELECT NAME, EMAIL
FROM   CSC_PARTICIPANT
WHERE  NAME LIKE 'John%';

-- Find participants whose name contains 'Smith'
WHERE  NAME LIKE '%Smith%';

-- Find participants whose email ends with '@company.com'
WHERE  EMAIL LIKE '%@company.com';
💡LIKE is case-insensitive in most databases. 'John%' matches 'John', 'john', 'JOHN', and 'JoHn'. If you need case-sensitive matching, check your database documentation (SAP HANA and Oracle handle this slightly differently).

NULL: The "Missing Value" Problem

NULL is tricky. It doesn't mean zero or empty string. It means "no value" or "unknown." And the rule is: NULL = anything is always false, even NULL = NULL.

SQL — NULL handling
-- WRONG: This returns zero rows, even if quotas are NULL
WHERE QUOTA_AMOUNT = NULL;  -- Never works!

-- CORRECT: Use IS NULL
WHERE QUOTA_AMOUNT IS NULL;

-- CORRECT: Use IS NOT NULL
WHERE QUOTA_AMOUNT IS NOT NULL;

Real ICM scenario: quota amounts are NULL for participants who haven't been assigned to a plan yet. To find these problem cases:

SQL — Find missing quotas
SELECT PARTICIPANT_ID, NAME, COMP_PLAN_ID
FROM   CSC_PARTICIPANT
WHERE  STATUS = 'ACTIVE'
  AND    QUOTA_AMOUNT IS NULL;

This returns all active participants with no quota assigned — a red flag for compensation setup issues.

Combining Conditions with AND / OR

AND means all conditions must be true. OR means any one must be true.

SQL — AND vs OR
-- AND: both conditions must be true
SELECT NAME
FROM   CSC_PARTICIPANT
WHERE  STATUS = 'ACTIVE'
  AND    COUNTRY = 'US';
-- Returns only US-based ACTIVE participants

-- OR: either condition can be true
SELECT NAME
FROM   CSC_RESULTS
WHERE  PERIOD_ID = '2026-Q1'
  OR     PERIOD_ID = '2026-Q2';
-- Returns results from Q1 OR Q2

You can mix AND and OR, but be careful about precedence. Use parentheses to be explicit:

SQL — Parentheses clarify logic
-- Find active OR inactive participants in US
SELECT NAME
FROM   CSC_PARTICIPANT
WHERE  (STATUS = 'ACTIVE' OR STATUS = 'INACTIVE')
  AND    COUNTRY = 'US';

ORDER BY: Sorting Results

ORDER BY sorts your result set. ASC (ascending) is the default; DESC (descending) reverses it.

SQL — ORDER BY examples
-- Sort by name alphabetically (A to Z)
SELECT NAME, EMAIL
FROM   CSC_PARTICIPANT
WHERE  STATUS = 'ACTIVE'
ORDER BY NAME ASC;

-- Sort earnings highest to lowest
SELECT PARTICIPANT_ID, RESULT_AMOUNT
FROM   CSC_RESULTS
WHERE  PERIOD_ID = '2026-Q1'
ORDER BY RESULT_AMOUNT DESC;

-- Sort by multiple columns: period first, then name
SELECT PARTICIPANT_ID, PERIOD_ID, NAME, RESULT_AMOUNT
FROM   CSC_RESULTS
ORDER BY PERIOD_ID ASC, NAME ASC;

The database evaluates ORDER BY last, after WHERE and SELECT, so you can order by columns you didn't explicitly select (though it's clearer to include them).

LIMIT and TOP: Restricting Result Count

LIMIT (SAP HANA Database) or TOP (Oracle Database) caps how many rows you get back:

Use Case SAP HANA Database Oracle Database
Get top 10 earners LIMIT 10 WHERE ROWNUM <= 10
Skip first 100, get next 10 LIMIT 10 OFFSET 100 More complex (Oracle has ROW_NUMBER)

SAP HANA example:

SQL — SAP HANA Database
-- Get top 10 highest earners in Q1 2026
SELECT PARTICIPANT_ID, RESULT_AMOUNT
FROM   CSC_RESULTS
WHERE  PERIOD_ID = '2026-Q1'
ORDER BY RESULT_AMOUNT DESC
LIMIT 10;

Oracle equivalent:

SQL — Oracle Database
SELECT PARTICIPANT_ID, RESULT_AMOUNT
FROM   CS_RESULTS
WHERE  PERIOD_ID = '2026-Q1'
  AND    ROWNUM <= 10
ORDER BY RESULT_AMOUNT DESC;
⚠️In Oracle, ROWNUM is assigned BEFORE ORDER BY, which makes it tricky. Use LIMIT if you're on SAP HANA. For Oracle, we'll use window functions in Lesson 05 for cleaner solutions.

Putting It All Together: A Real ICM Query

Here's a production query that combines everything we've learned. A manager asks: "Which ACTIVE participants in the Northeast region have earned more than $10,000 in Q1 2026, sorted by earnings, top 20?"

SQL — SAP SuccessFactors IM — Real scenario
SELECT
  p.PARTICIPANT_ID,
  p.NAME,
  p.REGION,
  r.RESULT_AMOUNT
FROM     CSC_PARTICIPANT p
WHERE    p.STATUS = 'ACTIVE'
  AND      p.REGION = 'Northeast'
  AND      r.RESULT_AMOUNT > 10000
  AND      r.PERIOD_ID = '2026-Q1'
ORDER BY r.RESULT_AMOUNT DESC
LIMIT    20;

Wait — this query has a problem. We're selecting from CSC_PARTICIPANT (p) but filtering on r.RESULT_AMOUNT and r.PERIOD_ID, which are in the CSC_RESULTS table. We need a JOIN! But we haven't covered that yet. That's Lesson 03.

For now, here's a query that works within SELECT/WHERE scope:

SQL — SAP SuccessFactors IM — Achievable in Lesson 02
SELECT
  PARTICIPANT_ID,
  NAME,
  REGION,
  COUNTRY,
  EMAIL
FROM   CSC_PARTICIPANT
WHERE  STATUS = 'ACTIVE'
  AND    REGION = 'Northeast'
  AND    COUNTRY IN ('US', 'CA')
ORDER BY NAME ASC;

This finds all active Northeast participants from US or Canada, ordered by name. You can run this, it works, and it's production-ready.

What Comes Next

In Lesson 03 (JOINs), we'll solve the problem above: combining data from multiple tables. With JOINs, you'll write queries that answer real business questions in seconds.