Query Structure: Beginner
| product_id | product_name | category | price | in_stock |
|---|---|---|---|---|
| 1 | Desk Lamp | lighting | 24.0 | 1 |
| 2 | Standing Desk | furniture | 349.0 | 1 |
| 3 | Monitor Arm | accessories | 89.5 | 0 |
| 4 | Office Chair | furniture | 199.0 | 1 |
| 5 | USB Hub | accessories | 18.75 | 1 |
Rows have no built-in order
Finding your way around several tables
- 1 record: 1 product, 1 order, 1 supplier
- Read across to see every fact about that record
- The count grows every time data is added
- Holds no fixed position in the table
- 1 attribute: price, category, country
- Read down to see 1 fact about every record
- The set is fixed when the table is designed
- Has a name and holds 1 kind of value
In the products table, what does the cell at the row where product_id is 5 and the column price hold?
A query is a question written for the database, and the smallest useful one has 2 parts. FROM names the table to read. SELECT lists the columns to bring back, separated by commas. The database finds the table and walks every row, keeping from each one only the columns on the list. What comes back is a new table that lives for the length of the query: the same rows, only the chosen columns.
Run it and 5 rows come back, 1 per product, each with 2 values: Desk Lamp beside 24.0, Standing Desk beside 349.0, and so on down to USB Hub beside 18.75. The query did not name product_id or category, so they were read off the table and left out before the result was assembled, and in_stock went the same way. The products table is the same afterward as before, because a SELECT reads the table and never edits it, however many times it runs.
| Step | Clause | What the database does |
|---|---|---|
| 1 | FROM products | Opens the table: 5 rows, 5 columns |
| 2 | SELECT product_name, price | Keeps 2 columns from every row |
| 3 | Return | Hands back 5 rows of 2 columns |
Read FROM before SELECT
You write SELECT first and FROM second, and the database works the other way around: it has to locate the table before it can know which columns exist. Read your own queries in the database's order. Start at FROM and picture the table with its column card. Then move up to SELECT and check that every name on the list belongs to that table. A name that does not, such as a misspelled pirce, stops the query with an error saying the column cannot be resolved, before a single row is returned.
The SELECT list sets the column order
The result's columns appear in the order the SELECT list gives them, so price comes first, even though product_id is the first column in the table. The table's own column order has no hold over the result. A column can also be listed twice, which returns it twice side by side; that reads as a mistake today and becomes useful in the last section of this lesson, where 1 copy is turned into a calculation and the other is kept as it is.
Every row comes back, repeats included
Naming 1 column still returns 5 rows: lighting, furniture, accessories, furniture, accessories. furniture appears twice because 2 products belong to it, and the database does not collapse repeats on its own. SELECT narrows the columns, never the rows; the result has as many rows as the table until the filtering lesson adds the tool that changes that. A count of rows in a result is therefore a count of rows in the table, which is a quick sanity check when a result looks too short or too long.
A comma separates every pair of columns, and the last column carries no comma before FROM; a stray comma there is a syntax error. The keywords SELECT and FROM can be typed in any mixture of upper and lower case, so select works exactly like SELECT. Upper case is the convention because it makes the skeleton of a long query visible at a glance: keywords stand out, column names stay quiet. A query may end with a semicolon; tools that run several statements in a row use it as a separator, and a single query runs the same with or without it.
- ▸A table name after FROM that exists in the database
- ▸At least 1 column name after SELECT, spelled as the table spells it
- ▸A comma between every 2 columns and none after the last
- ▸Keywords in any case, upper case by convention
What does this query return when run against the products table?
Writing a query from a request follows the same steps every time. Turn each thing the request asks for into a column name from the table's card, spelled exactly as the card spells it. List those names after SELECT in the order the request wants them, with a comma between each pair and none after the last. Put the table's name after FROM. Then read the query back in the database's order: the table first, then each name on the list checked against that table's columns.
An asterisk in place of the column list means every column of the table, in the order the table defines them. SELECT * FROM products returns all 5 columns and all 5 rows, the entire table. It is the quickest way to see what a table you have never opened holds, before you know a single column name, and the result doubles as the column card from the first section with real values filled in.
- Run SELECT * on it once and read the column headers and a few values
- Decide which columns the question in front of you needs
- Rewrite the query with those columns named, and keep that version
Where the star costs you
A report runs this query every morning and pastes the result into a spreadsheet laid out for 5 columns. Next week a weight_kg column is added to the products table. What does the report return after that?
- Use * to look at a table for the first time
- Follow the star with a named list once you know which columns matter
- Name columns in any query that is saved or shared, and in every scheduled one
- Ship * in a report or pipeline that runs on a schedule
- Assume the table's columns will stay the same forever
- Pull 80 columns to read 2
The star replaces the column list rather than 1 column on it. Writing SELECT * gives every column once; adding named columns beside it, as in SELECT *, price, returns price a second time and clutters the result. Keep the 2 styles apart: the star alone while exploring, a named list alone for the query you keep.
A wide star result is also hard to read where you are likely to read it: 80 columns wrap into unreadable lines in a terminal or on a phone, while 3 named columns fit on 1 line.
A result column takes its name from the table column it came from, and that name is often wrong for whoever reads the result. It may be too technical or too abbreviated, or the result may be headed for a tool that expects a different header. AS gives the result column a new name without touching the table. Write the column, then AS, then the name you want it to carry in the result.
SELECTproduct_name AS item,price AS unit_priceFROM products
Names with spaces and capitals
An alias written as a bare word follows the same rules as a column name. It must start with a letter and may contain digits and the _ character after that. The database folds it to lower case, so AS Unit_Price comes back as unit_price. To give a result column a name with a space or a capital letter in it, wrap the alias in double quotes. The quotes make the name exactly what sits between them, spaces and capitals included, and any symbols as well, which is what a spreadsheet or a chart wants to display as a header.
Single quotes mean something different: 'Product' is a piece of text, a value, while "Product" is a name. Writing price AS 'Unit Price' fails with a syntax error because the database expects a name after AS and finds a value instead. Double quotes go around names, and single quotes go around text.
AS is optional, and still worth typing
The database accepts an alias with the keyword left out, so price unit_price means the same as price AS unit_price. The cost shows up when a comma goes missing: SELECT product_name price FROM products returns 1 column holding the product names under the header price, and raises no error at all, because the database read price as an alias for product_name. Typing AS every time makes the intent visible, and when a comma is lost the missing AS makes the mistake stand out on the page.
The same column under 2 names
| Table column | Alias | What the alias adds |
|---|---|---|
| price | unit_price_usd | The quantity it refers to and the currency |
| in_stock | is_available | Reads as a yes or no answer |
| lead_days | delivery_days | The business meaning of the number |
| product_name | "Product" | A header fit for a spreadsheet |
- ▸An alias renames a result column; the table's column keeps its name
- ▸A bare alias starts with a letter, may add digits and the _ character, and comes back in lower case
- ▸Double quotes around an alias keep spaces and capitals exactly as written
- ▸Single quotes make text, so they never go around an alias
Which statement about this query is true?
Adding an alias to a working query follows the same path every time. Find the item on the SELECT list whose header needs to change, type AS after it, then the new header. Use a bare lowercase name, with words joined by _, when the header has no spaces, and double quotes when it has spaces or capitals. A header wrapped in single quotes is a piece of text, so it has no place after AS.
An item on the SELECT list does not have to be a column. It can be an expression: a column combined with numbers and other columns using +, -, * and /. The database evaluates the expression once per row, using that row's own values, and the results become a new column that exists only in the result. The table gains nothing and loses nothing; the query answers a question the table never stored.
SELECTproduct_name,price - 5 AS coupon_priceFROM products
Each row's price has 5 taken from it: 24.0 becomes 19.0, 349.0 becomes 344.0, 89.5 becomes 84.5, 199.0 becomes 194.0 and 18.75 becomes 13.75. The alias matters here more than anywhere else. Without it the database has to invent a header for the computed column, and the one it invents, _col1 for the second item on the list, tells the reader nothing about what the number means. Every computed column gets an AS, with a name that says what was computed.
| Operator | Meaning | On the Desk Lamp (price 24.0) | Result |
|---|---|---|---|
| + | add | price + 1 | 25.0 |
| - | subtract | price - 5 | 19.0 |
| * | multiply | price * 2 | 48.0 |
| / | divide | price / 4 | 6.0 |
| % | remainder after dividing | price % 5 | 4.0 |
Columns combined with columns
The query reads price in 2 expressions. retail_price multiplies it by 1.5, so the Office Chair at 199.0 shows 298.5. markup repeats that product and subtracts the original price, giving 99.5 on the same row. Each expression is computed on its own from the table's columns, which is why the multiplication is spelled out twice: markup cannot refer to retail_price by its alias, and price * 1.5 AS retail_price, retail_price - price AS markup stops with an error that retail_price cannot be resolved. The reason sits in the order the database works through a query, which a later lesson lays out.
Operator order and parentheses
Multiplication and division happen before addition and subtraction, as in arithmetic class. price - 5 * 2 takes 10 from the price. (price - 5) * 2 takes 5 and then doubles what is left. Parentheses cost nothing, and a reader should never have to recall precedence to know what a query computes, so wrap any expression that mixes operators even when the default order happens to be the one you want.
For the Desk Lamp, where price is 24.0, what value does this expression produce?
Whole numbers divide to whole numbers
- ▸Give every expression an alias that names what it means, such as retail_price
- ▸Wrap mixed arithmetic in parentheses so the order is visible without thought
- ▸Put a decimal point in a divisor when the fraction matters
- ▸Spell an expression out again rather than reusing another item's alias
> A merchant wants every product's name beside its price with a 3 dollar handling fee added, under the header final_price. Drag the tiles into the order that builds this query from the products table. One tile would turn the header into a piece of text, so leave it in the bank.
> A pricing analyst at an online furniture store is asked for a sheet to send to a reseller: each product's name, its current price, and the reseller price at 20 percent off, under headers that the reseller's spreadsheet already expects, Product, List Price and Partner Price.
Read a table, name the columns you want, relabel them, and compute new values: the 4 moves inside every query.
Your first SQL query — demystified
- Category
- SQL
- Difficulty
- beginner
- Duration
- 9 minutes
- Challenges
- 13 hands-on challenges
Topics covered: Tables, rows, and columns, SELECT and FROM basics, Selecting all columns (*), AS aliases for columns, Expressions in SELECT
Lesson Sections
- Tables, rows, and columns
A database keeps its data in tables. A table has a name, a fixed set of named columns, and any number of rows. Each column holds the same kind of value in every row: a price column holds prices, never a name in some rows and a date in others. Each row holds 1 record, such as one product in a products table, with a value under every column. The table below is the one this lesson reads again and again, so look at it closely before moving on. This table is named products. It has 5 columns and 5 row
- SELECT and FROM basics (concepts: Query Basics)
Read FROM before SELECT The SELECT list sets the column order Every row comes back, repeats included
- Selecting all columns (*) (concepts: Query Basics)
product_id comes first and in_stock last because that is how the table was designed, and the star takes that order as given. Set this result beside the 2-column query from the last section: the same 5 rows, with 3 more values on each. The star changed nothing about which rows come back; it widened every row to the table's full width. Flip the table card beside the query to its rows and the result and the card match cell for cell. The third tab is the star doing its real job. Nobody told you that
- AS aliases for columns (concepts: Query Basics)
Play it and watch the headers change while the values stay put: product_name becomes item and price becomes unit_price, and the 5 rows underneath hold the same values the table does. The alias lives in the result only. The products table still has a column called product_name, and the next query to read it has to call it product_name again, because an alias from 1 query is gone the moment that query finishes. Names with spaces and capitals AS is optional, and still worth typing The same column u
- Expressions in SELECT (concepts: Query Basics, Operator Precedence)
Columns combined with columns Operator order and parentheses Whole numbers divide to whole numbers This example reads a second small table, inventory, which holds stock counts for the same 5 products: product_name, units_on_hand for how many units sit in the warehouse, and box_size for how many units fit in 1 shipping box. Both count columns hold whole numbers, and dividing 2 whole numbers gives a whole number with the fraction dropped: 40 / 12 returns 3, with the .33 discarded. The % operator r