Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

SQL (PostgreSQL, MySQL, SQLite) Module 2 – Filtering and sorting rows

WHERE, comparison and logical operators

Filter rows with SQL WHERE: comparison operators, AND, OR and NOT with brackets, BETWEEN and IN, and how PostgreSQL, MySQL and SQLite compare text.

  • Beginner
  • 20 minutes
  • Examples run with sql.js 1.14.2
  • By MySmartCoPilot

What you will learn

  • Filter rows with WHERE using comparison operators
  • Combine conditions with AND, OR, NOT and parentheses
  • Use BETWEEN and IN for ranges and lists

Before you start

On this page

A table usually holds many more rows than one question needs. WHERE is the filter that throws the rest away: you write a condition, the database checks it against every row of the table, and only the rows for which it comes out true carry on to the rest of the query. The columns you list after SELECT, and everything else in this module, work on the rows that are left.

How WHERE picks rows

Think of the condition as a question asked once per row, with that row’s values filled in. “Is total_paise at least 100000?” is true for some orders and false for the others; the true ones are kept. The condition can use any column of the table, even one you do not show in the output.

The examples on this page use eight orders of a small online grocery. The first example creates the whole table; the others create the same orders again with only the columns they need, because every example runs in a fresh, empty database.

Two filters on the same orders SQL · comparisons.sql
-- Orders of a small online grocery. Money is stored as whole paise
-- (100 paise = 1 rupee), so 100000 means 1,000 rupees.
CREATE TABLE orders (
  id          INTEGER PRIMARY KEY,
  customer    TEXT    NOT NULL,
  city        TEXT    NOT NULL,
  status      TEXT    NOT NULL,
  placed_at   TEXT    NOT NULL,  -- 'YYYY-MM-DD HH:MM'
  total_paise INTEGER NOT NULL
);
INSERT INTO orders VALUES
  (101, 'Ishaan',   'Pune',    'paid',      '2026-02-27 09:15',  84000),
  (102, 'Revathi',  'Chennai', 'shipped',   '2026-03-01 11:40', 125000),
  (103, 'Gurpreet', 'Pune',    'shipped',   '2026-03-09 18:05',  46000),
  (104, 'Ananya',   'Kolkata', 'cancelled', '2026-03-14 07:30', 210000),
  (105, 'Tanvir',   'Pune',    'paid',      '2026-03-20 21:10',  99000),
  (106, 'Lakshmi',  'Chennai', 'refunded',  '2026-03-27 13:00',  38000),
  (107, 'Arjun',    'Indore',  'paid',      '2026-03-31 19:45', 150000),
  (108, 'Neha',     'Pune',    'delivered', '2026-04-02 10:20',  72000);

-- Orders of 1,000 rupees or more.
SELECT id, customer, total_paise
FROM orders
WHERE total_paise >= 100000;

-- Every order that was not cancelled.
SELECT id, status
FROM orders
WHERE status <> 'cancelled';

Output

┌─────┬──────────┬─────────────┐
│ id  │ customer │ total_paise │
├─────┼──────────┼─────────────┤
│ 102 │ Revathi  │ 125000      │
│ 104 │ Ananya   │ 210000      │
│ 107 │ Arjun    │ 150000      │
└─────┴──────────┴─────────────┘
┌─────┬───────────┐
│ id  │  status   │
├─────┼───────────┤
│ 101 │ paid      │
│ 102 │ shipped   │
│ 103 │ shipped   │
│ 105 │ paid      │
│ 106 │ refunded  │
│ 107 │ paid      │
│ 108 │ delivered │
└─────┴───────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < comparisons.sql

The first query keeps the three orders worth 1,000 rupees or more; the second keeps everything except the cancelled order. Rows come back here in the order they were inserted, but that is a habit of this small table, not a promise: only ORDER BY, later in this module, fixes the order of a result.

Operator Is true when the left side is … Example
= equal to the right side status = 'paid'
<> or != not equal to it status <> 'cancelled'
< and <= smaller (or smaller or equal) total_paise < 50000
> and >= larger (or larger or equal) total_paise >= 100000

Text values go in single quotes, numbers do not. Comparing text with < and > works too: SQLite compares the two strings from the left, character by character, by their character codes, which is why a later section stores dates in a particular way.

Text comparisons and letter case

Is 'Pune' equal to 'pune'? It depends on the engine, and this is one of the first places where the three engines in this track disagree:

The same city in different letter case SQL · letter_case.sql
CREATE TABLE orders (id INTEGER PRIMARY KEY, city TEXT NOT NULL);
INSERT INTO orders VALUES
  (101, 'Pune'), (102, 'Chennai'), (103, 'Pune'), (104, 'Kolkata'),
  (105, 'Pune'), (106, 'Chennai'), (107, 'Indore'), (108, 'Pune');

-- count(*) counts the rows that pass the WHERE clause.
SELECT count(*) AS same_case FROM orders WHERE city = 'Pune';
SELECT count(*) AS lower_case FROM orders WHERE city = 'pune';
SELECT count(*) AS both_lowered FROM orders WHERE lower(city) = 'pune';

Output

┌───────────┐
│ same_case │
├───────────┤
│ 4         │
└───────────┘
┌────────────┐
│ lower_case │
├────────────┤
│ 0          │
└────────────┘
┌──────────────┐
│ both_lowered │
├──────────────┤
│ 4            │
└──────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < letter_case.sql

  • SQLite compares text with its default BINARY collation, byte for byte, so 'pune' matches nothing here.
  • PostgreSQL behaves the same way with its default collations, which treat two strings as equal only when they are made of the same bytes.
  • MySQL uses the collation utf8mb4_0900_ai_ci unless told otherwise. The ci stands for case-insensitive (and ai for accent-insensitive), so in MySQL city = 'pune' would find all four Pune orders.

To get the same answer everywhere, compare a lowered copy of the column, as the third query does with lower(city). In SQLite lower() only changes the 26 letters of the English alphabet, which is enough for these city names but not for accented letters. Collations get a lesson of their own in the text-search module.

Combining conditions with AND, OR and NOT

One comparison is rarely enough. Three logical operators join them:

  • a AND b is true only when both sides are true.
  • a OR b is true when at least one side is true.
  • NOT a turns true into false and false into true.

When a condition mixes them, SQL does not read it from left to right. NOT is applied first, then AND, and OR last, in PostgreSQL, MySQL and SQLite alike. So a OR b AND c means a OR (b AND c), in the same way that 1 + 2 * 3 means 1 + (2 * 3).

That rule causes a classic bug. Suppose you want the paid or shipped orders from Pune and write the three conditions in the order you think of them:

A condition that mixes AND and OR SQL · precedence.sql
CREATE TABLE orders (id INTEGER PRIMARY KEY, status TEXT NOT NULL, city TEXT NOT NULL);
INSERT INTO orders VALUES
  (101, 'paid', 'Pune'),      (102, 'shipped', 'Chennai'),
  (103, 'shipped', 'Pune'),   (104, 'cancelled', 'Kolkata'),
  (105, 'paid', 'Pune'),      (106, 'refunded', 'Chennai'),
  (107, 'paid', 'Indore'),    (108, 'delivered', 'Pune');

-- Wanted: paid or shipped orders from Pune.
-- Without parentheses, AND is applied first.
SELECT id, status, city
FROM orders
WHERE status = 'paid' OR status = 'shipped' AND city = 'Pune';

-- With parentheses the OR is decided first.
SELECT id, status, city
FROM orders
WHERE (status = 'paid' OR status = 'shipped') AND city = 'Pune';

Output

┌─────┬─────────┬────────┐
│ id  │ status  │  city  │
├─────┼─────────┼────────┤
│ 101 │ paid    │ Pune   │
│ 103 │ shipped │ Pune   │
│ 105 │ paid    │ Pune   │
│ 107 │ paid    │ Indore │
└─────┴─────────┴────────┘
┌─────┬─────────┬──────┐
│ id  │ status  │ city │
├─────┼─────────┼──────┤
│ 101 │ paid    │ Pune │
│ 103 │ shipped │ Pune │
│ 105 │ paid    │ Pune │
└─────┴─────────┴──────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < precedence.sql

The first query returns order 107 from Indore. Because AND was grouped first, the query asked for “paid orders anywhere, or shipped orders from Pune”. The brackets in the second query say what was meant:

Two expression trees: without brackets OR joins status = 'paid' with an AND; with brackets AND joins an OR with city = 'Pune'.Without bracketsWith brackets around the ORORstatus ='paid'ANDstatus ='shipped'city ='Pune'ANDORstatus ='paid'status ='shipped'city ='Pune'add ( )

How SQL groups a condition that mixes AND and OR

Text description of the diagram

The diagram shows two trees, one above the other.

  1. Without brackets, the condition status = 'paid' OR status = 'shipped' AND city = 'Pune' is grouped with AND first. The top of the tree is OR. Its left branch is status = 'paid' on its own; its right branch is an AND of status = 'shipped' and city = 'Pune'. A paid order therefore passes whatever its city is.
  2. With brackets around the OR, the top of the tree is AND. Its left branch is the OR of status = 'paid' and status = 'shipped'; its right branch is city = 'Pune'. Every row that passes has to be from Pune.

An arrow from the first tree to the second is labelled: add brackets around the OR.

Whenever a condition contains both AND and OR, write the brackets, even where the grouping happens to be right without them. The next person who reads the query should not have to remember the precedence table.

Ranges with BETWEEN, lists with IN

Two shorthands save you from long chains of comparisons:

  • x BETWEEN low AND high means x >= low AND x <= high. Both ends belong to the range, and the smaller value must come first. NOT BETWEEN keeps the values outside it.
  • x IN (v1, v2, v3) means x = v1 OR x = v2 OR x = v3, and x NOT IN (…) means that x differs from every value in the list.
BETWEEN and NOT IN on the orders SQL · ranges_and_lists.sql
CREATE TABLE orders (id INTEGER PRIMARY KEY, status TEXT NOT NULL, total_paise INTEGER NOT NULL);
INSERT INTO orders VALUES
  (101, 'paid', 84000),      (102, 'shipped', 125000),
  (103, 'shipped', 46000),   (104, 'cancelled', 210000),
  (105, 'paid', 99000),      (106, 'refunded', 38000),
  (107, 'paid', 150000),     (108, 'delivered', 72000);

-- Both ends are included: 46000 and 99000 match.
SELECT id, total_paise
FROM orders
WHERE total_paise BETWEEN 46000 AND 99000;

-- The smaller value must come first, or nothing matches.
SELECT count(*) AS reversed_bounds
FROM orders
WHERE total_paise BETWEEN 99000 AND 46000;

-- A list of values instead of a chain of ORs.
SELECT id, status
FROM orders
WHERE status NOT IN ('cancelled', 'refunded');

Output

┌─────┬─────────────┐
│ id  │ total_paise │
├─────┼─────────────┤
│ 101 │ 84000       │
│ 103 │ 46000       │
│ 105 │ 99000       │
│ 108 │ 72000       │
└─────┴─────────────┘
┌─────────────────┐
│ reversed_bounds │
├─────────────────┤
│ 0               │
└─────────────────┘
┌─────┬───────────┐
│ id  │  status   │
├─────┼───────────┤
│ 101 │ paid      │
│ 102 │ shipped   │
│ 103 │ shipped   │
│ 105 │ paid      │
│ 107 │ paid      │
│ 108 │ delivered │
└─────┴───────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < ranges_and_lists.sql

Orders 103 and 105 sit exactly on the two ends of the range and are included. The second query is the edge case worth remembering: with the bounds reversed, no value can be at least 99000 and at most 46000 at the same time, so nothing matches and there is no error to warn you. PostgreSQL also offers BETWEEN SYMMETRIC, which swaps the two ends when they are the wrong way round. The quickest check of the rules is a query without any table:

BETWEEN at its edges SQL · between_edges.sql
SELECT 5 BETWEEN 1 AND 5     AS edge,
       5 BETWEEN 5 AND 1     AS swap,
       5 NOT BETWEEN 1 AND 4 AS out;

Output

┌──────┬──────┬─────┐
│ edge │ swap │ out │
├──────┼──────┼─────┤
│ 1    │ 0    │ 1   │
└──────┴──────┴─────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < between_edges.sql

SQLite shows true as 1 and false as 0; the last section explains why.

NOT IN has a trap of its own when the list contains a missing value (NULL): it then returns no rows at all. The next lessons explain NULL and why that happens.

Dates stored as text

SQLite has no separate date type. The usual practice, which this track follows, is to store a date or a time as text in the ISO 8601 order of year, month, day, hour and minute, such as '2026-03-09 18:05'. Text in that order sorts and compares in time order, because the most significant part comes first. Day-first text does not:

Comparing dates written as text SQL · dates_as_text.sql
-- Text is compared character by character, from the left.
SELECT '2026-03-05' < '2026-12-24' AS year_first,
       '05-03-2026' < '24-12-2025' AS day_first;

CREATE TABLE orders (id INTEGER PRIMARY KEY, placed_at TEXT NOT NULL);
INSERT INTO orders VALUES
  (101, '2026-02-27 09:15'), (102, '2026-03-01 11:40'),
  (103, '2026-03-09 18:05'), (104, '2026-03-14 07:30'),
  (105, '2026-03-20 21:10'), (106, '2026-03-27 13:00'),
  (107, '2026-03-31 19:45'), (108, '2026-04-02 10:20');

-- Orders placed in March, two ways.
SELECT count(*) AS with_between
FROM orders
WHERE placed_at BETWEEN '2026-03-01' AND '2026-03-31';

SELECT count(*) AS half_open
FROM orders
WHERE placed_at >= '2026-03-01' AND placed_at < '2026-04-01';

Output

┌────────────┬───────────┐
│ year_first │ day_first │
├────────────┼───────────┤
│ 1          │ 1         │
└────────────┴───────────┘
┌──────────────┐
│ with_between │
├──────────────┤
│ 5            │
└──────────────┘
┌───────────┐
│ half_open │
├───────────┤
│ 6         │
└───────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < dates_as_text.sql

The first row says that both comparisons are true. For the day-first strings that is wrong: the first date is more than two months later than the second, yet it compares as smaller, only because the character 0 sorts before 2.

The two counts show the other mistake. Six orders were placed in March, but BETWEEN '2026-03-01' AND '2026-03-31' finds five. The upper end, '2026-03-31', is shorter than '2026-03-31 19:45' and therefore sorts before it, so the evening order of the last day falls outside the range. A half-open range avoids the problem in every engine and for every type of date or timestamp: from the first moment of the period (>=) up to, but not including, the first moment of the next one (<).

PostgreSQL and MySQL have real date and timestamp types, and the dates module shows them. The half-open habit is worth keeping there too.

True and false in each engine

A condition does not have to be a comparison: any expression that is true or false will do, such as a column that records whether a product is in stock.

A yes-or-no column in SQLite SQL · true_and_false.sql
-- SQLite has no separate boolean type: true is 1 and false is 0.
CREATE TABLE products (
  id       INTEGER PRIMARY KEY,
  name     TEXT    NOT NULL,
  in_stock INTEGER NOT NULL  -- 1 = in stock, 0 = sold out
);
INSERT INTO products VALUES
  (1, 'Basmati rice 5 kg', 1),
  (2, 'Filter coffee 500 g', 0),
  (3, 'Toor dal 1 kg', 1);

SELECT name FROM products WHERE in_stock;
SELECT name FROM products WHERE NOT in_stock;
SELECT TRUE AS true_value, FALSE AS false_value, 2 > 1 AS comparison;

Output

┌───────────────────┐
│       name        │
├───────────────────┤
│ Basmati rice 5 kg │
│ Toor dal 1 kg     │
└───────────────────┘
┌─────────────────────┐
│        name         │
├─────────────────────┤
│ Filter coffee 500 g │
└─────────────────────┘
┌────────────┬─────────────┬────────────┐
│ true_value │ false_value │ comparison │
├────────────┼─────────────┼────────────┤
│ 1          │ 0           │ 1          │
└────────────┴─────────────┴────────────┘

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < true_and_false.sql

  • SQLite has no boolean storage class. True is stored as 1 and false as 0, and the keywords TRUE and FALSE are other ways to write those two numbers. In WHERE, any number other than zero counts as true.
  • MySQL works the same way: its BOOLEAN type is another name for TINYINT(1), zero is false and every other number is true.
  • PostgreSQL has a real boolean type, and the condition of a WHERE must be of that type. WHERE in_stock works when in_stock is a boolean column; on an integer column PostgreSQL stops with an error, and you write in_stock = 1 or in_stock <> 0 instead.

Version note

<> and != both mean “not equal” in PostgreSQL, MySQL and SQLite; <> is the spelling of the SQL standard, and PostgreSQL turns != into <> while it parses the query. MySQL also accepts &&, || and ! for AND, OR and NOT, but its 8.4 manual marks all three as deprecated, and in PostgreSQL and SQLite || joins two strings instead. Write AND, OR and NOT. The examples on this page ran with SQLite 3.49.1, the version inside sql.js 1.14.2.

The practice data

The orders and the exercise below come from an Indian online grocery. Amounts are stored as whole numbers of paise (100 paise make one rupee), so 2,000 rupees is 200000; whole numbers avoid the rounding errors of fractional money. The exercise’s payment methods include UPI (Unified Payments Interface).

SQLite Online (SQL Playground) Paste an example into a SQLite database in your browser and change the conditions to see which rows survive.

Key takeaways

  • WHERE checks its condition against every row and keeps the rows for which it is true.
  • NOT is applied before AND, and AND before OR: write brackets whenever AND and OR are mixed.
  • BETWEEN includes both ends and needs the smaller one first; IN and NOT IN compare with a list of values.
  • Text comparisons are case-sensitive in SQLite and PostgreSQL but not under MySQL’s default collation.
  • Store dates as ISO text in SQLite and select periods with half-open ranges (>= start, < next start).

Exercise

Exercise · Easy · SQL

Find the large UPI payments of one month

The table payments has one row per payment received by an online grocery, with these columns:

  • id: the payment's number;
  • paid_at: when it was made, as text such as 2026-03-04 10:00;
  • method: upi, card, netbanking or cod (cash on delivery);
  • state: the Indian state the customer lives in, such as Maharashtra;
  • amount_paise: the amount in paise (100 paise make one rupee).

Write a query that returns the id, state and amount_paise of every payment that meets all of these conditions:

  • it was made with UPI;
  • it is for more than 2,000 rupees (an amount of exactly 2,000 rupees does not count);
  • it was made in the month 2026-03, at any time of its first or last day;
  • the customer lives in Maharashtra or Karnataka.

The order of the rows does not matter. The sample tests run your query on three small tables, including payments at the very start and the very end of the month and payments of exactly 2,000 rupees.

Starter code · upi_payments.sql

-- UPI payments of more than 2,000 rupees (200000 paise), made in 2026-03
-- by customers in Maharashtra or Karnataka.
SELECT id, state, amount_paise
FROM payments;
The sample tests · tests.yaml
# Sample tests: each one builds a small payments table, runs your query on it and compares the rows it returns
# (in any order).
tests:
  - name: keeps UPI payments over 2,000 rupees from the two states only
    setup: |
      CREATE TABLE payments (id INTEGER PRIMARY KEY, paid_at TEXT, method TEXT, state TEXT, amount_paise INTEGER);
      INSERT INTO payments VALUES
        (1, '2026-03-04 10:00', 'upi',        'Maharashtra', 250000),
        (2, '2026-03-05 12:30', 'upi',        'Karnataka',   199999),
        (3, '2026-03-08 09:10', 'card',       'Maharashtra', 300000),
        (4, '2026-03-11 17:45', 'upi',        'Kerala',      260000),
        (5, '2026-03-15 08:00', 'upi',        'Karnataka',   200001),
        (6, '2026-03-21 19:20', 'netbanking', 'Karnataka',   410000);
    expected:
      columns: [id, state, amount_paise]
      rows:
        - [1, Maharashtra, 250000]
        - [5, Karnataka, 200001]
  - name: includes the first and last minutes of the month but not the days around it
    setup: |
      CREATE TABLE payments (id INTEGER PRIMARY KEY, paid_at TEXT, method TEXT, state TEXT, amount_paise INTEGER);
      INSERT INTO payments VALUES
        (1, '2026-03-01 00:00', 'upi', 'Maharashtra', 220000),
        (2, '2026-03-31 23:59', 'upi', 'Karnataka',   500000),
        (3, '2026-04-01 00:00', 'upi', 'Maharashtra', 300000),
        (4, '2026-02-28 23:59', 'upi', 'Karnataka',   300000);
    expected:
      columns: [id, state, amount_paise]
      rows:
        - [1, Maharashtra, 220000]
        - [2, Karnataka, 500000]
  - name: leaves out payments of exactly 2,000 rupees
    setup: |
      CREATE TABLE payments (id INTEGER PRIMARY KEY, paid_at TEXT, method TEXT, state TEXT, amount_paise INTEGER);
      INSERT INTO payments VALUES
        (1, '2026-03-10 11:00', 'upi', 'Maharashtra', 200000),
        (2, '2026-03-12 16:00', 'upi', 'Karnataka',   200000),
        (3, '2026-03-12 16:05', 'upi', 'Karnataka',   200100),
        (4, '2026-03-19 07:30', 'cod', 'Maharashtra', 450000);
    expected:
      columns: [id, state, amount_paise]
      rows:
        - [3, Karnataka, 200100]
A hint

Write one condition for each requirement and join them all with AND. For the two states, state IN (…) avoids an OR that would need brackets. For the month, compare paid_at with a half-open range: >= the first day of the month and < the first day of the next month, so that a payment late on the last day is still inside it.

The sample tests run on this device, in your browser (sql.js): nothing is sent to mysmartcopilot.com. The first run downloads SQLite (about 0.7 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 Which orders does WHERE status = 'paid' OR status = 'shipped' AND city = 'Pune' keep?

    Choose one answer.

    Show the answer to question 1

    Answer: Paid orders from any city, and shipped orders from Pune

    AND is grouped before OR, so the condition reads status = 'paid' OR (status = 'shipped' AND city = 'Pune'). A paid order passes whatever its city is. Brackets around the OR give the query that was probably meant.

  2. Question 2 of 5 What does this query print?

    What does this program print? Choose one answer.

    SELECT 5 BETWEEN 1 AND 5     AS edge,
           5 BETWEEN 5 AND 1     AS swap,
           5 NOT BETWEEN 1 AND 4 AS out;
    Show the answer to question 2

    Answer: it prints

    ┌──────┬──────┬─────┐
    │ edge │ swap │ out │
    ├──────┼──────┼─────┤
    │ 1    │ 0    │ 1   │
    └──────┴──────┴─────┘
    

    BETWEEN includes both ends, so 5 is between 1 and 5 (1, true). With the ends reversed nothing can be at least 5 and at most 1, so the answer is 0 (false), not an error. 5 is not between 1 and 4, so NOT BETWEEN is 1.

  3. Question 3 of 5 Which conditions keep exactly the orders whose status is paid or shipped, when the other statuses are cancelled, refunded and delivered?

    Choose every answer that is right.

    Show the answer to question 3

    Answer:

    • status = 'paid' OR status = 'shipped'
    • NOT (status <> 'paid' AND status <> 'shipped')
    • status IN ('paid', 'shipped')

    IN is shorthand for the chain of ORs, and the NOT (… AND …) form says the same thing the other way round. No row has two statuses at once, so the AND version keeps nothing. BETWEEN compares text alphabetically, and refunded sorts between paid and shipped, so it would slip in.

  4. Question 4 of 5 A table holds one row whose city is Pune. Which condition finds that row in MySQL with its default collation, but not in SQLite or PostgreSQL?

    Choose one answer.

    Show the answer to question 4

    Answer: city = 'pune'

    MySQL's default collation, utf8mb4_0900_ai_ci, is case-insensitive, so 'pune' equals 'Pune' there. SQLite's BINARY collation and PostgreSQL's default collations treat two strings as equal only when their bytes are the same. city = 'Pune' and lower(city) = 'pune' find the row in all three engines; city <> 'Pune' finds it in none.

  5. Question 5 of 5 placed_at holds the date and time of each order as ISO text, year first and then hour and minute. Which orders of the month does this query fail to count?

    Read the code, then choose one answer.

    SELECT count(*) AS march_orders
    FROM orders
    WHERE placed_at BETWEEN '2026-03-01' AND '2026-03-31';
    Show the answer to question 5

    Answer: Every order placed on the last day of the month, whatever its time

    Every value of the last day, even one at 00:00, is longer than the bare date of the upper end and starts with it, so as text it sorts after it and falls outside the range. The first day is fine: its values sort after the lower end. A half-open range, from the first day of the month up to but not including the first day of the next month, counts every order.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.