How would you set up a database account for analysts and dashboards that need to query production data, so their queries cannot damage or disrupt the transactional workload?
answer
- SELECT only, on curated views not raw tables
- read replica = resource isolation, costs lag
- statement_timeout + idle-in-transaction timeout on the role
- cap connections; separate pool from the app
- personal logins + role membership, not a shared password
basics
~20 sCreate a role with SELECT only - no DML, no DDL - on the specific tables or views it may see, point it at a read replica where possible, and attach per-account limits: read-only sessions, statement timeouts, and a capped connection allowance so a runaway query cannot starve the application.
solid answer
~50 sThree layers. **Privileges**: a role holding `SELECT` only, granted on curated views rather than raw tables where columns are sensitive, with nothing from `PUBLIC` and no DML or DDL. **Placement**: connect it to a read replica so long analytical scans consume replica CPU and I/O rather than competing with the primary's transactional work. **Resource containment**: this is the part people forget - read-only does not mean harmless. A `SELECT` with a bad join can pin CPU, blow up temp space, hold a snapshot open for hours and thereby block cleanup of old row versions, or exhaust the connection slots the app needs. So set a statement timeout and idle-in-transaction timeout at the role level, cap the account's concurrent connections, and route it through its own pool. Identity matters too: analysts should authenticate as themselves via role membership, not share one password, so audit records name a person.
code
sql · 9 linesCREATE ROLE reporting NOLOGIN;
GRANT USAGE ON SCHEMA app TO reporting;
GRANT SELECT ON app.v_orders_safe, app.v_customers_safe TO reporting;
ALTER ROLE reporting SET statement_timeout = '120s';
ALTER ROLE reporting SET idle_in_transaction_session_timeout = '60s';
CREATE ROLE alice LOGIN PASSWORD '...' CONNECTION LIMIT 5;
GRANT reporting TO alice;go deeper
Say SELECT-only role, granted on the specific tables or views needed, with its own credentials - not the application's account.
Add the read replica for isolation and at least one resource limit (statement timeout or connection cap), and explain why read-only still needs them.
Cover MVCC snapshot effects on cleanup and replica replay conflicts, role-level timeouts, a dedicated pool, curated views over raw tables, and per-analyst identity for auditability.
Weigh serving reporting from a replica against a separate analytical store, and set the organisational policy on lag tolerance, sandbox schemas and who approves new grants.
## Read-only is a privilege statement, not a safety statement The naive version of this is `GRANT SELECT ON ALL TABLES TO analyst` and a shared password. That gets the privilege side roughly right and misses the two things that actually cause incidents: resource contention and data exposure. ## Privileges Start with a role that has `SELECT` and nothing else - no INSERT/UPDATE/DELETE, no CREATE, no EXECUTE on functions that write. Grant on named objects rather than 'everything', so a new table containing tokens or payment data is not automatically visible. Where a table mixes sensitive and non-sensitive columns, grant on a **view** that projects only the safe columns, and do not grant on the base table. The view becomes the contract; the underlying schema can change without changing what analysts see. (Column-level restriction mechanics are a topic of their own - here the point is that the reporting account's grants should target the curated surface, not the raw one.) Also revoke inherited access: in engines where `PUBLIC` implicitly holds rights on a schema or on newly created objects, a 'read-only' role can quietly see more than you granted it. ## Placement Analytical queries are long, scan-heavy, and unpredictable; OLTP queries are short and latency-sensitive. Running both on the same instance means the analytical workload evicts the transactional working set from the buffer cache and competes for I/O. The default answer is a read replica dedicated to reporting. It gives physical isolation of resources, and it makes the account's read-only nature structural on engines where replicas reject writes outright. The cost is replication lag - reports may be seconds or minutes behind - which needs to be stated up front rather than discovered when a finance number disagrees with the app. ## Resource containment The failure modes of a read-only account are real: - **Runaway scans** - a missing join predicate produces a cross product; set a per-role `statement_timeout` so it dies in minutes rather than hours. - **Long-held snapshots** - in MVCC engines a long-running read keeps old row versions alive, bloating tables and, on a replica, potentially conflicting with replay. Bound query duration; on replicas, know your engine's setting for how long replay will wait for a reader before cancelling it. - **Connection exhaustion** - a BI tool that opens fifty connections can starve the application. Cap the role's concurrent connections and give reporting its own pool rather than sharing the app's. - **Idle transactions** - a client that opens a transaction and goes to lunch holds resources; set an idle-in-transaction timeout. These limits are best attached to the role itself, so they apply no matter which tool connects. ## Identity and auditing A shared reporting password destroys attribution: when an export of the customer table shows up in the audit log, you want a name. Give each analyst their own login and grant them membership in the reporting role; the privileges live on the role, the identity stays personal. Where the engine or tooling forces a single service account (many BI servers do), compensate by recording the end-user identity in the tool's own audit log and correlating, and by keeping that account's grants especially narrow. ## When read-only is not enough If analysts need to create their own tables for intermediate results, do not widen the production account - give them a separate schema (or a separate analytics database) where they own their objects, with `SELECT` on production data and `CREATE` only in their sandbox. That keeps the write capability off the production schema while still meeting the real requirement.
- A read-only account cannot write, so why do people still call it an availability risk?Because reads consume the same finite resources as writes. A cross-product scan can saturate CPU and I/O and evict the OLTP working set from cache; a long-running read in an MVCC engine keeps old row versions from being cleaned up, causing bloat; and a BI tool can occupy the connection slots the application needs. Timeouts, connection caps and a dedicated replica address all three.
- Analysts ask to create scratch tables from their query results. How do you answer without granting write access to production tables?Give them a separate schema or database they own, with CREATE there and SELECT on the production data they need. The write capability lands on their sandbox, not the production schema, and their objects can have their own retention and quota rules.
saying these in an interview costs you the question
- Treating 'read-only' as inherently safe and skipping statement timeouts and connection limits.
- Granting SELECT on all tables in the database instead of on curated views or named objects.
- One shared reporting password for the whole analytics team, which destroys attribution in the audit log.
- Pointing reporting at the primary because 'the replica might be stale', without quantifying the lag or the contention cost.