SQL SELECT: Querying a Database

Learn to answer questions from the data in one database table. Choose columns with SELECT and FROM, keep the rows you need with WHERE, comparison operators, AND, OR and NOT, and match lists, ranges and text patterns with IN, BETWEEN and LIKE. Find and handle missing values with IS NULL, list different values with DISTINCT, and sort and trim results with ORDER BY and LIMIT. The examples use SQL that runs in SQLite and PostgreSQL, including the widely used LIMIT clause, with notes on the standard form and where MySQL, SQL Server and Oracle differ.

BeginnerLesson 2 of 6 in SQL Basics Course

What you will learn

  1. Write queries that read chosen columns from one table, understand the result set they return, and list the different values in a column with DISTINCT.
    • Write a basic SELECT query
      Name the columns to show with SELECT and the table to read with FROM, follow the basic syntax rules, and know what the result looks like.
    • List different values with DISTINCT
      Remove repeated rows from a result with SELECT DISTINCT and predict how many rows remain, including when several columns or missing values are involved.
  2. Keep only the rows that answer a question by writing conditions with comparison operators on numbers, text and dates, and by combining conditions with AND, OR and NOT.
    • Compare numbers with comparison operators
      Use WHERE with =, <>, <, >, <= and >= to select rows by numeric values, and choose the right operator for the words of a question.
    • Compare text and dates correctly
      Write text and date values in the correct form, predict how case, alphabetical order and date formats affect a comparison, and avoid the usual quoting and date mistakes.
    • Combine conditions with AND, OR and NOT
      Join several conditions with AND and OR, reverse a condition with NOT, and use parentheses so the query tests exactly what the question asks.
  3. Shorten and sharpen conditions with IN for lists of values, BETWEEN for ranges and LIKE for text patterns, including their NOT forms and their usual traps.
    • Match a list of values with IN
      Use IN and NOT IN to keep rows whose value is one of several listed values, and know how lists interact with AND and with missing values.
    • Select a range with BETWEEN
      Use BETWEEN and NOT BETWEEN for ranges of numbers, dates and text, knowing that both ends are included and where that causes mistakes.
    • Search text patterns with LIKE
      Find text that starts with, ends with or contains given characters using LIKE with % and _, and handle case sensitivity, literal wildcards and missing values.
  4. Recognize NULL as a missing value, find it with IS NULL and IS NOT NULL, and predict how missing values change the result of every other kind of condition.
    • Find missing values with IS NULL
      Tell NULL apart from zero and empty text, and use IS NULL and IS NOT NULL to find rows where a value is or is not recorded.
    • Predict how NULL affects conditions
      Apply three-valued logic (true, false, unknown) to comparisons, AND, OR, NOT, IN, BETWEEN and LIKE, and decide whether rows with missing values belong in an answer.
  5. Put result rows in a meaningful order with ORDER BY, sort by several columns while handling ties and missing values, and return the top rows or one page of rows with LIMIT and OFFSET.
    • Sort rows with ORDER BY
      Sort a result by one column in ascending or descending order and predict how numbers, dates and text are ordered.
    • Sort by several columns and handle ties and NULLs
      Sort by more than one column with a direction for each, add tie-breakers for a repeatable order, and control where missing values appear.
    • Return top rows and pages with LIMIT and OFFSET
      Return the first few rows of a sorted result with LIMIT, skip rows with OFFSET to show later pages, and write the same request in other SQL dialects.
  6. Combine all the clauses into correct, readable queries, understand the order in which a query is processed, and turn everyday questions into queries whose results you check.
    • Put the clauses in order and know how a query runs
      Write SELECT, FROM, WHERE, ORDER BY and LIMIT in the required order and use the logical processing order to predict what each clause can see.
    • Turn a question into a query and check the answer
      Translate an everyday question into a single-table query step by step, then test the result for boundaries, missing values, case and ties.

SQL SELECT study guide

6 chapters

1

Choose the data to display

A useful query starts by choosing the table and the columns that answer your question. The result can show every record, a few fields from each record, or a list of different values. You will work with two small tables so you can trace each result back to the stored data. Reading the column names carefully and separating column order from row order will make later filters and sorts easier to understand.

1.1 SELECT, FROM, and the result table

A query asks a database for data. A plain SELECT query reads the stored table without changing it. SELECT names the columns to display, and FROM names the table to read.

The text examples use ordinary case-sensitive equality in SQLite and PostgreSQL. Collation settings can change how text compares and sorts.

Two tables to work with

The employees table has these eight rows. salary is yearly pay in dollars; hire_date is text in YYYY-MM-DD form. NULL marks a missing value.

employee_idfirst_namelast_namedepartmentcitysalaryhire_datephone
1AnaLopezSalesChicago520002019-03-15312-555-0147
2BenCarterITDenver710002021-07-01NULL
3ChloeNguyenSalesChicago480002023-01-09312-555-0198
4DavidKimMarketingBoston600002020-11-23617-555-0110
5EmmaBrownSalesDenver650002018-05-30NULL
6FaridHaddadITChicago830002017-09-12312-555-0175
7GraceOkaforNULLBoston450002024-02-19617-555-0133
8HugoO'NeillMarketingChicago580002022-08-05NULL

The products table records prices in dollars and stock in warehouse units.

product_idnamecategorypricestock
1Desk LampLighting24.9940
2Floor LampLighting89.0012
3Lamp ShadeLighting15.500
4Office ChairFurniture149.008
5Standing DeskFurniture349.005
6Desk OrganizerStorage18.7560
7BookshelfFurniture120.00NULL
8Paper TrayStorage9.9925
Read the query's shape
SELECT city, first_name
FROM employees;

The result set is another table: eight rows and two columns, with city first. No filter is present, so every employee contributes a row. SQL gives no guaranteed row order until you add ORDER BY.

SELECT * FROM products; displays every column in the table's defined order. The star is useful for exploring. In a saved query, naming the columns keeps the result focused and prevents a newly added table column from changing its shape.

Make the syntax readable

Separate columns with commas, with no trailing comma before FROM. End the statement with a semicolon. Keywords ignore case, so select works, but capitals make clauses easy to find. Spaces and line breaks separate words; they do not change this query's meaning. A -- comment continues to the end of its line.

Names must resolve to the schema. If frist_name causes a missing-column error, compare the spelling with first_name. Standard SQL uses double quotes for names containing spaces or reserved words, such as "order date". PostgreSQL folds unquoted names to lowercase, while "City" asks for that exact capitalized name. MySQL normally uses backticks; double quotes identify names in its ANSI_QUOTES mode. SQL Server also accepts square brackets and accepts double quotes when QUOTED_IDENTIFIER is on, its default.

Key Takeaways

  • SELECT chooses displayed columns; FROM identifies their table.
  • The result columns follow the SELECT list; row order needs ORDER BY.
  • SELECT * displays all columns; explicit names keep saved queries focused.
  • Use commas between columns and a semicolon to end a statement.
  • Check names against the schema and use the database’s identifier quoting rules.

1.2 Different values and different combinations

A repeated value in a query result is often useful. Several employees can live in the same city, and each employee still needs a row. Use DISTINCT when the question asks for different values or different combinations.

Choose what should count as a duplicate
SELECT DISTINCT city
FROM employees;

The result contains Chicago, Denver, and Boston, with no guaranteed order. The plain SELECT city would return eight rows because each employee supplies a city. Adding DISTINCT reduces that display to three different values. It leaves all eight employee records in storage.

The comparison includes every selected column. Consider:

SELECT DISTINCT department, city
FROM employees;

Ana and Chloe both contribute (Sales, Chicago), so that pair appears once. Emma contributes (Sales, Denver), which is different and stays. The other pairs are (IT, Denver), (Marketing, Boston), (IT, Chicago), (NULL, Boston), and (Marketing, Chicago). There are seven result rows. Repeated departments remain because a department paired with another city is a different result row.

Write DISTINCT once, directly after SELECT. It applies to the complete selected row. Placing it between column names, as in SELECT department, DISTINCT city, is a syntax error.

Watch which columns you include

If you add employee_id to the selected columns, every row becomes different. SELECT DISTINCT employee_id, city FROM employees; still returns eight rows: each ID is unique, even where the city repeats. To answer “Which cities have employees?”, select the city alone. To answer “Which employees live in each city?”, keep identifying columns and expect repeated cities.

For this duplicate-removal step, repeated NULLs are treated as duplicates. SELECT DISTINCT department FROM employees; returns Sales, IT, Marketing, and one NULL row. The NULL row records that a department is missing; it is not a fourth department name. This duplicate rule differs from NULL = NULL, which gives unknown in a filter.

Text comparisons follow a collation, the database's rules for matching and ordering text. With the ordinary case-sensitive comparisons used for these SQLite and PostgreSQL examples, Chicago and chicago are different values. A DISTINCT list can expose inconsistent capitalization. Removing repeated displays does not repair spelling in the stored data.

Combine DISTINCT with a sorted shortlist
SELECT DISTINCT city
FROM employees
ORDER BY city
LIMIT 2;

First, duplicate cities disappear. Next, the three cities are sorted. Finally, two result rows remain: Boston and Chicago, in that order. Applying the limit to employee rows first would answer a different question.

For a DISTINCT query, keep the sort columns in the SELECT list. PostgreSQL requires this; SQLite permits some queries that sort on an unselected column, but choosing among different hidden values makes their ordering hard to interpret. If a result unexpectedly repeats, inspect which columns you selected before deciding that DISTINCT belongs in the query.

Key Takeaways

  • DISTINCT removes duplicate complete result rows without changing stored rows.
  • Adding columns changes which rows count as duplicates; a unique key keeps every row different.
  • DISTINCT groups repeated NULLs together.
  • Duplicates are removed before the sorted result is limited.
2

Keep the rows that answer the question

Filtering turns a table into an answer about particular records: a pay range, a city, or a group meeting several conditions. Small choices matter, including whether an endpoint counts and whether two conditions must both hold. Numeric comparisons, text and date values, and grouped logic each have their own rules. A query can run successfully and still ask the wrong question, so you will trace which rows pass each test.

2.1 Numbers and boundary values

The WHERE clause keeps a row only when its condition is true. It follows FROM and can test any column in the table, including a column you do not display.

Match the words to the operator
RequestOperatorExample condition
Equal to=stock = 0
Different from<>stock <> 0
More than>salary > 60000
At least>=salary >= 60000
Less than<price < 20.50
At most<=price <= 20.50

<> is standard SQL for “not equal.” The five databases discussed here also accept !=. Use one equals sign for equality. SQLite accepts ==, but PostgreSQL rejects that form.

Trace a boundary row
SELECT first_name, salary
FROM employees
WHERE salary > 60000;

Ben has 71000, Emma has 65000, and Farid has 83000. Those are the three qualifying rows, with no stated order. David earns exactly 60000. His row fails > 60000 because “more than” excludes equality.

Change only the operator to >=. David now joins the result, giving four rows. This is how “at least 60000” differs from “more than 60000.” A useful boundary check asks what happens to a value exactly equal to the threshold; values far above it cannot reveal that mistake.

For “at most 20.50,” a price of 20.50 passes and a price of 20.51 fails. When a request gives both a lower and an upper bound, check each end separately before joining the tests.

Write numeric values as numbers

Use plain digits in SQL conditions: 60000, with no thousands comma or currency sign. A comma separates SQL items; WHERE salary > 60,000 is a syntax error. Decimal values use a point, as in WHERE price < 20.50. Salaries and prices belong in numeric columns so that comparisons use their numeric size.

Text that looks numeric follows text rules when compared as text. The text value '9' sorts after '10' because its first character comes later. That tells you about characters, not about nine being greater than ten. Read the column's data type before interpreting an unexpected comparison.

Filter without displaying the tested column
SELECT first_name
FROM employees
WHERE salary < 50000;

Chloe and Grace pass. Their salaries are used to test the rows but are absent from the result. You do not need to add salary to SELECT to make the filter work. While checking a query, showing it temporarily can help you inspect the reason each row passed.

Equality on a primary key provides a direct record lookup: WHERE employee_id = 6 finds Farid. A primary key identifies one row, so that equality cannot return several employees with the same ID.

Missing data needs separate care. WHERE stock <> 0 returns six products. Lamp Shade is left out because its known stock is zero. Bookshelf is also left out: its stock is NULL, so the comparison is unknown and WHERE does not keep it. A “not equal” test does not add missing values to its result.

Key Takeaways

  • WHERE retains only rows whose condition is true.
  • > and < exclude their thresholds; >= and <= include them.
  • Write numeric literals without currency signs, quotes, or thousands separators.
  • A filter can test a column that is not displayed.
  • Use a key equality to look up one record.
  • Ordinary comparisons with NULL do not pass a WHERE filter.

2.2 Text, quotes, and complete dates

Text and date conditions need values in a form the database can interpret. A misspelled value may quietly return no rows, while a missing quote can turn that value into a column name.

Distinguish a value from a name
SELECT first_name
FROM employees
WHERE city = 'Chicago';

The single quotes make 'Chicago' a text value. The result contains Ana, Chloe, Farid, and Hugo. Without quotes, Chicago is read as a name, so the database looks for a column with that name. Standard double quotes also mark names. PostgreSQL rejects city = "Chicago" when no such column exists. Some SQLite configurations accept double-quoted strings as a legacy feature; use single quotes for values consistently.

An apostrophe inside a value is doubled:

SELECT first_name
FROM employees
WHERE last_name = 'O''Neill';

That finds Hugo. The two adjacent single quotes represent one apostrophe inside the value; the outer quotes mark its start and end. Standard SQL does not use a backslash to escape that apostrophe.

Check the comparison rules

A collation controls text equality and sorting, including case and accented letters. In the ordinary case-sensitive SQLite and PostgreSQL comparisons used here, 'Chicago' and 'chicago' differ. WHERE city = 'chicago' returns no rows. MySQL and SQL Server commonly use case-insensitive collations, so their usual settings can match both spellings.

Text range comparisons also follow collation. With these names, WHERE last_name < 'M' returns Brown, Carter, Haddad, Kim, and Lopez. Values are compared from the left. A longer value beginning with the same characters comes after its prefix: 'Martin' is after 'M'. A cutoff of 'M' is not the same as “all names beginning with M.”

Put the year first

The example hire_date column stores full dates as text: year, month, then day, with fixed widths. This makes text order agree with calendar order.

SELECT first_name, hire_date
FROM employees
WHERE hire_date >= '2022-01-01';

Chloe, Grace, and Hugo pass. PostgreSQL also has a real DATE type that accepts this literal. Its standard typed spelling is DATE '2022-01-01'; SQLite does not use that typed-literal syntax.

A local date string such as '03/04/2024' is ambiguous in a date column because settings can make it March 4 or April 3. Stored as text, month-first dates compare by month before year. For example, '12/01/2021' sorts after '03/15/2024' even though the first calendar date is earlier.

Translate the full date boundary

“Hired after 2022” means starting with '2023-01-01'. Comparing text to '2022' instead also admits Hugo's '2022-08-05': the longer text sorts after the matching prefix.

If a column contains times, equality with a date alone does not select every time on that day. To include March 31, 2024, use ordered_at >= '2024-03-31' AND ordered_at < '2024-04-01'. This keeps every time from the start of March 31 up to, but excluding, the start of April 1.

Key Takeaways

  • Use single quotes for text values; double an apostrophe inside a value.
  • Collation determines how text case and characters compare.
  • Use complete YYYY-MM-DD dates so fixed-width text dates sort chronologically.
  • To cover an entire day with times, include its start and exclude the next day’s start.

2.3 AND, OR, NOT, and grouped conditions

A row may need to meet several tests. AND requires both conditions to be true. OR requires at least one to be true. NOT reverses true and false; when a condition is unknown because of NULL, NOT leaves it unknown.

Decide whether both tests must hold
SELECT first_name
FROM employees
WHERE department = 'Sales' AND city = 'Chicago';

Ana and Chloe pass both tests. Emma works in Sales but lives in Denver, so she fails this AND. Changing AND to OR admits Emma, Farid, and Hugo as well: the five qualifying employees satisfy one test or both.

Adding an AND condition can keep or reduce the result. Adding an OR condition can keep or expand it. This gives you a quick check when building a filter in stages.

Every comparison must be complete. Write department = 'IT' OR department = 'Marketing', repeating the column and operator. department = 'IT' OR 'Marketing' does not express the second comparison. PostgreSQL rejects it; SQLite treats the text on its own as false and returns only IT staff. Likewise, write salary > 50000 AND salary < 70000, with salary on both sides of AND.

Translate groups of alternatives

“Employees in Boston and Denver” usually asks for people in either city. Each row has one city, so use city = 'Boston' OR city = 'Denver'. That returns Ben, David, Emma, and Grace. An AND between these equalities asks one city value to be both places at once and returns no rows.

When operators mix, NOT is evaluated before AND, and AND before OR. Without parentheses:

WHERE department = 'Sales'
   OR department = 'Marketing' AND city = 'Chicago'

This means Sales staff anywhere, plus Marketing staff in Chicago. Emma passes because the Sales comparison is enough. To restrict both departments to Chicago, group the alternatives:

SELECT first_name
FROM employees
WHERE (department = 'Sales' OR department = 'Marketing')
  AND city = 'Chicago';

Now Ana, Chloe, and Hugo pass. The parentheses put the two department choices under one shared city requirement. Use them to make a mixed AND/OR filter clear even when the default order would already be right.

Reverse a whole group

NOT (city = 'Chicago' OR city = 'Denver') returns David and Grace in Boston. Neither listed city is allowed. It is equivalent to city <> 'Chicago' AND city <> 'Denver' for these values.

Using OR between those two “not equal” tests is a common trap. A Chicago row differs from Denver, and a Denver row differs from Chicago. Every employee with a recorded city passes at least one test, so the OR fails to exclude either city.

Build a complex condition one piece at a time. Check the rows after the first test, then after adding the next. You can swap the two sides of an AND, or the two sides of an OR, without changing the answer; choose the arrangement that makes the groups easiest to read.

Key Takeaways

  • AND needs both conditions; OR needs at least one.
  • Write a complete comparison on each side of a logical operator.
  • NOT is evaluated before AND, then OR; parentheses make the intended grouping explicit.
  • Alternatives for one column usually need OR, even when the request says “and.”
  • To exclude either of two matches, negate the OR group or join the two exclusions with AND.
3

Match lists, ranges, and text patterns

Some questions call for several exact choices, others for a stretch of values or part of a name. IN, BETWEEN, and LIKE give each kind of request a clear form. Choosing between them starts with what a match means: a whole value, a value between endpoints, or text fitting a pattern. You will also check missing values and the details that change an answer, including inclusive endpoints, wildcard placement, and case sensitivity.

3.1 Lists of exact choices with IN

Use IN when one column may match any value in a list. It shortens a group of equalities joined with OR while keeping their meaning.

Build a list of alternatives
SELECT first_name
FROM employees
WHERE department IN ('IT', 'Marketing');

Ben, David, Farid, and Hugo pass. Each has one of the two department values. The same test could be written as department = 'IT' OR department = 'Marketing'.

Put list items inside parentheses, separated by commas. Quote text; leave numeric values unquoted. For example, employee_id IN (2, 4, 6) selects Ben, David, and Farid. The order of list entries does not sort the result. Repeating an entry does not repeat a matching row: IN (6, 2, 2, 4) selects the same records. Use ORDER BY if you need a result order.

IN matches whole values under the same comparison rules as equality. city IN ('Chi') does not match Chicago. Under the ordinary case-sensitive comparisons used here, city IN ('chicago') does not match it either. A fragment search needs LIKE, while case behavior depends on collation.

Apply another rule to every choice
SELECT first_name
FROM employees
WHERE department IN ('IT', 'Marketing')
  AND city = 'Chicago';

Farid and Hugo pass. IN is one condition, so AND applies the city test to the entire department choice. In the expanded OR version, put parentheses around the two department comparisons. Without them, department = 'IT' OR department = 'Marketing' AND city = 'Chicago' also admits Ben, because AND binds more tightly and the city rule applies only to Marketing.

Exclude the listed values

WHERE city NOT IN ('Chicago', 'Denver') returns David and Grace. A recorded city passes only when it differs from both entries. This is a useful form for several exclusions and avoids the mistake of joining “not equal” conditions with OR.

Missing values are not a match for either form. Grace has a NULL department, so department NOT IN ('Sales') does not include her. The database cannot establish that the missing department differs from Sales. To include unassigned staff as well, write department NOT IN ('Sales') OR department IS NULL.

A NULL inside a NOT IN list creates a broader problem. department NOT IN ('Sales', NULL) requires both department <> 'Sales' and department <> NULL. The second comparison is always unknown, so the combined test is never true and WHERE returns no rows. Keep missing-value tests separate from the value list.

With IN, a real match still succeeds even if another list entry is NULL. department IN ('Sales', NULL) keeps the three Sales staff, but it does not add Grace. Use IS NULL to request missing values explicitly.

An ordinary SQL IN list needs at least one item. PostgreSQL rejects IN (); SQLite accepts it as false. If a program builds a list from user choices, handle an empty list before sending the query so the behavior is deliberate across databases.

Key Takeaways

  • IN matches any whole value in its parenthesized, comma-separated list.
  • List order and repeated entries do not control result order or duplicate matching rows.
  • IN combines with AND as one condition.
  • NOT IN excludes listed values but does not include missing values.
  • A NULL inside a NOT IN list prevents any row from passing the WHERE test.

3.2 Ranges and included endpoints

BETWEEN tests a range with both ends included. Use it when “from” and “to” include the named boundary values; use explicit comparisons when either boundary must be excluded.

Check the two ends
SELECT name, price
FROM products
WHERE price BETWEEN 15.50 AND 89.00;

Desk Lamp, Floor Lamp, Lamp Shade, and Desk Organizer pass. Floor Lamp costs exactly 89.00 and Lamp Shade exactly 15.50, so both remain. The condition means price >= 15.50 AND price <= 89.00.

Put the lower value first. price BETWEEN 89.00 AND 15.50 asks for a price at least 89.00 and at most 15.50. It is valid syntax but has no matches. Ordinary BETWEEN does not reorder the endpoints for you. PostgreSQL offers BETWEEN SYMMETRIC to do that, but use the lower-first form for a query shared with SQLite.

For “more than 15.50 and less than 89.00,” write price > 15.50 AND price < 89.00. Only Desk Lamp and Desk Organizer pass. That difference is about the question's endpoints, not the way the result is displayed.

Outside a range

price NOT BETWEEN 20 AND 100 means price < 20 OR price > 100. It returns Lamp Shade, Office Chair, Standing Desk, Desk Organizer, Bookshelf, and Paper Tray. Values exactly 20 or 100 would be inside the original range and would fail NOT BETWEEN.

A missing tested value is unknown in either test. For stock, BETWEEN 5 AND 40 selects five products and NOT BETWEEN selects two: Lamp Shade with 0 and Desk Organizer with 60. Bookshelf has NULL stock and belongs to neither result. A range and its negation therefore need not cover every stored row.

Dates and times

Complete date strings in YYYY-MM-DD order support date ranges. hire_date BETWEEN '2020-01-01' AND '2022-12-31' selects Ben, David, and Hugo, covering all three calendar years in this date-only column.

A column containing times needs a different upper boundary for a whole month. A date-only endpoint in a timestamp column means midnight at the start of that day. In text, even '2024-03-31 00:00:00' sorts after the shorter prefix '2024-03-31'. To include all March times, use:

WHERE ordered_at >= '2024-03-01'
  AND ordered_at < '2024-04-01'

This includes the beginning of March and stops just before April, without guessing the final fraction of a second on March 31.

Text ranges and the two uses of AND

Text endpoints are complete values. last_name BETWEEN 'A' AND 'C' includes Brown but excludes Carter, since Carter sorts after its prefix C. Under these text rules, last_name >= 'A' AND last_name < 'D' covers names beginning A, B, or C and returns Brown and Carter.

In price BETWEEN 10 AND 100 AND category = 'Lighting', the first AND separates range endpoints; the second joins the range test to the category test. The matching products are Desk Lamp, Floor Lamp, and Lamp Shade. Line breaks can make the two roles easier to see.

Key Takeaways

  • BETWEEN includes both endpoints and expects the lower endpoint first.
  • NOT BETWEEN selects values below the low end or above the high end.
  • Explicit comparisons let you exclude one or both endpoints.
  • For a whole month with times, include its start and stop before the next month.
  • A text endpoint is a whole value, not every value with that starting letter.
  • NULL in the tested column passes neither range test.

3.3 Text patterns with LIKE

LIKE tests text against a pattern. Use it to search for a beginning, an ending, or a fragment when equality would ask for a whole exact value.

Place the wildcards
PatternMeaningMatching product names
'Lamp%'Starts with LampLamp Shade
'%Lamp'Ends with LampDesk Lamp, Floor Lamp
'%Lamp%'Contains LampDesk Lamp, Floor Lamp, Lamp Shade
'Lamp'Exactly LampNone

The pattern must match the whole value. % can represent zero or more characters, including spaces. In '%Lamp%', either percent sign may match nothing, so the text can begin or end with Lamp as well as contain it in the middle.

_ stands for exactly one character. last_name LIKE '_im' finds Kim. Four underscores in first_name LIKE '____' find Emma and Hugo, the first names with exactly four letters. The underscore is not an optional character.

SELECT first_name, phone
FROM employees
WHERE phone LIKE '312-%';

Ana, Chloe, and Farid pass because their recorded phone text starts with 312-. The percent sign covers every remaining character.

Choose the operator and case rule

Wildcards belong to LIKE patterns. name = '%Lamp%' looks for literal percent signs and returns no products here. The file-search wildcards * and ? have no special meaning in LIKE; name LIKE 'Desk*' asks for the literal asterisk after Desk.

SQLite's LIKE normally ignores case for ASCII letters A–Z, while PostgreSQL's ordinary LIKE is case-sensitive. name LIKE '%desk%' therefore finds Desk Lamp, Standing Desk, and Desk Organizer in SQLite, but none in PostgreSQL under case-sensitive collation. PostgreSQL also provides the case-insensitive ILIKE. MySQL's behavior follows collation.

For the ASCII product names used here, LOWER(name) LIKE '%desk%' gives the same matches in SQLite and PostgreSQL: LOWER converts the name to lowercase before matching. Case conversion for other writing systems depends on database and language rules. SQLite's equality is also different from its LIKE: city = 'chicago' fails to find Chicago under the default binary collation even though city LIKE 'chicago' finds it.

Search for a real wildcard character

To match an underscore literally, choose an escape character:

WHERE code LIKE 'SPRING!_%' ESCAPE '!'

In a discount-code table, this matches 'SPRING_10' but not 'SPRINGS10'. The !_ is a real underscore; the last % still matches a sequence. Use the same approach with !% for a literal percent sign. Writing ESCAPE explicitly avoids relying on database defaults; PostgreSQL normally uses a backslash and SQLite has no default escape character.

Negation and search size

name NOT LIKE '%Lamp%' returns the other five products. Missing text passes neither LIKE nor NOT LIKE. Of the employees, three phones match '312-%', two recorded phones do not, and the three NULL phones appear in neither result.

A leading % gives an ordinary index no fixed starting text to narrow down, so a large part of the table may be scanned. A fixed start such as 'Desk%' can help an eligible index. For dates, use date ranges: LIKE on these text dates works, but PostgreSQL requires conversion before applying LIKE to a true DATE column.

Key Takeaways

  • LIKE matches the whole value; % covers any number of characters and _ exactly one.
  • Put % at the start, end, or both to control where a fragment can occur.
  • Case behavior differs by database and collation; LOWER helps with these ASCII names.
  • ESCAPE lets a wildcard represent its literal character.
  • NULL matches neither LIKE nor NOT LIKE.
  • Leading wildcards can make a search scan more data; use ranges for dates.
4

Account for missing values

Missing data changes which records a filter can find. A zero stock count, an empty phone string, and an unrecorded value tell you different things, even if a query tool makes some cells look alike. You will use explicit missing-value tests and trace the third truth value, unknown. These rules explain why a filter and its opposite may leave out the same rows, and how to include those rows when the question calls for them.

4.1 NULL, zero, and empty text

NULL marks a missing value: a value may be unknown, not recorded, or inapplicable. It is different from a known zero or known text with no characters.

Read what a missing value means

Lamp Shade has stock = 0. Its recorded quantity is zero, so it is out of stock. Bookshelf has stock NULL: the quantity was not recorded. The database cannot tell you that it has zero units or any other number.

The distinction changes the question you can answer:

QuestionConditionResult
Which products are out of stock?stock = 0Lamp Shade
Which stock quantities are unknown?stock IS NULLBookshelf
Which quantities are recorded?stock IS NOT NULLThe other seven products

A blank-looking cell in a query tool is not enough to distinguish these states. Some tools show NULL as an empty cell; others print NULL. Test the stored value directly.

Use the missing-value tests
SELECT first_name
FROM employees
WHERE phone IS NULL;

Ben, Emma, and Hugo have no recorded phone. IS NULL gives true for their missing values. To find the five employees with a recorded phone, use phone IS NOT NULL: Ana, Chloe, David, Farid, and Grace.

Do not write phone = NULL to find missing phones. An ordinary comparison involving NULL gives unknown, even when both sides are NULL. WHERE keeps true rows only, so = NULL returns none. <> NULL has the same problem. IS NULL is a test for the missing marker and gives a definite true or false answer for these single-column values.

The form NOT phone IS NULL also finds present phones in SQLite and PostgreSQL, but phone IS NOT NULL makes the intended test easier to read.

Separate three kinds of text
SQL expressionWhat it represents
NULLA missing value
''A known text value with zero characters
'NULL'Four ordinary letters

SQLite, PostgreSQL, MySQL, and SQL Server distinguish empty text from NULL. phone = '' would find empty strings, not Ben's, Emma's, or Hugo's missing phones. If a column can contain both, use phone IS NULL OR phone = '' to find either state. Oracle treats an empty text value as NULL, so this distinction changes when you move databases.

Apply the wording and the schema

“Missing,” “not recorded,” and “not assigned” point to IS NULL. department IS NULL finds Grace. Her missing department does not mean the department text is the word 'NULL', nor does it establish which department she belongs to.

The table definition can mark a column NOT NULL, meaning that a stored row cannot have NULL in that column. An IS NULL query on such a column returns no rows. Check this rule when inspecting a schema: columns that permit missing data need special attention when you build exclusions or combine conditions.

Key Takeaways

  • NULL is a missing marker; zero and empty text are known values.
  • IS NULL finds missing values, and IS NOT NULL finds present values.
  • = NULL and <> NULL cannot find rows through ordinary comparisons.
  • Quoted 'NULL' is text, and Oracle treats empty text differently from the other databases shown.
  • A NOT NULL column cannot store NULL.

4.2 True, false, and unknown

A condition in SQL can be true, false, or unknown. Missing values make the third state necessary: without a recorded quantity, a database cannot establish whether that quantity is above a threshold.

A condition and its opposite

For Bookshelf, stock > 10 is unknown because stock is NULL. So is stock <= 10. WHERE keeps only true conditions and drops both false and unknown ones.

In the products table, stock > 10 returns four rows and stock <= 10 returns three. Together they cover seven of the eight products. Bookshelf is absent from both. Applying NOT does not restore it: NOT unknown is unknown, so NOT (stock > 10) still selects only the three known counts at most 10.

This differs from a value that fails a condition. Lamp Shade's known zero makes stock > 10 false, so NOT turns it true. A missing count supplies no such definite comparison to reverse.

Combine unknown with another test
CombinationResult
Unknown AND falseFalse
Unknown AND trueUnknown
Unknown AND unknownUnknown
Unknown OR trueTrue
Unknown OR falseUnknown
Unknown OR unknownUnknown

Bookshelf costs 120.00. In stock > 10 OR price > 100, the price condition is true. OR needs only one true part, so the row is kept even though the stock comparison is unknown. In stock > 10 AND price > 100, the stock condition remains unresolved, so the result is unknown and the row is dropped.

Include missing values deliberately
SELECT first_name
FROM employees
WHERE department <> 'Sales'
   OR department IS NULL;

This returns Ben, David, Farid, Hugo, and Grace. The comparison alone would return four, leaving Grace out. The IS NULL branch explicitly includes staff whose department is unassigned. Whether such staff belong in “outside Sales” is a question about the intended answer; the query must express that choice.

A NULL value in a NOT IN list can block the entire result. department NOT IN ('Sales', NULL) means that the department must differ from Sales and differ from NULL. The latter comparison is unknown for every row, so the full condition is never true. IN still keeps real matches: department IN ('Sales', NULL) selects the three Sales employees, but does not find Grace.

A missing tested value also produces unknown for LIKE and for an ordinary BETWEEN range. IS NULL and IS NOT NULL give definite answers for these scalar columns. Check missing values with those tests separately when a row count seems surprising.

Know when NULLs are grouped

NULL = NULL remains unknown; two missing phone numbers are not known to be the same number. DISTINCT, however, treats repeated missing values as duplicates, and ORDER BY places them together. For a comparison that deliberately treats two NULLs as equal, SQLite and PostgreSQL also provide IS NOT DISTINCT FROM.

Key Takeaways

  • WHERE keeps true rows and removes false and unknown rows.
  • NOT leaves unknown unchanged; negated filters can still miss NULL rows.
  • A true OR branch can keep a row with another unknown branch.
  • Add IS NULL explicitly when missing values should belong in the answer.
  • A NULL in a NOT IN list means no row can pass that exclusion filter.
  • Duplicate removal groups NULLs even though ordinary NULL equality is unknown.
5

Sort results and choose a page

A row limit becomes meaningful only after you decide which rows should come first. Sorting can arrange numbers, text, or dates, and later sort columns settle ties in earlier ones. Missing values and tied rows need attention when a shortlist or a page must be repeatable. You will build complete sort orders, apply limits after filtering, and recognize the different row-limit syntax used by common database systems.

5.1 Ascending and descending order

ORDER BY sets the order of result rows. Without it, SQL makes no promise about which row appears first, even when a small table seems to return the same order every time.

Choose the direction
DirectionNumbersComplete datesText
ASCSmallest firstEarliest firstCollation order
DESCLargest firstLatest firstReverse collation order

ASC is the default. ORDER BY salary and ORDER BY salary ASC ask for the same direction. “Highest paid first” calls for ORDER BY salary DESC.

SELECT first_name, salary
FROM employees
ORDER BY salary DESC;

Read the result from highest to lowest: Farid 83000, Ben 71000, Emma 65000, David 60000, Hugo 58000, Ana 52000, Chloe 48000, and Grace 45000. The database rearranges the result rows; this query does not change the stored table.

Filter before sorting
SELECT name, price
FROM products
WHERE category = 'Lighting'
ORDER BY price;

Only the three Lighting rows enter this result. Their ascending prices produce Lamp Shade 15.50, Desk Lamp 24.99, then Floor Lamp 89.00. ORDER BY follows WHERE in the statement. Reversing those clauses is a syntax error.

The sort column need not be displayed. SELECT first_name FROM employees ORDER BY salary DESC; gives the same name order without showing salaries. PostgreSQL restricts this when DISTINCT is present: a DISTINCT query must select its sort columns.

Read the stored data type

Numeric columns sort by numeric size. Text columns sort by characters under their collation. The text values '10', '9', and '100' sort as '10', '100', '9' under ordinary character ordering. This is not a correct numeric ranking, so check the column type when amounts look out of order.

The example hire_date values are complete fixed-width text dates with the year first. ORDER BY hire_date starts with Farid on 2017-09-12 and ends with Grace on 2024-02-19. DESC reverses that for newest hires first. Month-first text dates such as MM/DD/YYYY compare by month before year and cannot give reliable calendar order across years.

Text order depends on collation

SQLite's default binary collation puts uppercase ASCII letters before lowercase letters. Under that rule, 'Zebra' precedes 'apple', and 'é' comes after 'z'. Many PostgreSQL databases use language-aware collations that produce another order. Consistent capitalization helps, but the database's comparison rules still decide the order of accented and other characters.

Use column names for a clear sort specification. ORDER BY 2 DESC means the second item in the SELECT list, so moving columns can silently change the sort. ORDER BY 'salary' names a text constant, not the salary column: SQLite provides no useful ordering from it, and PostgreSQL rejects it. Write ORDER BY salary.

Key Takeaways

  • ORDER BY determines row order; ASC is the default and DESC reverses the direction.
  • WHERE filters rows before they are sorted.
  • Numeric size, date representation, and text collation determine what “ascending” means.
  • Name sort columns explicitly to avoid quoted constants and changing column positions.

5.2 Several sort columns, ties, and missing values

A second sort column matters only when rows tie on the first. Think of a sorted staff list: group by city first, then put the names within each city in order.

Follow the sort columns from left to right
SELECT last_name, city
FROM employees
ORDER BY city, last_name;

The result begins with Kim and Okafor in Boston. Then come Haddad, Lopez, Nguyen, and O'Neill in Chicago. Brown and Carter in Denver are last. last_name does not compete with city; it decides the order only within a shared city.

Reversing the columns changes the main order. ORDER BY last_name, city mainly sorts by last name and consults city only when last names tie. Use one ORDER BY clause with commas between its sort columns. A second ORDER BY clause is invalid.

Give each column its direction

ORDER BY department, salary DESC means department ascending, then salary descending within each department. DESC belongs only to salary. To make both descending, write department DESC, salary DESC.

In SQLite's default NULL ordering, the first form places Grace's missing department first. IT follows with Farid then Ben; Marketing has David then Hugo; Sales has Emma, Ana, then Chloe. You are reading one hierarchy of comparisons, not first sorting every row by salary and then ignoring that order.

Finish a tie with a unique value

Rows equal on every sort column can appear in either order. ORDER BY city does not settle which Chicago employee comes first. A complete sort order adds a last column that uniquely identifies each row:

SELECT first_name, city
FROM employees
ORDER BY city, employee_id;

The IDs resolve every city tie. For unchanged data, this makes a later LIMIT or page select a repeatable part of the ordered list. A unique tie-breaker fixes which rows appear at a cutoff; it does not include extra tied rows.

Position missing values deliberately

Database defaults differ:

DatabaseNULL in ASC orderNULL in DESC order
SQLite, MySQL, SQL ServerFirstLast
PostgreSQL, OracleLastFirst

So the department query puts Grace last in PostgreSQL under its default, even though it puts her first in SQLite. This difference is separate from the salary direction.

SELECT first_name, department, salary
FROM employees
ORDER BY department NULLS LAST, salary DESC;

SQLite 3.30 or later, PostgreSQL, and Oracle support NULLS FIRST and NULLS LAST. Here, they put Grace after all recorded departments while preserving salary order within each department. MySQL and SQL Server do not accept those clauses.

Translate a requested hierarchy in the same sequence it is spoken: “by department, highest paid first within each department” gives department, salary DESC. “Alphabetically by last name, then first name” gives last_name, first_name. Add an ID last when you need repeatable order among people with identical names.

Key Takeaways

  • Later ORDER BY columns break ties in earlier ones.
  • ASC or DESC applies to its own column; omitted directions default to ASC.
  • Adding a unique final sort column makes the order complete for unchanged data.
  • NULL placement depends on the database; supported NULLS FIRST/LAST clauses set it explicitly.
  • A tie-breaker settles row choice without expanding a LIMIT to include all ties.

5.3 Top results and pages with LIMIT

LIMIT keeps at most a chosen number of result rows. It does not decide which rows deserve to be first. Use ORDER BY to give a shortlist a meaning such as highest paid, newest, or cheapest.

Sort, then cut
SELECT first_name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;

The sorted salaries begin with Farid 83000, Ben 71000, and Emma 65000, so those three rows remain. Without ORDER BY, LIMIT 3 returns some three rows, with no promise that they are the highest earners.

WHERE works before the limit. WHERE city = 'Boston' ORDER BY salary DESC LIMIT 5 returns only David and Grace because just two employees qualify. LIMIT is a maximum, not a request to invent extra rows. LIMIT 0 returns none.

Skip rows for a later page

OFFSET skips rows before LIMIT counts the rows to return. ORDER BY salary DESC LIMIT 3 OFFSET 3 skips Farid, Ben, and Emma, then returns David, Hugo, and Ana: positions four through six.

For page k with n rows per page, skip (k − 1) × n rows. Page three at three rows per page uses LIMIT 3 OFFSET 6. Only Chloe and Grace remain. The first page has offset zero, not one.

“Second row” and “second different value” are different requests. ORDER BY salary DESC LIMIT 1 OFFSET 1 returns Ben with 71000 here. If two employees shared the top salary, that query would return another top earner. To find the second different salary, select DISTINCT salary before sorting and skipping.

Settle the cutoff

ORDER BY city LIMIT 3 includes the two Boston employees and one Chicago employee, but does not specify which Chicago employee. ORDER BY city, employee_id LIMIT 3 selects David, Grace, and Ana. The unique ID resolves the cutoff.

To keep everyone tied at the final sort value, PostgreSQL 13 or later and Oracle support FETCH FIRST 3 ROWS WITH TIES. With ORDER BY city, it keeps six rows: both Boston rows and all four Chicago rows. If the ORDER BY also includes a unique ID, rows cannot tie on the complete sort key.

Recognize the database's syntax
DatabaseA three-row limit
SQLite, PostgreSQL, MySQLORDER BY salary DESC LIMIT 3
PostgreSQL, Oracle 12c or laterORDER BY salary DESC FETCH FIRST 3 ROWS ONLY
SQL ServerSELECT TOP (3) first_name, salary FROM employees ORDER BY salary DESC;

Standard SQL uses OFFSET and FETCH, not LIMIT. PostgreSQL and Oracle accept OFFSET 3 ROWS FETCH FIRST 3 ROWS ONLY. SQL Server uses ORDER BY salary DESC OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY; ORDER BY is required for this paging form.

SQLite and MySQL also accept LIMIT 6, 3, with the offset first. Prefer LIMIT 3 OFFSET 6 so the roles are explicit. Paging runs a query again for each page. A complete sort order keeps pages consistent while data stays unchanged; inserts or deletions between requests can still make pages repeat or skip records.

Key Takeaways

  • ORDER BY decides which rows come first; LIMIT keeps at most the requested number.
  • OFFSET skips rows before LIMIT counts; the first page uses offset zero.
  • Select DISTINCT first when ranking different values instead of individual rows.
  • A unique tie-breaker fixes a cutoff; WITH TIES can include extra rows tied on the full sort key.
  • LIMIT, TOP, and OFFSET/FETCH are database-specific forms of row limiting.
  • Changed data can shift page boundaries between requests.
6

Build and check a complete query

A workplace question usually combines several choices: which records count, which details to show, and which rows come first. Planning those choices separately helps you write a query whose parts agree. The written clause order and the logical order explain why a filter can use a hidden column and why a limit applies to qualifying rows. You will also check boundaries, missing data, and ties before trusting a plausible-looking result.

6.1 Written order and logical processing

SQL clauses have a fixed written order. Their logical order explains the result as a sequence of decisions, even when the database uses a different internal execution plan.

Write the statement in clause order
SELECT DISTINCT city
FROM employees
WHERE department = 'Sales'
ORDER BY city
LIMIT 2 OFFSET 0;

SELECT and FROM identify the requested columns and source table. The other clauses are optional, but when present they keep this order: WHERE, ORDER BY, LIMIT, then OFFSET in this LIMIT form. DISTINCT sits directly after SELECT.

Use one WHERE clause with several joined conditions. A second WHERE is a syntax error. The same applies to ORDER BY: put several sort columns in one clause, separated by commas. Moving WHERE below ORDER BY does not ask for a different processing order; it produces invalid SQL.

Trace the logical decisions
Logical stageWhat happens in the example
FROMRead employee rows.
WHEREKeep Ana, Chloe, and Emma in Sales.
SELECTKeep their city values: Chicago, Chicago, Denver.
DISTINCTReduce those values to Chicago and Denver.
ORDER BYPut Chicago before Denver.
OFFSET and LIMITSkip none and return at most two rows.

The result is Chicago, then Denver. The repeated city disappears before the limit counts result rows. You are not taking two employees and then asking whether they live in different cities.

The optimizer may use a different physical execution plan that produces the same result as this logical sequence.

Use columns where they are available

WHERE tests the source rows before SELECT chooses displayed columns:

SELECT first_name
FROM employees
WHERE salary > 60000;

This works without salary in the output. The qualifying names are Ben, Emma, and Farid. Salary is still a column of each source row when the test is made.

Likewise, filtering before limiting is essential for a top result within a group:

SELECT first_name
FROM employees
WHERE department = 'Sales'
ORDER BY salary DESC
LIMIT 1;

The answer is Emma. Sales staff qualify first; their salaries are then ranked. Taking the company's top earner first and testing that single row for Sales would miss the intended answer, because Farid belongs to IT.

Make the structure visible

Write one clause per line and place longer conditions on separate indented lines. For “Furniture products below 200 dollars, highest price first,” use:

SELECT name, price
FROM products
WHERE category = 'Furniture'
  AND price < 200
ORDER BY price DESC;

Office Chair at 149.00 comes before Bookshelf at 120.00. The two conditions share one WHERE, the numeric threshold has no currency sign, and ORDER BY sorts the rows that passed. Checking each line against its role makes both syntax errors and wrong interpretations easier to locate.

Key Takeaways

  • Write SELECT, FROM, WHERE, ORDER BY, and LIMIT in their required clause order.
  • Logically read the table, filter, select columns, remove duplicates, sort, then slice rows.
  • WHERE can test source columns that SELECT does not display.
  • Filtering before ranking finds the top result within the requested group.

6.2 From a request to a checked answer

Before writing SQL, identify the separate decisions hidden in an everyday request. Each decision belongs to a clause, and each clause can be checked against the intended answer.

Make six decisions
  1. Which table contains the records? Choose FROM.
  2. Which records qualify? Build WHERE.
  3. Which fields answer the question? Choose SELECT.
  4. Are repeated result values useful? Decide on DISTINCT.
  5. What should come first? Build ORDER BY.
  6. Is there a maximum or page? Add LIMIT and OFFSET.

For “at least,” include equality. “One of” suggests IN, “contains” suggests a LIKE pattern with percent signs at both ends, “missing” suggests IS NULL, and “different values” suggests DISTINCT. A top-n request needs both a sort direction and a limit.

Build a staff shortlist

“Show the names and hire dates of the three newest hires outside Sales who have a phone on file, newest first.” Include unassigned staff in this request's meaning of outside Sales.

Start with employees. The qualifying department rule is department <> 'Sales' OR department IS NULL. Group that choice so the phone rule applies to both branches. Show names and dates, then rank the qualifying rows:

SELECT first_name, last_name, hire_date
FROM employees
WHERE (department <> 'Sales' OR department IS NULL)
  AND phone IS NOT NULL
ORDER BY hire_date DESC
LIMIT 3;

Grace Okafor on 2024-02-19 is first, David Kim on 2020-11-23 second, and Farid Haddad on 2017-09-12 third. Removing the IS NULL branch loses Grace and leaves only two results. For an unclear request, settle how missing departments should count and state the choice with the answer.

Combine category, price, and stock

“Which Lighting or Storage products cost less than 20 dollars and are in stock, cheapest first?” Use IN for the two category choices and require a known positive quantity:

SELECT name, price
FROM products
WHERE category IN ('Lighting', 'Storage')
  AND price < 20
  AND stock > 0
ORDER BY price;

Paper Tray at 9.99 comes before Desk Organizer at 18.75. Lamp Shade is cheap enough and in a chosen category, but its stock is zero, so it fails the final test. A missing stock quantity would also fail because it cannot establish that stock is positive.

Check the rows that could expose a mistake

Begin with SELECT * FROM products LIMIT 5; when exploring the schema and a few records. Add filters one at a time, checking changes in the result count and temporarily displaying tested columns when useful.

Test exact boundaries, missing values, text capitalization, and ties at the limit. NOT shows rows where a condition was false; it does not reveal rows where the condition was unknown. Run IS NULL tests separately. Check that your output has the requested columns and that ORDER BY resolves any ties that affect the answer.

When moving the query to another database, check row-limit syntax, equality and LIKE case rules, identifier quoting, date handling, and NULL placement. The examples use LIMIT in SQLite and PostgreSQL; standard SQL's row-limit form is OFFSET with FETCH FIRST.

Key Takeaways

  • Plan the table, qualifying rows, output columns, duplicates, ordering, and row count separately.
  • Write the missing-value choice explicitly when an exclusion should include unassigned records.
  • Check boundaries and individual filter effects against the question.
  • Inspect NULLs separately because NOT does not turn unknown into true.
  • Recheck case, dates, names, NULL ordering, and row limits when changing databases.

SQL SELECT examples

6 Walkthroughs

1

From employee records to a city list

11 steps
Choose result columns, then decide what should count as a duplicate.
  1. 1
    IntroductionOne table, two kinds of answers

    A staff list and a list of staff cities use the same employee records, but they need different result shapes. A basic SELECT chooses the fields to display. DISTINCT can then reduce repeated selected values. We will first build a two-column employee result, then change the question to different cities and trace what disappears.

  2. 2
    ExplanationChoose the displayed columns

    Start with a specific request: show each employee’s city followed by their first name. Write:

    SELECT city, first_name
    FROM employees;
    

    The SELECT list sets the output column order, so city comes first. FROM supplies the table. There is no filter, so all eight employees contribute a row; this plain query leaves the stored records unchanged.

  3. 3
    ExampleRead the result shape

    Ana’s record contributes Chicago and Ana; Ben’s contributes Denver and Ben. Each remaining employee contributes the same two fields. The result therefore has eight rows and two columns. Repeated cities stay because those rows still describe different people. No ORDER BY is present, so these examples identify rows without promising their display order.

  4. 4
    ChallengeCheck a changed SELECT list

    What changes if the query becomes SELECT first_name FROM employees;? State its row count and column count.

    ---reveal---

    It still returns eight rows, now with one column, first_name. Removing city from SELECT removes a displayed field, not employee records. A filter or row limit would be needed to reduce the rows.

  5. 5
    TipKeep syntax errors local

    Separate column names with commas, but leave no trailing comma before FROM. Use a semicolon to end the statement; -- begins a comment to the end of the line. If the database reports an unknown column, compare the spelling with the schema. SELECT * is useful for a first look, while explicit names keep a saved result focused.

  6. 6
    ExplanationAsk for different cities

    Now ask which cities have staff. First names no longer belong in that answer:

    SELECT DISTINCT city
    FROM employees;
    

    The eight city entries reduce to Chicago, Denver, and Boston, in no guaranteed order. DISTINCT removes repeats from the selected result, not from the stored employee table. Each city appears once even though several people may live there.

  7. 7
    ExampleCompare complete selected rows

    Add department to the SELECT list. Ana and Chloe both contribute the pair Sales-Chicago, which collapses to one row. Emma contributes Sales-Denver, which stays because the city differs. The complete SELECT DISTINCT department, city FROM employees; result has seven rows. DISTINCT is written once after SELECT and compares every selected field together.

  8. 8
    ChallengeA unique ID changes duplicate removal

    How many rows would SELECT DISTINCT employee_id, city FROM employees; return, and why?

    ---reveal---

    All eight rows remain. Every employee_id is unique, so each ID-city pair differs from every other pair. Repeated cities alone are insufficient to make complete selected rows duplicate.

  9. 9
    Key InsightDecide what a NULL row says

    A DISTINCT department list contains Sales, IT, Marketing, and one NULL row for Grace. Repeated missing markers would also collapse to one. That row says a department is unrecorded; it is not another named department. Duplicate grouping treats NULLs together even though an ordinary comparison NULL = NULL is unknown.

  10. 10
    ChallengeFinish with a sorted shortlist

    Predict SELECT DISTINCT city FROM employees ORDER BY city LIMIT 2;. Give the values in their returned order and explain what the limit counts.

    ---reveal---

    Boston, then Chicago. DISTINCT first reduces the city values to three; ORDER BY sorts those values and LIMIT keeps two. It counts different result cities after duplicate removal, not two employee records.

  11. 11
    RecapMatch the output to the question

    A staff result keeps identifying fields; a city inventory selects the city alone with DISTINCT. Selected columns determine duplicate comparisons, while ORDER BY determines row order. When changing a query, check both the output fields and what one result row represents.

2

Build a filter that means what you intend

13 steps
Translate thresholds, quoted values, and groups of conditions into row tests.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionEvery filter asks a precise question
  2. 2
    ExplanationTranslate the pay threshold
  3. 3
    ChallengeInclude the boundary when needed
  4. 4
    TipKeep numeric literals plain
  5. 5
    ExplanationQuote a text value
  6. 6
    ExampleKeep an apostrophe inside the value
  7. 7
    ExplanationUse a complete date boundary
  8. 8
    ChallengeCheck a date with a time
  9. 9
    ExplanationJoin the conditions
  10. 10
    ExampleApply one city rule to alternatives
  11. 11
    ChallengeReverse the group
  12. 12
    ChallengeCombine a date and a department group
  13. 13
    RecapTrace a revealing row
3

Choose an exact list, a range, or a pattern

15 steps
Use the matching form that fits the question and check its boundaries.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionThree meanings of a match
  2. 2
    ExplanationStart with whole category choices
  3. 3
    ExampleApply a stock rule to the list
  4. 4
    ChallengePredict an exclusion
  5. 5
    ExplanationPlace a price inside a range
  6. 6
    ChallengeRemove the endpoints deliberately
  7. 7
    TipCover a whole month with times
  8. 8
    ExplanationDecide where the fragment belongs
  9. 9
    ExampleCount one character with an underscore
  10. 10
    ExampleMake the wildcard literal
  11. 11
    ChallengeCheck a pattern at its edges
  12. 12
    ConnectionCheck database case behavior
  13. 13
    ChallengeAccount for a missing phone
  14. 14
    ChallengeCombine all three kinds of matching
  15. 15
    RecapChoose the form from the meaning
4

Find and reason about missing data

10 steps
Distinguish NULL from known values and trace unknown through a filter.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionAn unrecorded quantity is unresolved
  2. 2
    ExampleSeparate zero from missing
  3. 3
    ExplanationTest presence directly
  4. 4
    ChallengeCheck empty text
  5. 5
    ExplanationFollow a comparison and its opposite
  6. 6
    ExampleLet a true OR branch decide
  7. 7
    ChallengeA false AND branch can settle it
  8. 8
    Key InsightAvoid NULL inside an exclusion list
  9. 9
    ChallengeInclude missing departments, require phones
  10. 10
    RecapInspect missing data separately
5

Build a repeatable sorted page

15 steps
Rank qualifying rows, resolve ties, and apply a limit and offset.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionA page is a slice of an ordered answer
  2. 2
    ExampleChoose highest first
  3. 3
    ChallengeSort only qualifying products
  4. 4
    TipKnow whether the sort is numeric
  5. 5
    ExplanationRead sort priorities from the left
  6. 6
    ChallengeGive each sort column its direction
  7. 7
    ExplanationResolve the remaining ties
  8. 8
    ConnectionPlace missing values explicitly
  9. 9
    ExampleKeep the first three ranked rows
  10. 10
    ExampleMove past the first page
  11. 11
    Key InsightA row rank is not a distinct-value rank
  12. 12
    ChallengeInspect a tied cutoff
  13. 13
    TipRecognize another row-limit form
  14. 14
    ChallengeKeep the second city page
  15. 15
    RecapState the ranking before the slice
6

Translate and check a workplace request

12 steps
Build a complete single-table query and audit how each clause changes the answer.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSeparate the decisions in a request
  2. 2
    ExplanationWrite valid clause order
  3. 3
    ExampleTrace the logical model
  4. 4
    ChallengeFilter before choosing a winner
  5. 5
    ExplanationIdentify the staff request
  6. 6
    ExampleMake the department rule explicit
  7. 7
    ExampleApply the phone requirement
  8. 8
    ExampleRank the qualifying records
  9. 9
    ChallengeCheck the missing-value decision
  10. 10
    ChallengeConnect the clauses to one pipeline
  11. 11
    TipChoose checks that expose mistakes
  12. 12
    RecapRead the answer back into the request

SQL SELECT practice questions

50 Practice Questions in 5 sets

Set 1: Lists, ranges, and character patterns10 Practice Questions

Question 1 of 10

A numeric temperature column contains 4, 9, 14, 19, and NULL. What values pass WHERE temperature NOT BETWEEN 9 AND 14?
  1. 4, 19
  2. 4, 9, 14, 19
  3. 4, 19, NULL
  4. 9, 14
Show answerHide answer
Answer: A
NOT BETWEEN keeps values below 9 or above 14, so 4 and 19 pass. The endpoints 9 and 14 are inside the inclusive original range and are excluded here. NULL passes neither form because the comparison is unknown; negation does not make it true.

Question 2 of 10

A promo_code column contains 'VIP_2', 'VIPA2', 'VIP_', and 'VIP2'. What set matches WHERE promo_code LIKE 'VIP!_%' ESCAPE '!'?
  1. {VIP_2, VIPA2, VIP_}
  2. {VIP_2}
  3. {VIP_2, VIP_}
  4. {VIPA2, VIP2}
Show answerHide answer
Answer: C
The escape makes !_ a literal underscore after VIP, and the final percent sign can match zero or more characters. VIP_2 and VIP_ therefore match. VIPA2 would match an unescaped underscore pattern, but it lacks the real underscore required by this one.

Question 3 of 10

A numeric fee column has values 12, 18, 24, and 30. What values pass WHERE fee BETWEEN 18 AND 30?
  1. 24 only
  2. 18, 24
  3. 18, 24, 30
  4. 12, 18, 24
Show answerHide answer
Answer: C
BETWEEN includes both ends of its range, so 18 and 30 qualify along with 24. Keeping only the middle value would treat both endpoints as excluded. The lower value 12 fails because it is below 18, regardless of its position in the table.

Question 4 of 10

What LIKE pattern selects text ending with report?
  1. '%report'
  2. 'report%'
  3. '%report%'
  4. '_report'
Show answerHide answer
Answer: A
A leading percent sign allows any prefix, and nothing follows report in the pattern, so the whole value must end there. Putting percent signs on both sides would also allow a suffix and admit values where report appears in the middle. An underscore allows only one prefix character.

Question 5 of 10

A requests table has (request_id, region) rows (31, 'South'), (32, 'East'), (33, 'South'). What set of IDs passes WHERE region IN ('South', 'South')?
  1. {31, 31, 33, 33}
  2. {31}
  3. {31, 32, 33}
  4. {31, 33}
Show answerHide answer
Answer: D
Each row is tested once for membership in the allowed values, so the two South records pass once each. Repeating South in the list does not duplicate those result rows. The list also supplies no result ordering, so the answer is a set of IDs.

Question 6 of 10

In a labels table, what set of values matches label LIKE '__7%': 'AB7', 'A7', 'AB70', and 'ABC7'?
  1. {AB7, A7, AB70}
  2. {AB7, AB70}
  3. {AB70, ABC7}
  4. {A7, AB7}
Show answerHide answer
Answer: B
The two underscores require exactly two characters before the literal 7. The final percent sign accepts zero characters in AB7 and one in AB70. ABC7 fails because its third character is C, while A7 has only one character before 7.

Question 7 of 10

An appointments table stores time-bearing text in fixed-width YYYY-MM-DD HH:MM:SS form. What condition selects every appointment in February 2028?
  1. starts_at BETWEEN '2028-02-01' AND '2028-02-29'
  2. starts_at >= '2028-02-01' AND starts_at < '2028-03-01'
  3. starts_at > '2028-02-01' AND starts_at > '2028-03-01'
  4. starts_at >= '2028-02-01' AND starts_at <= '2028-03-01'
Show answerHide answer
Answer: B
The lower bound includes the start of February and the strict upper bound stops before March begins. A date-only upper endpoint of February 29 sorts before time-bearing text on that date, so BETWEEN would miss the last day’s appointments. The next-month boundary avoids that problem.

Question 8 of 10

Under SQLite’s default LIKE behavior, what set matches word LIKE 'map%' from 'Maple', 'map', 'roadmap', and 'MAPS'?
  1. {map}
  2. {map, roadmap}
  3. {Maple, map}
  4. {Maple, map, MAPS}
Show answerHide answer
Answer: D
SQLite’s default LIKE ignores ASCII letter case, so all three values beginning with map match. The percent sign may represent no characters, which admits map itself. Roadmap fails because the pattern has no leading wildcard to allow characters before map.

Question 9 of 10

A members table has (member_id, tier) rows (41, 'Gold'), (42, 'Silver'), (43, NULL). What set of IDs passes WHERE tier IN ('Silver', NULL)?
  1. {}
  2. {42, 43}
  3. {41, 42}
  4. {42}
Show answerHide answer
Answer: D
Silver has a real matching list entry, so member 42 passes. A missing tier does not equal the NULL list entry; that comparison is unknown, so member 43 is dropped. IN with a NULL entry can keep known matches, unlike NOT IN, which cannot establish every required inequality.

Question 10 of 10

A shipments table records one port per row. What condition excludes both Oakland and Tacoma when all port values are recorded?
  1. NOT (port = 'Oakland' OR port = 'Tacoma')
  2. NOT port = 'Oakland' OR port = 'Tacoma'
  3. port <> 'Oakland' OR port <> 'Tacoma'
  4. NOT (port <> 'Oakland' AND port <> 'Tacoma')
Show answerHide answer
Answer: A
The grouped OR identifies either unwanted port, and NOT reverses that complete group. Joining the two inequalities with OR would admit every recorded port, because each differs from at least one named port. The intended exclusion requires both inequalities to hold.
Set 2: Columns, comparisons, and clear conditions10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A rooms table has 12 rows. What is the shape of the result of SELECT floor, room_name FROM rooms;?
  1. 2 rows and 12 columns
  2. 12 rows and 2 columns
  3. 12 rows and every column
  4. 2 rows and every column
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A parcels table stores numeric weight_kg. What condition keeps parcels weighing at most 8 kilograms?
  1. weight_kg < 8
  2. weight_kg >= 8
  3. weight_kg > 8
  4. weight_kg <= 8
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What SQL condition matches the exact text value D'Arcy in a surname column?
  1. surname = 'D''Arcy'
  2. surname = 'D'Arcy'
  3. surname = "D'Arcy"
  4. surname = D'Arcy
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A tickets table has status and numeric priority. What condition selects open or pending tickets, each with priority at least 4?
  1. status = 'open' OR status = 'pending' AND priority >= 4
  2. (status = 'open' AND status = 'pending') OR priority >= 4
  3. (status = 'open' OR status = 'pending') AND priority >= 4
  4. (status = 'open' OR status = 'pending') AND priority > 4
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A visits table contains (site, day) pairs ('East', 'Mon'), ('East', 'Mon'), ('East', 'Tue'), and ('West', 'Mon'). How many rows does SELECT DISTINCT site, day FROM visits; return?
  1. 2 rows
  2. 3 rows
  3. 4 rows
  4. 1 row
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A row has team = 'Blue', city = 'Austin', and score = 62. Does it pass WHERE team = 'Blue' OR city = 'Miami' AND score >= 80, and why?
  1. Yes; the true team test is enough for the OR.
  2. No; the score test applies to both locations.
  3. Yes; OR requires both text comparisons to be true.
  4. No; the city test is false and must always pass.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A text column stores full dates as YYYY-MM-DD. What condition selects dates after the calendar year 2025?
  1. event_date > '2025'
  2. event_date > '2025-01-01'
  3. event_date >= '2025-12-31'
  4. event_date >= '2026-01-01'
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What condition selects records whose whole zone value is either North or West?
  1. zone IN 'North', 'West'
  2. zone IN ('North, West')
  3. zone IN ('North', 'West')
  4. zone = 'North' AND zone = 'West'
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A loans table has numeric days_late values 0, 2, 5, and NULL. How many rows pass WHERE days_late <> 0?
  1. 4 rows
  2. 2 rows
  3. 3 rows
  4. 1 row
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A stockrooms table has (room_id, building) rows (1, 'C'), (2, 'A'), (3, 'A'), (4, 'B'), (5, 'C'). What ordered values result from SELECT DISTINCT building FROM stockrooms ORDER BY building DESC LIMIT 2;?
  1. C, C
  2. A, B
  3. B, A
  4. C, B
Show answerHide answer

Save this lesson to see this answer.

Set 3: Missing values and combined filters10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A table has five numeric load values: 0, 3, 7, NULL, NULL. Two queries use WHERE load > 3 and WHERE NOT (load > 3). What are their row counts, in that order?
  1. 1 and 2
  2. 3 and 2
  3. 1 and 4
  4. 2 and 3
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A deliveries table has district values 'Hill', NULL, 'Hill', NULL, and 'Lake'. How many rows does SELECT DISTINCT district FROM deliveries; return?
  1. 4 rows
  2. 2 rows
  3. 3 rows
  4. 5 rows
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A report uses WHERE rating >= 4 and then checks excluded rows with WHERE NOT (rating >= 4). Some records appear in neither result. What additional test reveals records whose rating was not recorded?
  1. WHERE rating < 4
  2. WHERE rating = NULL
  3. WHERE rating IS NULL
  4. WHERE rating <> 4
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A messages table has subjects 'Note A', 'Alert', NULL, and 'Note B'. How many rows pass WHERE subject NOT LIKE 'Note%'?
  1. 2 rows
  2. 4 rows
  3. 3 rows
  4. 1 row
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A SQLite contacts table stores phone values NULL, '', 'NULL', and '555-0188'. How many rows pass WHERE phone IS NULL?
  1. 2 rows
  2. 3 rows
  3. 4 rows
  4. 1 row
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A devices row has battery_pct = NULL and status = 'ready'. What happens under WHERE battery_pct > 50 OR status = 'ready'?
  1. It is dropped because any NULL makes OR unknown.
  2. It is kept because the status branch is true.
  3. It is dropped because both branches must be true.
  4. It is kept because OR always ignores NULL branches.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What condition finds rows with a recorded email value, including empty text if it is stored as a value in PostgreSQL?
  1. email <> NULL
  2. email <> ''
  3. email IS NOT NULL
  4. email = 'NULL'
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A jobs table contains (job_id, hours, urgent) rows (51, NULL, 'yes'), (52, 6, 'no'), (53, 2, 'yes'), (54, NULL, 'no'). What set passes WHERE NOT (hours > 4) OR urgent = 'yes'?
  1. {51, 53}
  2. {53}
  3. {51, 53, 54}
  4. {51, 52, 53}
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A files table has folder values 'Archive', 'Active', and NULL. What condition includes Active and the missing folder, while excluding Archive?
  1. folder <> 'Archive'
  2. folder <> 'Archive' OR folder IS NULL
  3. folder <> 'Archive' AND folder IS NULL
  4. folder NOT IN ('Archive', NULL)
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A routes table has (route_id, depot) rows (61, 'A'), (62, 'B'), (63, NULL). What set of IDs passes WHERE depot NOT IN ('A', NULL) OR depot IS NULL?
  1. {62, 63}
  2. {}
  3. {61, 62, 63}
  4. {63}
Show answerHide answer

Save this lesson to see this answer.

Set 4: Ranking, ties, and later pages10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A numeric duration_minutes column has values 16, 4, and 23. What is their order under ORDER BY duration_minutes?
  1. 23, 16, 4
  2. 16, 23, 4
  3. 4, 16, 23
  4. 16, 4, 23
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A racers table has (racer_id, group_name, points) rows (71, 'A', 8), (72, 'B', 12), (73, 'A', 15). What ID order results from ORDER BY group_name, points DESC?
  1. 73, 71, 72
  2. 73, 72, 71
  3. 72, 73, 71
  4. 71, 73, 72
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A fixed ordered result contains 23 rows. What LIMIT and OFFSET return page 3 with 6 rows per page?
  1. LIMIT 6 OFFSET 3
  2. LIMIT 6 OFFSET 18
  3. LIMIT 3 OFFSET 6
  4. LIMIT 6 OFFSET 12
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A fixed orders table has unique order_id values, and several rows share both order_date and total. What ORDER BY gives newest dates first, highest totals within a date, and a complete order?
  1. ORDER BY order_date DESC, total DESC
  2. ORDER BY order_date DESC, total DESC, order_id
  3. ORDER BY order_id, order_date DESC, total DESC
  4. ORDER BY order_date, total DESC, order_id DESC
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

An offers table has (offer_id, rate) rows (81, 40), (82, 40), (83, 35), (84, 30). What rate results from SELECT DISTINCT rate FROM offers ORDER BY rate DESC LIMIT 1 OFFSET 1;?
  1. 40
  2. 35
  3. 30
  4. No rate
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A queues table contains names 'Delta', NULL, and 'Alpha'. Under the default ORDER BY queue_name ASC, where is the NULL row in SQLite and PostgreSQL?
  1. First in SQLite; first in PostgreSQL.
  2. Last in SQLite; first in PostgreSQL.
  3. First in SQLite; last in PostgreSQL.
  4. Last in SQLite; last in PostgreSQL.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A sales table has (sale_id, amount) rows (91, 70), (92, 70), (93, 55), (94, 55), (95, 20). In PostgreSQL, how many rows does SELECT sale_id, amount FROM sales ORDER BY amount DESC FETCH FIRST 3 ROWS WITH TIES; return?
  1. 4 rows
  2. 2 rows
  3. 5 rows
  4. 3 rows
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A SQLite text column stores the values '6', '12', and '100' under binary collation. What is their ascending order?
  1. 6, 12, 100
  2. 100, 6, 12
  3. 12, 100, 6
  4. 100, 12, 6
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A page query uses ORDER BY item_id LIMIT 4 OFFSET 4 on unchanged rows with unique IDs 1 through 12. Before the next page query, row 2 is deleted. What IDs does LIMIT 4 OFFSET 8 now return under the same sort?
  1. 9, 10, 11, 12
  2. 8, 9, 10, 11
  3. 10, 11, 12
  4. 9, 10, 11
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A tasks table has (task_id, owner, hours) rows (101, 'Lee', 9), (102, 'Ari', 8), (103, 'Lee', 5), (104, 'Lee', 3). What ID results from SELECT task_id FROM tasks WHERE owner = 'Lee' ORDER BY hours DESC LIMIT 1 OFFSET 1;?
  1. 103
  2. 102
  3. 101
  4. 104
Show answerHide answer

Save this lesson to see this answer.

Set 5: Complete queries and result checks10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What is the written order of clauses for a table query with a filter, a sort, and a row limit?
  1. SELECT, FROM, ORDER BY, WHERE, LIMIT
  2. FROM, SELECT, WHERE, ORDER BY, LIMIT
  3. SELECT, FROM, WHERE, LIMIT, ORDER BY
  4. SELECT, FROM, WHERE, ORDER BY, LIMIT
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A courses table has title, numeric fee, and numeric places_left. What query shows the titles and fees of courses under 75 dollars with available places, cheapest first?
  1. SELECT title, fee FROM courses WHERE fee < 75 AND places_left > 0 ORDER BY fee;
  2. SELECT title, fee FROM courses WHERE fee <= 75 AND places_left > 0 ORDER BY fee;
  3. SELECT title, fee FROM courses WHERE fee < 75 OR places_left > 0 ORDER BY fee;
  4. SELECT title, fee FROM courses WHERE fee < 75 AND places_left > 0 ORDER BY fee DESC;
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A cases table has (case_id, team, opened_on, phone) rows (111, 'Red', '2026-06-12', 'x'), (112, NULL, '2026-06-20', 'y'), (113, 'Blue', '2026-06-18', NULL), (114, 'Blue', '2026-06-10', 'z'). What ordered IDs give the two newest cases outside Red, including unassigned teams, with a recorded phone?
  1. 113, 114
  2. 112, 114
  3. 112, 113
  4. 114 only
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A rentals row has branch = 'East' and returned = 'yes'. A query should show unreturned rentals from East or West, but this row appears. The filter is branch = 'East' OR branch = 'West' AND returned = 'no'. What correction applies the return rule to both branches?
  1. branch = 'East' AND branch = 'West' AND returned = 'no'
  2. branch = 'East' OR (branch = 'West' AND returned = 'no')
  3. (branch = 'East' OR branch = 'West') AND returned = 'no'
  4. (branch = 'East' OR branch = 'West') OR returned = 'no'
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A catalog table has ASCII product names. What condition finds names containing steel without ASCII case differences in both SQLite and PostgreSQL?
  1. LOWER(name) LIKE '%steel%'
  2. name LIKE 'steel%'
  3. LOWER(name) LIKE 'steel%'
  4. name = '%steel%'
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A SQLite text column has codes 'G', 'G1', 'H2', 'J', and 'J1' under binary collation. What set passes WHERE code BETWEEN 'G' AND 'J' AND code NOT LIKE 'H%'?
  1. {G, H2, J}
  2. {G, G1, J, J1}
  3. {G1, J1}
  4. {G, G1, J}
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A logs table contains (region, channel) rows ('East', 'web'), ('East', 'web'), ('East', 'store'), ('West', 'web'), ('West', 'store'). What ordered rows result from SELECT DISTINCT region, channel FROM logs WHERE channel = 'web' ORDER BY region DESC LIMIT 1;?
  1. West-web, East-web
  2. East-web
  3. West-web
  4. West-store
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

In PostgreSQL, a bids table has (bid_id, amount) rows (121, 90), (122, 90), (123, 80), (124, 80). How many rows does SELECT bid_id, amount FROM bids ORDER BY amount DESC, bid_id FETCH FIRST 3 ROWS WITH TIES; return?
  1. 4 rows
  2. 3 rows
  3. 2 rows
  4. 1 row
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A sensors table contains (sensor_id, reading, enabled) rows (131, NULL, 'yes'), (132, 8, 'yes'), (133, 2, 'no'), (134, NULL, 'no'). What set passes WHERE NOT (reading > 5 AND enabled = 'yes')?
  1. {131, 133, 134}
  2. {133}
  3. {131, 133}
  4. {133, 134}
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A report is meant to include fees of at least 50. Its filter is fee > 50, and the only available test records have fees 20 and 80. What extra test fee most directly exposes the error?
  1. 49
  2. 50
  3. 51
  4. 100
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

61 Key Terms, each with its definition

A

AND
AND is a logical operator that requires both joined conditions to be true for the complete condition to pass a WHERE filter.
The condition Sales AND Chicago keeps Sales staff in Chicago but excludes a Sales employee whose recorded city is Denver.
ASC
ASC specifies ascending order for a sort column, such as smallest numeric value or earliest date first; it is the default direction.
Sorting Lighting products by price ASC puts the cheapest lighting product first and the most expensive last.

B

BETWEEN
BETWEEN is a range test requiring a value to be at least the lower endpoint and at most the upper endpoint, including both.
price BETWEEN 15.50 AND 89.00 includes products priced at exactly 15.50 or 89.00, the two named endpoints.
Boundary check
A boundary check tests values exactly at, just below, or just above a threshold to verify that the query matches the requested inclusion.
A boundary check using a salary of exactly 60000 distinguishes “at least 60000” from a strict greater-than filter.

C

Clause order
Clause order is SQL’s required written sequence for parts of a statement, including WHERE before ORDER BY and row limiting afterward.
The required clause order puts WHERE before ORDER BY, so reversing them produces a syntax error.
Collation
A collation is the set of rules used to compare and order text, including how uppercase, lowercase, and accented characters behave.
A case-sensitive collation distinguishes 'Chicago' from 'chicago', so a filter for 'chicago' misses rows stored as Chicago.
Comparison operator
A comparison operator tests the relationship between two values, such as equality or which is larger, and produces a truth state.
The comparison operator >= includes an employee paid exactly 60000 in salary >= 60000 because that pay equals the threshold.
Complete sort order
A complete sort order distinguishes every result row by its sort values, giving repeatable row positions while the data stays unchanged.
The complete sort order ORDER BY city, employee_id settles every employee’s position through the unique ID.

D

DATE type
A DATE type stores calendar dates as date values, allowing a database to compare them by date instead of local text formatting.
The PostgreSQL DATE type accepts DATE '2022-01-01'; SQLite does not use this typed-literal syntax.
DESC
DESC specifies descending order for its sort column, reversing ascending order to put larger numbers or more recent dates first.
ORDER BY hire_date DESC puts an employee hired in 2024 before one hired in 2023 in the employee list.
DISTINCT
DISTINCT is a SELECT modifier that removes duplicate complete rows from the selected result while leaving stored records unchanged.
When eight employees work in three cities, SELECT DISTINCT city FROM employees; lists just those three cities.
Duplicate result row
A duplicate result row is one whose selected values all match another result row under the applicable duplicate-comparison rules.
Two employees with the same department and city create a duplicate result row in a department-city list, so DISTINCT keeps their pair once.

E

Empty text
Empty text is a known string with zero characters, written as two adjacent single quotes; most databases distinguish it from NULL.
In SQLite, phone = '' checks for empty text and does not find employee phone values that are missing.
Endpoint
An endpoint is a boundary defining the start or end of a range; whether it is included depends on the chosen comparison.
Each endpoint price in BETWEEN 15.50 AND 89.00 is included, so products priced at exactly 15.50 or 89.00 both qualify.
ESCAPE clause
An ESCAPE clause declares a character that makes a following wildcard literal inside a LIKE pattern, allowing real % or _ searches.
The ESCAPE clause in LIKE 'SPRING!_%' ESCAPE '!' makes the underscore after SPRING a literal character.

F

FROM clause
The FROM clause identifies the table supplying the records for a basic query, so the selected columns can be read from it.
The FROM clause in SELECT name, price FROM products; identifies products as the source of both fields.

H

Half-open date range
A half-open date range includes its starting boundary and excludes its ending boundary, often the start of the next day or month.
A half-open date range for March stops before '2024-04-01', preserving late times on the final day.

I

Identifier
An identifier is a name for a database object, such as a table or column; its spelling and quoting must follow database rules.
The identifier hire_date names a column, while the quoted text '2024-02-19' represents a value.
ILIKE
ILIKE is PostgreSQL’s case-insensitive pattern-matching operator; it offers a database-specific alternative to ordinary LIKE.
A PostgreSQL search with name ILIKE '%desk%' can match product names containing the capitalized text Desk.
IN
IN is a membership test that checks whether a whole value equals any entry in a parenthesized list of allowed values.
department IN ('IT', 'Marketing') selects every IT or Marketing employee without repeating a department comparison.
IN list
An IN list is a comma-separated collection of possible matches inside parentheses; its entry order does not order query results.
An IN list of (6, 2, 2, 4) matches the same employee records as (2, 4, 6), despite its order and repetition.
Inclusive threshold
An inclusive threshold admits a value exactly equal to its boundary; the comparison operators >= and <= express that inclusion.
An inclusive threshold of 60000 keeps an employee whose salary is exactly 60000 when the filter uses salary >= 60000.
Incremental query building
Incremental query building adds and checks one condition at a time, making it easier to explain which requirement removes each row.
Incremental query building adds a phone test after a department test to show which employees the phone test removes.
IS NOT DISTINCT FROM
IS NOT DISTINCT FROM is a comparison that treats two NULLs as equal, while comparing non-NULL inputs like ordinary equality.
SQLite and PostgreSQL offer IS NOT DISTINCT FROM when a comparison should treat two missing markers as equal.
IS NOT NULL
IS NOT NULL is a direct test that is true when a scalar column holds a present value, including empty text where it is distinct from NULL.
The filter phone IS NOT NULL keeps an employee whose phone is recorded even when that employee’s department is missing.
IS NULL
IS NULL is a direct test for a missing scalar value; unlike equality with NULL, it gives a definite true or false result.
phone IS NULL finds the employees whose records have no phone value recorded.
ISO date form
The ISO date form YYYY-MM-DD writes the year before fixed-width month and day parts, allowing date text to sort chronologically.
Using the ISO date form, hire_date >= '2023-01-01' includes employees hired on or after that date in calendar order comparisons.

L

LIKE
LIKE is a text-matching operator that checks a whole value against a pattern containing literal characters and optional wildcards.
name LIKE '%Lamp%' finds every product name containing Lamp, whether Lamp occurs at the start, middle, or end.
LIMIT
LIMIT is a row-count clause that returns at most the specified number of result rows, without choosing a ranking on its own.
A Boston employee filter with LIMIT 5 returns just two rows when only two Boston employees qualify.
Logical processing order
Logical processing order is the model that explains a query’s result by reading, filtering, selecting, deduplicating, sorting, and slicing.
The logical processing order removes repeated cities before a LIMIT counts rows in a DISTINCT city result.
Lower bound
A lower bound is the value at the bottom of a range, placed before AND in ordinary BETWEEN; reversing the bounds does not swap them.
The lower bound 89.00 in price BETWEEN 89.00 AND 15.50 exceeds the upper bound, leaving no matches.

N

NOT
NOT is a logical operator that changes true to false and false to true; it leaves an unknown condition unknown in SQL.
Negating stock > 10 does not include a product with missing stock, because that row still gives an unknown condition.
NOT BETWEEN
NOT BETWEEN is the negation of an inclusive range test; for a non-NULL value, it keeps values below the low end or above the high end.
stock NOT BETWEEN 5 AND 40 keeps a product with zero stock and one with sixty, but excludes missing stock.
NOT IN
NOT IN is an exclusion test requiring a value to differ from every listed item; a NULL in the list prevents a WHERE match.
city NOT IN ('Chicago', 'Denver') keeps Boston staff because their cities differ from both entries.
NULL
NULL is a marker for a missing value, such as data that is unknown, not recorded, or inapplicable, and is distinct from a known zero.
When a product has NULL stock, its row says nothing about the number of units currently in the warehouse.
NULLS LAST
NULLS LAST is an ORDER BY modifier that explicitly places missing values after recorded values for its associated sort column.
department NULLS LAST puts employees with no recorded department after recorded departments in current SQLite and PostgreSQL queries.
Numeric literal
A numeric literal is a number written directly in SQL using digits and, when needed, a decimal point, without currency notation.
The numeric literal 20.50 in price < 20.50 has no quotes, thousands separator, or currency sign.

O

OFFSET
OFFSET is a row-skip clause that discards a specified number of result rows before the row limit counts which rows to return.
With descending salaries, LIMIT 3 OFFSET 3 skips the top three earners and returns the fourth, fifth, and sixth.
Operator precedence
Operator precedence is the priority for evaluating mixed operators without parentheses; among logical operators, NOT precedes AND, then OR.
Operator precedence makes parentheses necessary when a city requirement must apply to each of two department alternatives joined by OR.
OR
OR is a logical operator whose condition is true when at least one joined condition is true, including when both are true.
Sales OR Chicago admits Sales staff in other cities, Chicago staff in other departments, and staff matching both.
ORDER BY
ORDER BY is the clause that arranges result rows by specified sort values; without it, SQL does not guarantee row order.
ORDER BY salary DESC places the highest-paid employee first without changing any stored employee record.

P

Page
A page is one slice of a query result; with n rows per page, page k skips the rows in the k − 1 complete pages before it.
With three rows per page, the third page uses OFFSET 6 and shows the rows after the sixth in a fixed salary ranking.
Percent wildcard
The percent wildcard is the % character inside a LIKE pattern, where it represents any sequence of zero or more characters.
The percent wildcard in 'Lamp%' permits any suffix after Lamp, including no suffix for the value Lamp.
Primary sort column
A primary sort column is the first ORDER BY field, which controls the main ordering before later columns resolve any equal values.
City is the primary sort column in ORDER BY city, last_name, keeping Boston’s names ahead of Chicago’s.

Q

Query
A query is a request for data from a database, written as a statement that specifies what records or values should be returned.
The query SELECT first_name FROM employees; asks the database for the first-name field of each employee.
Query optimizer
A query optimizer chooses an internal execution plan that can differ from the logical model while preserving the query’s defined result.
A query optimizer can find matches without physically processing the whole table in the logical teaching order.

R

Result check
A result check compares a query’s fields, qualifying rows, order, and row count with the request to catch a plausible but wrong answer.
A result check on a product with zero stock explains why it is absent from a list of cheap, in-stock products.
Result set
A result set is the table of rows and columns returned by a query; it can contain fewer fields or records than its source.
For a table of eight employees, the result set from SELECT city FROM employees; has eight rows, with repeated city values allowed.

S

SELECT clause
The SELECT clause names the columns to display in a query result and sets the order in which those output columns appear.
The SELECT clause in SELECT city, first_name FROM employees; puts city before the employee’s first name.
Selected combination
A selected combination is the group of values appearing together in one result row; adding a column can change its uniqueness.
Adding employee_id makes each selected combination in a city result different because the IDs are unique.
Sort column
A sort column is a field named in ORDER BY to determine result-row order; it need not be displayed in an ordinary SELECT query.
Salary can be the sort column when the output shows first_name alone, producing a ranked list of names.
SQL comment
A SQL comment is explanatory text ignored when a statement runs; two hyphens begin a comment lasting to the end of the line.
An SQL comment after -- can explain why a filter includes unassigned staff without changing its conditions.
SQL dialect
A SQL dialect is a database system’s version of SQL syntax and behavior, including differences in row limits, text matching, and quoting.
When the SQL dialect changes from SQLite to SQL Server, a LIMIT query needs TOP or OFFSET/FETCH instead.

T

Text literal
A text literal is a value written directly inside single quotes in SQL; an apostrophe inside the value is doubled to preserve it.
The text literal 'O''Neill' matches the surname O’Neill because its doubled quote represents one apostrophe.
Three-valued logic
Three-valued logic is SQL’s system for combining true, false, and unknown conditions using operators such as AND, OR, and NOT.
Under three-valued logic, a product with missing stock passes a stock OR price test when its price > 100 branch is true.
Tie-breaker
A tie-breaker is a later sort column consulted when earlier sort values are equal; a unique final value makes the order complete.
Adding employee_id as a tie-breaker after city decides which of several Chicago employees fills a shortlist’s last place.
TOP
TOP is SQL Server’s SELECT-level row-limit syntax, used with ORDER BY when a request needs a particular highest or first set of rows.
SELECT TOP (3) can replace a three-row LIMIT shortlist when the query is written for SQL Server.

U

Underscore wildcard
The underscore wildcard is the _ character inside a LIKE pattern, where it represents exactly one character at that position.
Four copies of the underscore wildcard match first names such as Emma and Hugo because each has four characters.
Unknown truth value
Unknown is SQL’s third truth state, produced when missing data prevents a definite comparison; WHERE drops rows in this state.
A product’s missing stock produces an unknown truth value for stock > 10, so that row fails the filter.

W

WHERE clause
The WHERE clause filters source records by a condition and keeps only those rows for which that condition evaluates to true.
The WHERE clause WHERE salary < 50000 keeps employees paid under 50000, even when the output hides their salaries.
WITH TIES
WITH TIES extends a FETCH row limit to include extra rows equal to the cutoff row on every ORDER BY value, where supported.
With ORDER BY city, FETCH FIRST 3 ROWS WITH TIES returns every row sharing the third row’s city, not just three rows.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started