How to Run Your First Amazon Redshift Query (Step-by-Step)
This guide is for anyone with a Redshift cluster or Serverless workgroup who wants to write and run their first SQL queries. You will get a working connection, run three tiny test queries as an immediate first win, create a table, load data from S3, and inspect an EXPLAIN plan. By the end you will have completed the entire beginner Redshift workflow end to end.
Amazon Redshift querying in plain English
Think of Redshift like a giant spreadsheet that lives on a cluster of machines instead of your laptop. You write SQL to ask questions about the data, and Redshift scans billions of rows in seconds by spreading the work across many machines in parallel. The difference from a regular database is scale: Redshift is built for queries that would bring a normal PostgreSQL instance to its knees.
Here is what actually happens when you query Redshift:
- You connect to a Redshift database the same way you would connect to PostgreSQL
- Tables live inside schemas inside that database
- You write standard SQL:
SELECT,JOIN,GROUP BY, and so on - Redshift executes the query across all compute nodes in parallel and returns the combined result
One thing to understand early: Redshift is built for analytics, not for the traffic that backs an app. It is not the right database for “look up this one user’s profile” or “insert this order as the checkout happens.” It is optimised for “give me total revenue by region for the last 12 months across 200 million rows.”
A normal beginner workflow looks like this:
- Connect to the database
- Run a tiny test query to confirm the connection is working
- Create a table with the right column types and distribution keys
- Load data from S3 using the COPY command
- Write analytical SQL to answer real questions
- Use EXPLAIN to inspect and tune slow queries
Before you start
You need the following before any of this works:
- A provisioned Redshift cluster or a Redshift Serverless workgroup in the Available state
- Your database name, username, and password (or an IAM identity with database access configured)
- Access to Query Editor v2 in the AWS console, or a local SQL client such as psql or DBeaver
- If using psql or another direct client: a security group rule that allows inbound TCP on port 5439 from your IP address
- If loading data from S3: an IAM role attached to the cluster that has S3 read permissions, and the role ARN copied somewhere handy
Redshift Serverless has a free trial and takes a few minutes to set up in the AWS console. You create a workgroup, pick a namespace, and it is ready to query. No node type selection or cluster sizing needed. It is the fastest way to get started.
60-second quick start: run your first successful query
Before creating tables or loading data, confirm the connection is working with a tiny query. This is the fastest possible win and it rules out networking and credential problems early.
Open Query Editor v2
- Go to the AWS console and open the Amazon Redshift service
- Click Query Editor v2 in the left sidebar
- Click Connect in the top right corner
- Select your cluster or serverless workgroup from the dropdown
- Enter your database name, username, and password, then click Connect to database
You now have a SQL editor in your browser with nothing to install. The left panel shows your databases, schemas, and tables. Write SQL in the editor pane and press Ctrl+Enter or click Run to execute.
Run these three test queries
Copy and run these one at a time. If all three return a result, your connection is working and you are ready to move on.
SELECT current_date;SELECT current_user;SELECT version();If any of these fail, the problem is with the connection itself (credentials, network, or security group) rather than anything to do with your tables or data. Fix the connection before moving on.
The version() query returns something like
“PostgreSQL 8.0.2 on i686-pc-linux-gnu…”. That is expected. Redshift reports
a PostgreSQL-compatible version string even though it runs its own engine internally.
Connecting with psql (optional)
If you prefer a terminal-based client or need to script queries, psql works with Redshift because Redshift uses a PostgreSQL-compatible wire protocol.
Install psql via your package manager, then connect with:
psql \
-h your-cluster.abc123.us-east-1.redshift.amazonaws.com \
-U admin \
-d dev \
-p 5439Two things to get right: the port is 5439 (not the PostgreSQL default of 5432), and your security group must allow inbound TCP on 5439 from your IP. If the connection hangs or is refused, the security group is almost always the cause.
For Redshift Serverless, the endpoint looks different:
workgroup-name.account-id.region.redshift-serverless.amazonaws.com.
Copy the full endpoint from the Workgroup details page in the console.
Creating your first table
Redshift uses standard SQL DDL. Here is a realistic example: a table for e-commerce orders.
CREATE TABLE orders (
order_id BIGINT NOT NULL,
customer_id BIGINT NOT NULL,
order_date DATE NOT NULL,
region VARCHAR(50),
product_id BIGINT NOT NULL,
quantity INTEGER NOT NULL,
unit_price DECIMAL(10,2) NOT NULL,
total_amount DECIMAL(10,2) NOT NULL
)
DISTSTYLE KEY
DISTKEY (customer_id)
SORTKEY (order_date);The two Redshift-specific clauses are worth understanding now rather than later:
DISTKEY (customer_id) tells Redshift to distribute rows across compute nodes based on the customer ID column. Rows with the same customer ID land on the same node. When you later join this table against a customers table that is also distributed by customer_id, the matching rows are already co-located, which avoids expensive data movement.
SORTKEY (order_date) means rows are stored sorted by date on disk. When a query filters by a date range, Redshift can skip entire disk blocks outside the range instead of scanning the whole table.
Think of DISTKEY like a filing system where every folder for a given customer goes into the same physical cabinet. When you later ask “show me all orders for customer 42”, the clerk only has to open one cabinet instead of searching every one in the building. SORTKEY is like sorting the folders inside each cabinet by date, so finding “orders from January” means skipping straight to that section rather than flipping through the whole drawer.
See the Redshift Architecture guide for a full explanation of how distribution and sort keys work across nodes and slices.
Loading data from S3 with COPY
Do not use individual INSERT statements to load data into Redshift. The COPY command reads files from S3 in parallel across all compute nodes and is dramatically faster for any non-trivial dataset.
COPY uses an IAM role attached to your Redshift cluster to read from S3. It cannot use your personal AWS credentials. If you have not attached a role to the cluster yet, the COPY command will fail with an access denied error. Set up the role before running COPY.
Here is a complete COPY command for loading a CSV file:
COPY orders
FROM 's3://my-data-bucket/orders/2025/'
IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftS3ReadRole'
FORMAT AS CSV
IGNOREHEADER 1
DATEFORMAT 'YYYY-MM-DD'
REGION 'us-east-1';What each option does:
FROM ‘s3://…’: a prefix loads every file under that path; a full object key loads one specific fileIAM_ROLE: the ARN of the role attached to your cluster that grants S3 read accessFORMAT AS CSV: the file format. Other valid options areJSON,PARQUET, andORCIGNOREHEADER 1: skips the first line of each CSV file (the header row)DATEFORMAT: the date format used in your files. UseDATEFORMAT ‘auto’if you are unsure
If COPY fails, query the STL_LOAD_ERRORS system table to see exactly which rows
failed and the reason. The
Loading Data into Redshift guide
covers IAM setup, Parquet and JSON formats, manifest files, and error diagnosis in full.
Running analytical SQL queries
Redshift supports standard SQL with PostgreSQL-compatible extensions. Here are realistic examples using the orders table created above.
Basic count and filter
SELECT COUNT(*) AS total_orders
FROM orders
WHERE order_date >= '2025-01-01'
AND order_date < '2026-01-01';Aggregation by group
SELECT
region,
DATE_TRUNC('month', order_date) AS month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_revenue,
AVG(total_amount) AS avg_order_value
FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY region, DATE_TRUNC('month', order_date)
ORDER BY month, total_revenue DESC;Window function: running total per customer
SELECT
customer_id,
order_date,
total_amount,
SUM(total_amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS UNBOUNDED PRECEDING
) AS running_total
FROM orders
ORDER BY customer_id, order_date;Join across two tables
SELECT
c.customer_name,
c.country,
COUNT(o.order_id) AS total_orders,
SUM(o.total_amount) AS lifetime_value
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date >= '2025-01-01'
GROUP BY c.customer_name, c.country
HAVING SUM(o.total_amount) > 10000
ORDER BY lifetime_value DESC
LIMIT 50;Always name the columns you need rather than using SELECT *. Redshift stores
data in columns, not rows. Selecting all columns forces it to read every column from disk
even when your aggregation only touches three of them.
Reading an EXPLAIN plan
EXPLAIN is like the turn-by-turn preview on a GPS app before you start driving. You can see the full route, the estimated time, and whether there is traffic before you commit. The query never actually runs, so checking the plan is free.
The EXPLAIN keyword shows the query execution plan without running the query.
This is how you diagnose slow queries. Prepend it to any SELECT statement:
EXPLAIN
SELECT region, SUM(total_amount)
FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY region;The output looks like this (simplified):
XN HashAggregate (cost=16523.84..16523.96 rows=50 width=42)
-> XN Seq Scan on orders (cost=0.00..15223.00 rows=260000 width=42)
Filter: ((order_date >= '2025-01-01') AND (order_date <= '2025-12-31'))Key things to look for in the output:
- Seq Scan: A full scan of the table. Normal, but check the estimated row count against the total table size. If the sort key is helping prune blocks, the row estimate should be much lower than the full table.
- DS_DIST_ALL_NONE or DS_DIST_NONE: No data redistribution needed across nodes. This is good.
- DS_BCAST_INNER: The inner table is being broadcast to all nodes. Normal for small dimension tables joining to large fact tables.
- DS_DIST_BOTH: Both tables are being redistributed during the join. This is expensive and usually means the distribution keys on the two tables do not match.
- Hash Join or Merge Join: The join algorithm. Hash joins are the most common in Redshift.
If you see DS_DIST_BOTH in your EXPLAIN output, Redshift is shuffling rows
from both tables across the network before it can join them. On a large table this turns
a fast join into a slow one. The fix is to make both tables share the same DISTKEY column
so the matching rows are already on the same node.
For a detailed guide on using EXPLAIN to fix distribution mismatches and tune WLM queues, see Redshift Performance Optimisation.
Useful AWS CLI commands
You can manage Redshift clusters from the terminal without opening the console:
# List all clusters in the current region
aws redshift describe-clusters \
--query 'Clusters[*].{ID:ClusterIdentifier,Status:ClusterStatus,Nodes:NumberOfNodes}'
# Pause a provisioned cluster (stops billing for compute)
aws redshift pause-cluster \
--cluster-identifier my-cluster
# Resume a paused cluster
aws redshift resume-cluster \
--cluster-identifier my-cluster
# Get the endpoint for a cluster
aws redshift describe-clusters \
--cluster-identifier my-cluster \
--query 'Clusters[0].Endpoint'You can run SQL against Redshift from the terminal without a direct network connection using
aws redshift-data execute-statement. This is useful in CI pipelines or Lambda
functions where opening a direct TCP connection to port 5439 is not practical.
Common beginner mistakes
- Connecting on port 5432 instead of 5439. Redshift uses port 5439 by default. If psql gives a connection refused error, check the port number first.
- Using SELECT * in analytical queries. Selecting all columns defeats the purpose of columnar storage. Redshift reads every column from disk even if your aggregation only uses two of them. Name your columns explicitly.
- Running COPY without an attached IAM role. Redshift cannot use your personal credentials to read from S3. The cluster needs an IAM role attached to it before the COPY command will work.
- Not checking STL_LOAD_ERRORS after a failed COPY. When COPY fails it can be vague about why. Run
SELECT * FROM STL_LOAD_ERRORS ORDER BY starttime DESC LIMIT 10;to see which rows failed and the exact reason. - Running ORDER BY on large result sets without LIMIT. Sorting billions of rows is expensive. If you only need the top 100 results, add a LIMIT clause.
Summary
- Connect via Query Editor v2 (browser-based, no setup) or psql on port 5439
- Run
SELECT current_date;immediately after connecting to confirm everything is working before building tables - Create tables with DISTKEY and SORTKEY to control how data is distributed across nodes and sorted on disk
- Use the COPY command to load data from S3 in parallel across all compute nodes
- Redshift supports standard SQL including window functions, CTEs, and complex joins
- Prepend EXPLAIN to any query to see the execution plan without running it
- Check STL_LOAD_ERRORS when a COPY command fails to find the exact row and reason
Frequently asked questions
How do I connect to Redshift without installing software?
Use Redshift Query Editor v2, which is built into the AWS console. Navigate to the Redshift service, click Query Editor v2 in the left menu, and connect to your cluster or serverless namespace. No driver installation or client software is needed.
Can I connect to Redshift with psql?
Yes. Redshift is compatible with the PostgreSQL wire protocol, so the standard psql client works. Use the connection string format: psql -h <endpoint> -U <username> -d <database> -p 5439. The default Redshift port is 5439, not 5432.
What does the EXPLAIN command do in Redshift?
EXPLAIN shows the query execution plan without actually running the query. It breaks down each step the query planner will take, shows which tables and columns are read, any sort or redistribute steps, and estimated row counts. This is useful for understanding why a query is slow and whether your distribution and sort keys are being used effectively.