skip to content

Views, Procedures & Server-Side Logic

I learn the database objects that live beyond plain tables — views, materialized views, stored procedures, triggers, generated columns, sequences, and temp tables — and when pushing logic into the engine helps or hurts. Interviewers use this area to test whether I understand what the engine can do server-side and can argue the business-logic-in-the-database trade-off.

part ofRelational database conceptsoverview, primer and where to startread it →
on this pageshow

questions

page 2 of 2

How can a view act as an access-control boundary — for example exposing an employees table without its salary and national-ID columns — and what are the limits of relying on that?

level: middleimportance: should knowfreq 42%

basics

~20 s

Grant SELECT on a view that projects only the allowed columns and rows, and grant nothing on the base table. Because the view typically executes with its owner's privileges, callers can read exactly what it projects and no more. Limits: it does not stop anyone holding direct base-table rights, and clever predicates can leak information.

open as a page

For keeping a derived value in a table up to date, when would you use a generated column and when would you fall back to a trigger? What does each guarantee?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Use a generated column whenever the value is a deterministic function of the same row - the engine guarantees it can never be wrong or bypassed, and there is no procedural code to maintain. Use a trigger only when the derivation needs other rows, other tables, non-deterministic input, or must freeze at insert time.

open as a page

Your team keeps meaningful business logic in database stored procedures. How do you version, test, review, and roll back that code with the same discipline you apply to application code?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Keep every procedure body in version control as the source of truth, deploy it through the same ordered migration pipeline as schema changes, test against a real database in CI, keep signatures backward compatible across rolling deploys, and treat rollback as a forward migration re-applying the old body.

open as a page

While a materialized view is being refreshed, what happens to queries that read it, and how does a concurrent refresh mode (REFRESH MATERIALIZED VIEW CONCURRENTLY and its equivalents) change that?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A plain refresh typically takes an exclusive lock and rebuilds the view, so readers block for the whole refresh. A concurrent refresh builds the new result separately and merges the differences row by row, letting readers keep querying — at the cost of a slower refresh and a required unique index.

open as a page

A sequence can be created with a CACHE setting (for example CACHE 1 versus CACHE 1000). Explain what that setting does, and how you would choose it for a high-throughput insert workload — including what happens on a crash and across multiple database instances.

level: seniorimportance: should knowfreq 38%

basics

~20 s

CACHE n preallocates n values into memory so most allocations avoid touching and persisting shared sequence state. Bigger cache means less contention and fewer writes, but larger gaps after a crash and, across instances, values issued out of global order.

open as a page

When a stored routine's body is executed repeatedly with different parameter values, how does the database handle execution plans for the statements inside it, and what problem can that cause?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Engines cache plans for statements inside a routine and reuse them across calls, planning them against the first parameter values seen. If the data is skewed, a plan optimal for one value is terrible for another — parameter sniffing. Remedies: force a per-call replan, use a generic plan, split the routine, or restructure the query.

open as a page

A query that joins against a set-returning (table-valued) function performs far worse than the equivalent query written against base tables. What is going on inside the optimizer, and what are your options?

level: seniorimportance: should knowfreq 34%

basics

~20 s

The optimizer usually cannot see inside the function, so it uses a fixed default row estimate instead of statistics. A wrong cardinality picks the wrong join order and join method — typically nested loops over millions of rows. Fixes: get the function inlined, declare a row estimate, or materialize its output into a temp table with statistics.

open as a page

A stored procedure that creates a temporary table runs thousands of times a minute, and the database starts showing contention and bloat unrelated to the business tables. What is happening, and how would you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Each creation writes system-catalog rows and allocates storage, so at high rates the catalog itself becomes the hot spot: latch or lock contention on metadata, catalog bloat, and constant plan recompilation. Fixes: create the table once per session and reuse it, drop the staging step entirely, or move the intermediate inline.

open as a page

Inside a trigger body, how do you reference the row's values before and after the change, and how does that differ between row-level and statement-level triggers? Explain what transition tables (REFERENCING OLD TABLE / NEW TABLE) give you.

level: seniorimportance: should knowfreq 36%

basics

~20 s

Row-level triggers get single-row images, conventionally OLD and NEW: INSERT has only NEW, DELETE only OLD, UPDATE both. Statement-level triggers have no per-row image; transition tables expose the whole set of changed rows as two readable relations you can join and aggregate in one set-based statement.

open as a page

A view aggregates and joins several tables, so the engine will not accept writes against it. How does an INSTEAD OF trigger let you support writes anyway, and what are the risks of that approach?

level: seniorimportance: should knowfreq 35%

basics

~20 s

An INSTEAD OF trigger replaces the engine's automatic rewrite: instead of executing the INSERT, UPDATE or DELETE, the engine runs your code, which issues explicit statements against the base tables. Risks: you own correctness, multi-row semantics, ordering, error handling, and returned row counts.

open as a page

A view joins a customers table to an orders table, and an application tries to UPDATE a column through it. What is a key-preserved table, and why do engines use that concept to decide whether the update is allowed?

level: seniorimportance: should knowfreq 32%

basics

~20 s

A table is key-preserved in a join view when its primary key stays unique in the view result — each of its rows appears at most once. Only key-preserved tables can be updated through the view, because only then does a view row identify exactly one base row.

open as a page

A reporting query reads from a view that itself reads from three other views, and it is far slower than expected. What problems are typical of deep view-on-view chains, and how would you attack this?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Stacked views accumulate joins and columns nobody downstream needs, and inner aggregations or DISTINCTs block predicate pushdown so the bottom of the stack scans everything. Read the expanded plan, find the fence and the joins contributing nothing, then flatten the query for that consumer.

open as a page

A proposal moves a CPU-heavy pricing calculation from the application tier into database procedures so it runs next to the data. How would you evaluate that proposal from a capacity and scaling perspective?

level: principalimportance: should knowfreq 30%

basics

~20 s

Compare where the work is cheapest to add capacity. Application servers are stateless and scale horizontally for money; the primary database is one writable node, shared by every query, and often licensed per core. Move work down only when it removes far more data movement than the CPU it adds.

open as a page

How would you choose between refreshing a materialized view on commit of every base-table transaction versus on demand on a schedule, and how do you decide the acceptable staleness window?

level: principalimportance: should knowfreq 34%

basics

~20 s

On-commit refresh keeps the view nearly current but charges every writing transaction and serialises writers on shared aggregate rows. On-demand refresh batches the cost but leaves a staleness window. Choose by the consumer's tolerance for stale data against the write throughput you can afford to lose.

open as a page

You need a durable audit trail of every change to a handful of core tables. Compare building it with audit triggers, with log-based change data capture that reads the database transaction log, and with a transactional outbox table written by the application — and say when you would choose each.

level: principalimportance: should knowfreq 38%

basics

~20 s

Triggers capture every writer atomically with the change but tax every write and roughly double log volume. Log-based capture reads the transaction log the engine already writes, so the write path pays nothing, but it is asynchronous and loses application context. An outbox gives typed business events in the same transaction, but only for writers that cooperate.

open as a page

You need to split a wide table into two and rename several of its columns, but dozens of applications read it and you cannot redeploy them together. How would you use views as a facade to stage that change, and where does the approach run out?

level: principalimportance: should knowfreq 28%

basics

~20 s

Introduce the new tables alongside the old shape, then expose a view carrying the old name and column names over the new structure so existing readers keep working unchanged. Migrate consumers to new views over time, then retire the compatibility view. It runs out on writes, on plan quality, and on the fact that a shim nobody is forced to leave lives forever.

open as a page

showing 31–46 of 46