Topic 13

Expressions, Aliases, and Functions

SQL

Lora is planning matinee pricing: every afternoon ticket at 20% off. The discounted prices exist nowhere in the database — and they don't need to. A query can compute while it reads: show each screening's stored price and, right next to it, the matinee price derived on the fly, under a readable column name.

That is this page's one idea, worn three ways. An expression is a small computation written where a column name would go. An alias gives the computed column a name worth reading. And functions are the built-in helpers — rounding, changing case, measuring length — that expressions call on. Together they mean queries don't just fetch facts; they derive conclusions from them.

Arithmetic While You Read

In English: show each screening's price, and next to it the price times 0.8. The multiplication sits right in the SELECT list, where a column name would normally be:

A derived column — computed per row, stored nowhere
SELECT screening_id, price, price * 0.8
FROM screenings;
screening_idpriceprice * 0.8
112.009.60
210.008.00
312.009.60
49.507.60

The database evaluated price * 0.8 once per row and put the result in the answer. Nothing was stored: the screenings table still holds price and only price. Ask again tomorrow and the matinee numbers are derived again, fresh, from whatever the stored prices are then. A receipt at a till is the right picture — the shelf price is what's stored, and the line total is computed at the till, every time, from the same stored facts. Map the picture, drop it: derived values live in answers, stored values live in tables.

AS Gives It a Name

That third column heading — price * 0.8 — is what the database shows when you don't name a computed column, and no one wants to read it on a schedule board. The keyword AS attaches an alias, a name that exists only in this answer:

The same query, with the derived column properly named
SELECT screening_id, price, price * 0.8 AS matinee_price
FROM screenings;
screening_idpricematinee_price
112.009.60
210.008.00
312.009.60
49.507.60

One honest footnote: in most engines the word AS is optional — price * 0.8 matinee_price works too — but always writing it is kinder to the next reader, and the next reader is usually you. Aliases also rename ordinary columns (runtime_min AS minutes) whenever a report needs friendlier headings.

Stored in the table vs. computed at query time
Stored columns
price · starts_at · screen — facts the table holds; the single source of truth
Derived columns — computed at query time
matinee_price = price * 0.8 · hours = ROUND(runtime_min / 60.0, 1) — conclusions recomputed from the facts, every time

A Tour of Everyday Functions

A function takes a value in and hands a value back: you write its name, then the input in parentheses. ROUND(9.6, 1) rounds to one decimal place; UPPER(title) and LOWER(email) shift text to one case; LENGTH(title) measures how long a piece of text is (with one engine wrinkle — MySQL counts bytes here, where CHAR_LENGTH gives the character count). Like arithmetic, a function in the SELECT list runs once per row against a copy of the value — the marquee sign needs shouting capitals is one function call away:

Two functions at work — capitals for the sign, runtimes in hours
SELECT UPPER(title) AS sign_title,
       ROUND(runtime_min / 60.0, 1) AS hours
FROM films;
sign_titlehours
THE LONG RAIN1.9
PAPER LANTERNS1.6
NIGHT BUS1.6

And to be clear about what did not happen: the films table still says "The Long Rain", in ordinary case. UPPER shouted at a copy in the answer; the stored title never heard it. Functions in a SELECT transform the copy being examined, never the data itself.

Dates get functions too — extract the year from joined_on, subtract two dates to count days — and here comes the engine-honesty sentence: date function names vary annoyingly between engines, so the exact spelling is a thing you look up per engine, while the idea — dates are values you can compute with — is universal.

Functions in WHERE Too

Everything above works in a WHERE clause as well, which resolves last topic's loose end about text case. Lora wants the customers with Gmail addresses, however their emails happen to be capitalized. Lowercase each email before testing it, and match the pattern anything, then @gmail.com:

A function inside WHERE — plus a first taste of LIKE
SELECT name, email
FROM customers
WHERE LOWER(email) LIKE '%@gmail.com';

Two new things in one line, so read it slowly. LOWER(email) normalizes each row's email to lowercase for the test — the stored emails keep their capitals. And LIKE compares against a pattern instead of an exact value, where % means "anything here": the pattern reads anything, ending in @gmail.com. Consider LIKE a cameo appearance — pattern matching is a deeper toolbox than one page can hold — but the lowercase-before-comparing trick is the everyday standard for case-proof text tests, and it's yours now.

Common Confusions
  • "The computed column is now in the table." Derived values live in the answer only. The screenings table still holds price and nothing else — run the query again and matinee_price is computed again, fresh.
  • "I should store the discounted price too." Store facts, derive conclusions. Store both and one day they'll disagree — someone updates the price and forgets the discount. This instinct grows into a design principle in Chapter 6.
  • "Functions change the data." In SELECT and WHERE, a function transforms the copy being examined. UPPER(email) shouts at nobody's actual email; the stored value is untouched.
  • "An alias renames the real column." An alias exists only in that one answer. The table's column is still named what CREATE TABLE named it; the next query starts from scratch.
Why It Matters
  • Deriving at read time is the everyday power move of SQL: prices with tax, ages from birthdays, names normalized for matching — all computed fresh from stored facts, never stored twice.
  • "Store facts, derive conclusions" looks like a syntax lesson and is actually a design principle — it's half of why databases stay trustworthy, and Chapter 6 builds on it by name.

Knowledge Check

After running SELECT price, price * 0.8 AS matinee_price FROM screenings;, what does the screenings table contain?

  • Both price and a new matinee_price column
  • Just price, exactly as before the query ran
  • The price column, overwritten with discounted values
  • A price column renamed to matinee_price

What does AS do in price * 0.8 AS matinee_price?

  • Permanently renames the price column
  • Names the computed column in this answer only
  • Performs the multiplication itself
  • Saves the expression so future queries can reuse it

Lora runs SELECT UPPER(title) FROM films;. What happens to the title "Night Bus" stored in the table?

  • Nothing — the answer shows NIGHT BUS, the table still says Night Bus
  • It becomes NIGHT BUS in the stored films table from now on, permanently
  • The table keeps both versions side by side
  • An error — UPPER only works inside WHERE

Why is "store facts, derive conclusions" better than also storing the matinee price in the table?

  • Because tables have a strict limit on column count
  • Because computed columns are always faster to read
  • Because stored twice, the two values will eventually disagree
  • Because SQL flatly forbids storing any value that could be computed instead

You got correct