ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

Dataset Join: Combine Data from Multiple Sources

By MCP Analytics Team | Published , updated

Your data lives in separate files. Products in one spreadsheet, ratings in another. January metrics in one CSV, February in the next. Customer info in the CRM export, transactions in the billing export. The Dataset Join module merges two or more datasets on a common column, like an SQL JOIN, but without writing SQL. Upload your files, pick the join type, and get a combined dataset with join statistics. The module finds the join column for you, so check that it picked the right one.

What Is a Dataset Join?

A join combines rows from two or more datasets based on a shared column: a common key that exists in all of them. If you have a products table with columns for product_id, name, and price, and a ratings table with product_id, rating, and review_count, joining them on product_id produces a single table with product_id, name, price, rating, and review_count. Every row that matches on product_id gets combined.

This is the same concept as a SQL JOIN, a VLOOKUP in Excel, or a merge in pandas. The difference is that you do not need to know SQL, write formulas, or write code. You upload your files and pick a join type, and the module handles the rest, including a log of how many rows went in and came out at each step.

There are four join types, and choosing the right one matters:

Inner join keeps only rows where the join column value exists in all datasets. If product_id 5 exists in the products table but not in the ratings table, it is excluded from the result. Use inner joins when you only want complete records, rows with data from every source.

Left join keeps all rows from the first (left) dataset and adds matching columns from the second dataset. If product_id 5 has no match in the ratings table, it still appears in the result, but the rating and review_count columns will be empty (NA). Use left joins when your first dataset is the primary source and you want to enrich it without losing any rows.

Right join is the mirror image: all rows from the second dataset are kept, with matching data from the first. Less commonly used, but important when the second dataset is your primary source.

Full join (outer join) keeps all rows from all datasets. If a product_id exists in only one table, it still appears with NAs for the columns from the other table. Use full joins for before/after comparisons, for example, joining January and February GSC data to see which pages appeared in only one month (gains and losses).

A Worked Example With Real Numbers

Here is a tiny test you can run yourself in R. The data is made up. A products table has 5 rows (ids 1 to 5). A ratings table has 5 rows too, but it has two ratings for product 2 and one for product 6, which is not in the products table.

products <- data.frame(product_id=c(1,2,3,4,5),
  name=c("Mug","Lamp","Desk","Chair","Rug"), price=c(8,25,140,90,60))
ratings  <- data.frame(product_id=c(1,2,2,3,6),
  rating=c(4.5,4.0,3.5,4.8,5.0))
nrow(merge(products, ratings, by="product_id"))                # inner
nrow(merge(products, ratings, by="product_id", all.x=TRUE))    # left
nrow(merge(products, ratings, by="product_id", all.y=TRUE))    # right
nrow(merge(products, ratings, by="product_id", all=TRUE))      # full

We ran this in R 4.6.0. The row counts were:

Join typeRows outWhy
Inner4Products 1, 2, 3 match. Product 2 has two ratings, so it appears twice.
Left6The 4 matched rows, plus Chair and Rug with empty ratings.
Right5The 4 matched rows, plus the rating for product 6 with an empty name and price.
Full7All of the above together.

Two lessons. First, the left join has 6 rows even though the products table has 5. A duplicate key on the right side adds rows. Second, the inner join keeps 4 rows even though only 3 products matched, for the same reason. So a row count that goes up does not always mean a bug, and a row count that stays flat does not prove every key matched. Count the duplicates in each key column before you join. In the example, sum(duplicated(ratings$product_id)) returns 1.

Limits: this is a toy table. Real files have messy keys, such as trailing spaces and mixed case, and those cause missed matches that a row count alone will not explain.

When to Use Dataset Join

Any time your analysis requires data that lives in separate files, Dataset Join is the first step. Here are the most common scenarios:

Enriching records: You have a customer list with IDs and demographics in one file, and purchase history with the same customer IDs in another. An inner or left join on customer_id combines them so you can analyze purchase behavior by demographic segment.

Time period comparison: You have Google Search Console data for January in one export and February in another. A full join on the page URL gives you a combined table where you can calculate month-over-month changes in clicks and impressions. Pages that appear in only one month show up as gains or losses with NA values in the other month's columns.

Cross-platform correlation: You have GA4 engagement metrics (sessions, bounce rate) in one file and GSC search metrics (impressions, clicks, CTR) in another. Joining on page URL lets you correlate search performance with on-site engagement, something neither platform can show you on its own.

Survey response merging: You ran the same survey across multiple cohorts or time periods and exported each separately. If respondents carry an ID, a join on that ID puts each person's answers from both rounds on one row. If the exports share the same columns and you just want more rows, that is stacking (concatenation), a different operation that this module does not do.

Product and transactional data: Your product catalog (SKU, name, category, cost) lives separately from your order data (SKU, quantity, revenue, date). Joining on SKU produces a dataset with both product attributes and sales performance, enabling margin analysis, category comparison, and product profitability reports.

What Data Do You Need?

You need two or more datasets (CSV files or connector sources). The key requirements are:

A common join column: Each dataset needs a key column whose values can match the others. The module picks the key for you. In each dataset it takes the first column whose name starts with the dataset name and an underscore. If there is none, it takes the first text column, and if there is no text column, the first column. The report does not print the key it picked, so check the Key Overlap Analysis card: a pair with few shared keys usually means the wrong column was used. If your real key is not first, move it to the front (or make it the first text column) before you upload. Text keys such as customer_id or page URL work best.

Overlapping values: For inner joins, at least some values in the join column must match between datasets. If no values overlap, an inner join produces zero rows. The Join Details table in the report shows match rates so you can diagnose this immediately.

Join type selection: Choose inner, left, right, or full based on what you need to preserve (see the descriptions above). The default is inner join.

You can join from 2 up to 6 datasets. We have not published a tested maximum size. When joining more than two datasets, they are joined in order: dataset 1 is joined with dataset 2, then the result is joined with dataset 3, and so on. The join type can be set for each step.

How to Read the Report

The report follows the join from inputs to result.

The Executive Summary states which datasets were joined, the rows in and out, the overall match rate, the column count and the join type used at each step.

The Join Details table is the most important diagnostic. It shows each join step in order: which datasets were joined, the rows on the left and right side, the rows after the join, the join type, and the match rate. For an inner join, the row count usually goes down because only matches survive, but it can go up if a key repeats, as in the example above. A full join keeps at least as many rows as the larger input. The match rate in the table is result rows divided by the rows in the left side, so it can pass 100% when keys repeat. If the row count drops sharply on an inner join, few values matched: check the join column for trailing spaces, case mismatches and different ID formats.

The Column Summary lists every column in the result with the dataset it came from, its non-empty count and its share of NAs. A column with many NAs after a left or full join points to keys that did not match.

The Key Overlap Analysis compares the key values of each pair of datasets: how many are shared, and how many appear only on one side.

The Result Preview shows the first 20 rows of the joined result. Scan this to check the join worked as expected: are the columns from both sources present? Do the values line up correctly? Are there unexpected NAs (which might indicate a mismatch in the join column)?

When to Use Something Else

If you need to stack datasets vertically (same columns, different rows) rather than merging them horizontally (same rows, different columns), you need concatenation, not a join. This is common when combining monthly exports of the same report.

If your join columns have slightly different values that should match (e.g., "John Smith" vs. "john smith", or "Product A" vs. "Product A" with an extra space), a standard join will miss these matches. You need fuzzy matching or data cleaning before joining.

If you need to join on multiple columns simultaneously (e.g., match on both customer_id AND date), or need complex join conditions (join where value is within a range), pre-process your data in SQL or a spreadsheet tool before uploading.

If your goal is not to combine data but to analyze it, jump directly to the appropriate analysis module. For example, if you want to compare metrics across time periods, a trend analysis may be more appropriate than joining and then analyzing manually.

The R Code Behind the Analysis

Every report names its method and includes the R code that produced it, so you can reproduce, audit and cite it. The method code is fixed, not rewritten for each run, so the same data gives the same analysis every time.

The join operation uses merge() from base R. The all, all.x, and all.y arguments control the join type (inner, left, right, full). Join statistics are computed using nrow() before and after each merge step. When two datasets share a column name other than the key, the column from the later dataset gets a suffix made of an underscore and that dataset's name. Every step is visible in the code tab of your report, so you can verify exactly what was done and reproduce it independently.

Sources

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai