How to Join Two Custom SQL Queries in Tableau
Joining data from different tables is straightforward in Tableau, but what happens when you need more control? Combining pre-filtered, aggregated, or complex SQL queries is a powerful way to prep your data exactly how you need it before it even hits your dashboard. This article provides a step-by-step guide on how to join two separate custom SQL queries directly within Tableau.
Why Use Custom SQL in Tableau?
While Tableau’s drag-and-drop interface is great for simple joins and data blending, there are specific scenarios where writing your own SQL gives you a significant advantage. It allows you to leverage the full power of your database's processing capabilities before pulling data into Tableau for visualization.
Consider using custom SQL when you need to:
- Improve Performance: By filtering and aggregating massive datasets at the database level, you send a much smaller, more manageable subset of data to Tableau. This can dramatically speed up dashboard load times and interactivity.
- Perform Complex Joins: Sometimes you need to join tables based on complex logic or calculations that aren't easily configured in Tableau’s visual interface, such as joins involving conditional statements or string manipulations.
- Unify Unstructured Data: You might need to use functions like
UNIONto stack data from tables with similar structures, which is a perfect use case for a custom query. - Leverage Database-Specific Functions: Your database (like PostgreSQL, SQL Server, or Snowflake) has a rich library of functions for things like window calculations, date manipulation, or JSON parsing that you can use to shape your data perfectly.
Essentially, custom SQL lets you handle the heavy data preparation on the database server, leaving Tableau to do what it does best: visualization and exploration.
Setting the Stage: A Practical Example
Let's imagine we're working for an e-commerce company. Our database has two primary tables: orders and customers. Our goal is to analyze the Q1 2024 sales performance for our top-spending customers in the United States.
To do this, we need to answer two separate questions and then combine the results:
- Which orders were placed in Q1 2024 by U.S. customers?
- Which of our U.S. customers have the highest lifetime value (LTV)?
Directly joining the full orders and customers tables in Tableau and then filtering would work, but it would pull in years of entire order history for every single customer, making our workbook slow. A smarter approach is to use two optimized SQL queries and join them.
Query 1: Get U.S. Sales in Q1 2024
This query will select only the relevant orders, filtering by date and country right at the source.
SELECT
o.order_id,
o.customer_id,
o.order_date,
o.order_total,
c.country
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.country = 'USA'
AND o.order_date >= '2024-01-01' AND o.order_date <= '2024-03-31'Free PDF · the crash course
AI Agents for Marketing Crash Course
Learn how to deploy AI marketing agents across your go-to-market — the best tools, prompts, and workflows to turn your data into autonomous execution without writing code.
Query 2: Calculate Lifetime Value for U.S. Customers
This query will calculate the total historical spending for every U.S. customer.
SELECT
c.customer_id,
c.first_name,
c.last_name,
SUM(o.order_total) as lifetime_value
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE c.country = 'USA'
GROUP BY 1, 2, 3Our task is to join these two results inside Tableau on the customer_id field. This will give us a clean dataset containing Q1 sales alongside the total LTV for each U.S. customer involved in those sales.
Step-by-Step Guide: Joining Custom SQL Queries in Tableau
Now, let's walk through the process of connecting and joining our two queries within the Tableau Desktop interface. The process is the same whether you're using Tableau's older logical joins or newer relationships model.
Step 1: Connect to Your Database
First, open Tableau and connect to your data. In the Connect pane, select your database type (e.g., Microsoft SQL Server, PostgreSQL, MySQL) and enter your server credentials to establish the connection. Once connected, select the database that contains your tables.
Step 2: Add Your First Custom SQL Query
On the Data Source page, you'll see a list of tables on the left side. Instead of dragging a table into the canvas, find and drag the New Custom SQL option. You'll usually find it at the bottom of the table list.
A dialog box will pop up. This is where you'll paste your first query. Let's name this query "Q1_US_Sales" and paste the first SQL snippet from our example.
SELECT
o.order_id,
o.customer_id,
o.order_date,
o.order_total,
c.country
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.country = 'USA'
AND o.order_date >= '2024-01-01' AND o.order_date <= '2024-03-31'Click OK. Tableau will execute the query and show you a preview of the resulting data at the bottom of the screen. You should now see a box in your data canvas representing this query.
Step 3: Add Your Second Custom SQL Query
This is where the magic happens. To add the second query, simply drag another New Custom SQL object from the left pane and drop it onto the canvas, near the first one.
Tableau will draw a line - affectionately called a "noodle" - connecting the two Custom SQL objects, indicating it's ready to create a relationship or join.
Another dialog box will appear. Name this one "Customer_LTV" and paste our second query.
SELECT
c.customer_id,
c.first_name,
c.last_name,
SUM(o.order_total) as lifetime_value
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE c.country = 'USA'
GROUP BY 1, 2, 3Click OK.
Step 4: Configure the Join Relationship
Tableau is pretty smart and will often correctly guess the join relationship based on field names. In our case, it will likely identify that both queries contain customer_id and automatically establish the link.
If you need to configure it manually, simply click on the connecting noodle. A "Configure Relationship" (or "Edit Join") dialog box will appear.
Here, you need to define:
- The Join Key: Select
customer_idfrom the "Q1_US_Sales" query andcustomer_idfrom the "Customer_LTV" query. - The Join Type (Operator): Tableau's default relationship model is flexible, but if you're editing a physical join, you’ll see options like Inner, Left, Right, and Full Outer. For our case, an Inner Join makes sense, as we only want to see Q1 sales data for customers we've calculated an LTV for. A Left Join would be useful if we wanted to see all Q1 sales, even if a customer somehow didn't have a calculated LTV.
Once you close this window, the join is complete!
Step 5: Go to Your Worksheet and Build!
Your data source is now a "virtual table" created by joining the results of your two custom queries. Click on "Sheet 1" in the bottom tab bar. In the worksheet view, you will see fields from both queries in the Data pane on the left, ready for you to drag, drop, and visualize. You can now easily build a chart showing things like order_total against lifetime_value.
Common Issues and Best Practices
Joining custom queries is powerful, but it's important to be aware of a few potential hiccups.
Tip 1: Performance is Tied to Your Query
Tableau is simply sending your raw SQL to the database. If your query is inefficient, your dashboard will be slow. Always test your queries in a separate SQL tool first. Avoid using SELECT * FROM ... and only select the columns you actually need for your analysis.
Free PDF · the crash course
AI Agents for Marketing Crash Course
Learn how to deploy AI marketing agents across your go-to-market — the best tools, prompts, and workflows to turn your data into autonomous execution without writing code.
Tip 2: Watch Out for Mismatched Data Types
Joins demand that the key columns have matching data types. If customer_id is an INTEGER in one query but a VARCHAR (string) in another, the join will fail. If this happens, you can use the CAST() function in your SQL to convert them to a matching type, for example: CAST(customer_id AS INTEGER).
Tip 3: Give Your Custom SQL Queries Unique Names
If you have a field named order_id in both Custom SQL queries, Tableau will present them as order_id and order_id (Custom SQL Query 2), which can be confusing. Be sure to name your custom query objects clearly (e.g., "Q1_Sales" and "Customer_Data") so it's easier to distinguish fields in your worksheets.
Tip 4: Use Parameters for Dynamic Queries
You can make your custom SQL dynamic by inserting Tableau Parameters. For example, instead of hardcoding 2024-01-01, you could use a StartDate parameter. Your WHERE clause would look like o.order_date >= <Parameters.StartDate>. This allows users to change the dates directly on the dashboard, and Tableau re-executes the query with the new values.
Final Thoughts
Joining custom SQL queries in Tableau gives you fine-grained control over your data preparation process, enabling more complex analysis and optimizing dashboard performance. While it requires some SQL knowledge, it bridges the gap perfectly between raw database tables and clean, insightful visualizations.
And if you'd rather not write SQL at all, that's where we come in. Here at Graphed, we’ve built an AI data analyst that connects to all your sources - from Google Analytics to Salesforce - and lets you build dashboards using simple conversational language. Instead of writing and joining queries, you can just ask, “Create a dashboard joining my Shopify Q1 sales to my customer LTV from our database,” and Graphed builds interactive, real-time-updated dashboards for you in seconds.
Related Articles
How to Create Facebook Ads API for Claude Code
Create a Meta Marketing API access token that never expires, using a system user, so Claude Code can pull Facebook ad reports and manage campaigns.
ClickHouse vs Postgres: When to Switch to Columnar
Comparing ClickHouse vs PostgreSQL means matching storage architecture to workload. Row storage wins for transactions. Columnar wins for scan-heavy analytics.
Google Ads for Orthodontists: Get More Booked Consults
Most orthodontic Google Ads campaigns waste money on low-intent searches. The problem is structure, not the platform.