Course / 04 Code / Lesson 11 of 11

SQL enough to read a query

Estimated time: 15 min reading · 15 min activity

A page can show information that originally came from a table. To follow that flow later, first learn to read one query against a small table. You will predict which names appear in its result without creating a database.

A database stores organized data. A table is a named collection arranged into columns and rows. A column names a kind of fact, such as name or active. A row describes one record. SQL, Structured Query Language, expresses requests to a database. A query here asks for selected values from rows that match a condition.

The examples use SQLite, one database system, and its SELECT statement. SELECT retrieves a result without changing the supplied table. A WHERE clause supplies a condition, also called a predicate, to test against each row. The examples use active values 1 and 0; the query asks to keep 1.

Before you begin

Estimated time
15 min reading, 15 min activity. Setup and optional practice may take longer.
Where you work
Read and trace the supplied source and input in your browser. A later optional run uses Python 3.12 and its standard library in a fresh directory under your own ~/yanosec-lab. The ~ means your home directory. Keep paired files together and avoid overwriting existing work. No extra packages or network calls are required.
Without a lab
Follow each source line against the supplied input and predicted trace. You can complete the same reasoning without running Python; the practical run can wait. Earlier recorded Python results are labeled separately from predictions. Reading works without JavaScript. Answer checking needs JavaScript and a secure context; saving additionally needs usable browser storage.

After this lesson you can:

  • Read a SELECT that keeps rows matching a WHERE predicate.
  • Predict the kept names from a printed table.
  • Separate browser display, file bytes, and table rows.

01Example

Read the worked table first, then the query. FROM identifies members_demo as the input table. WHERE active = 1 keeps lane and paz, and SELECT name returns their name values. This is a paper prediction; no database was opened. The set of names matters here, not their display order.

name | active
lane | 1
om   | 0
paz  | 1

SELECT name FROM members_demo WHERE active = 1;

Kept names: lane and paz.
om is left out because active is 0.
The table is unchanged.

02A filter is not a sentence about the table

Read the clauses in their roles: FROM identifies the table, WHERE filters its rows, and SELECT names the columns returned. The SQL comparison active = 1 uses a single equals sign; Python used == for comparison. Judge punctuation in the language you are reading.

A row that fails the condition remains in the stored table. It is absent from the result, not deleted. Without ORDER BY, which requests a sort order, do not assume that returned rows have a guaranteed order. This activity asks for the set of matching names.

The SQL downloads also show CREATE TABLE, which defines columns, and INSERT, which adds rows. Those statements explain where the printed grid would come from if executed. Reading them changes nothing; executing them would create or change data. Text values in these examples use single quotes.

The optional Python snippet uses its sqlite3 module. connect(":memory:") creates a temporary database in the process’s memory; executescript runs the supplied SQL setup; execute runs the fixed SELECT. The for loop receives result rows, and row[0] selects the first column. These calls are background for the snippet, not requirements for the paper activity. Values from users should be passed with bound placeholders, a way to supply data separately from SQL instructions, rather than joined into SQL text.

Filter active=1, then project name; excluded om remains in the original members_demo table.Model. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

members_demoWHERE active = 1SELECT name FROM members_demo WHERE active = 1;

All supplied source rows.

Immutable supplied teaching corpus

nameactive
lane1
om0
paz1
Selected view

Result

lane  1
om  0
paz  1

The fixed members_demo table contains lane/1, om/0 and paz/1.

Follow the explanation

  1. The fixed members_demo table contains lane/1, om/0 and paz/1.
  2. The WHERE condition retains lane and paz; om remains in the unchanged source table.
  3. SELECT name projects lane and paz without deleting om.
Read every state and value

1. The fixed members_demo table contains lane/1, om/0 and paz/1.

View
source
Rows shown
lane · 1 · om · 0 · paz · 1
Highlighted Cell Refs
None
Result rows
Source Row: lane · 1; Cells: lane · 1 · 1 · Source Row: om · 0; Cells: om · 0 · 0 · Source Row: paz · 1; Cells: paz · 1 · 1

2. The WHERE condition retains lane and paz; om remains in the unchanged source table.

View
where
Rows shown
lane · 1 · paz · 1
Highlighted Cell Refs
Row: lane · 1; Column: active · Row: paz · 1; Column: active
Result rows
Source Row: lane · 1; Cells: lane · 1 · 1 · Source Row: paz · 1; Cells: paz · 1 · 1

3. SELECT name projects lane and paz without deleting om.

View
SELECT name FROM members_demo WHERE active = 1;
Rows shown
lane · 1 · paz · 1
Highlighted Cell Refs
Row: lane · 1; Column: name · Row: paz · 1; Column: name
Result rows
Source Row: lane · 1; Cells: lane · 1 · Source Row: paz · 1; Cells: paz · 1
Filter active=1, then project name; excluded om remains in the original members_demo table.

03Keep the active rows from the members card

Use the activity grid containing ada, bo, and cy. First evaluate the condition by hand; then compare with the SQL source that describes those same rows. The required question later supplies a different pair of names.

YanoSec / Code
Original members table. No database was opened.

column name | column active
ada         | 1
bo          | 0
cy          | 1

Each data line is a row. name and active are columns.
Query to read, not to run here:
SELECT name FROM members WHERE active = 1;
Rows whose active value is 1: ada and cy.
bo has active 0, so the predicate leaves bo out.
Row order is not part of this check. SELECT does not change the table.
Apply the condition independently to every row in a new table.

Download the members grid and the predicted names

CREATE TABLE members (
  name TEXT NOT NULL,
  active INTEGER NOT NULL
);
INSERT INTO members (name, active) VALUES ('ada', 1);
INSERT INTO members (name, active) VALUES ('bo', 0);
INSERT INTO members (name, active) VALUES ('cy', 1);

Download the SQL setup source for the same rows

Optional later snippet, not run:
import sqlite3
connection = sqlite3.connect(":memory:")
connection.executescript(open("members.sql", encoding="utf-8").read())
for row in connection.execute("SELECT name FROM members WHERE active = 1"):
    print(row[0])
Predicted kept names: ada and cy.
Order is not part of the check. Close the process when you stop; there is no server.
  1. In table.txt, read each name with its active value. ada has 1, bo has 0, and cy has 1.
  2. Apply active = 1 separately to each row. Compare with the supplied result set: ada and cy. bo remains stored but does not appear in this result.
  3. Read the INSERT lines in members.sql and match each name and active value with the grid. They describe the same three records.
  4. Stop with a written explanation of the kept rows. No SQLite launch is needed. If you later use the optional snippet, use only these supplied files in a fresh lab directory; its database is in memory and ends with the process.

04Page and table case

Use the stage case to connect representation, JavaScript, and a query. The HTML and SQL are separate files, and the text card supplies both the table and the predicted page change. Make your predictions before comparing with its rubric.

YanoSec / Code
Stage case for representation, a page script, and a table.
Self-review case. The page and query results below are predictions.
A file URL is not HTTP. The SQL file is not part of the HTML file.

Representation:
The displayed word desk-lead uses nine ASCII characters.
UTF-8 stores those nine characters as nine bytes.
Counting characters and counting bytes agree for this ASCII word.
They do not agree for every word, as the Unicode lesson showed.

Page script:
Before the click, #role text is reader.
The click handler sets textContent to desk-lead.
Predicted DOM text after the click: desk-lead.
The script does not open the SQL file and does not send a request.

Table:
name | lane
ria  | a
sol  | b
tez  | a
Query: SELECT name FROM shifts WHERE lane = 'a';
Kept rows: ria and tez. sol is left out.
SELECT does not change the stored rows.

Boundary:
Changing the paragraph does not change the shifts table.
Changing a row would not change the paragraph unless some other,
absent program copied it. This case has no such program.
No server permission is granted by the new label.

Rubric: name the byte count for desk-lead, the DOM text after the click,
the kept names, and the store the click does not modify.

Download the stage case and its rubric

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Desk role</title>
</head>
<body>
  <p id="role">reader</p>
  <button id="promote" type="button">Change the label</button>
  <script>
    document.querySelector("#promote").addEventListener("click", function () {
      document.querySelector("#role").textContent = "desk-lead";
    });
  </script>
</body>
</html>

Download the role-page source

CREATE TABLE shifts (
  name TEXT NOT NULL,
  lane TEXT NOT NULL
);
INSERT INTO shifts (name, lane) VALUES ('ria', 'a');
INSERT INTO shifts (name, lane) VALUES ('sol', 'b');
INSERT INTO shifts (name, lane) VALUES ('tez', 'a');

Download the shifts-table setup source

  1. Count the ASCII characters in desk-lead. Check: 9 characters and 9 UTF-8 bytes. Explain why this agreement depends on the characters being ASCII.
  2. Follow the HTML click handler: it replaces reader with desk-lead in the role paragraph. It neither opens the SQL file nor sends a request.
  3. Use the printed shifts grid and apply WHERE lane = 'a'. Check: ria and tez match; sol does not. SELECT returns matching rows without changing the stored table.
  4. Write an evidence note with the byte count, predicted DOM text, matching names, and the state the click does not change. The paragraph and table remain separate; the new label grants no server permission. Stop without executing either file.

05Stage evidence

Your evidence note should connect each result to its source: count pairs for byte size, apply a declared decoding rule, follow a script’s read and write operations, trace a DOM assignment, and evaluate a table condition. These are related reading skills, but each uses a different kind of evidence. The supplied tables and page traces let you practise them in a browser. Running Python, activating the local page, and executing SQL are separate practical experiences; a correct prediction alone does not demonstrate those operations.

06Optional challenge

Spend about ten extra minutes changing the activity condition in notes to active = 0. The matching set should contain only bo. Explain why ada and cy still remain in the stored table even though this new result leaves them out.

07Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

SQL case A: independent tableOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

members_demo
name | active | team
Fia | 0 | red
Gus | 1 | blue
Hui | 1 | red
Ike | 0 | blue
Query: SELECT name FROM members_demo WHERE active = 0;
No ORDER BY is present. This is a paper prediction.

SQL case B: separate changed filterOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

members_demo
name | active
Jen | 1
Kit | 0
Lou | 0
Query: SELECT name FROM members_demo WHERE active = 1;
Change only the condition to active = 0. No INSERT, UPDATE or DELETE is performed.

08Explain your prediction

Optional written self-check: Explain FROM, WHERE and SELECT separately. Distinguish a result from stored rows, avoid claiming a guaranteed order without ORDER BY, and keep SQL table changes separate from browser display and file bytes.

09Common mistakes

If a nonmatching row appears in your result, write its active value beside the condition and evaluate the comparison again. Check each row independently.

If you describe the absent row as deleted, identify the actual statement. This fixed SELECT retrieves values; it contains no instruction to delete a row.

If the page role is used as a table value, compare the two source files. The case supplies no program that copies the DOM label into the table.

References

  • SQLite: SELECT

    Sections 1, 2.3, and 4 explain result rows, filtering, and explicit ordering.

  • Python 3.12: sqlite3

    Documents in-memory connections, executescript, execute, and bound data values.

Next is the first lesson of Networks.