---
title: "Joined Data Marts in Google Sheets, without VLOOKUP chains"
canonical: "https://www.owox.com/blog/articles/joinable-data-marts-google-sheets"
updated: "2026-10-11"
---

# Joined Data Marts in Google Sheets: one report from several tables, no VLOOKUP chains

When someone asks for one sheet that combines orders, products and customers, the usual answer is a chain of exported tabs and VLOOKUPs that breaks quietly. This guide shows how joined Data Marts in OWOX Data Marts deliver the same sheet as one report from your warehouse: what the analyst declares (join fields, primary keys, how each joined field collapses, one storage), how the generated SQL keeps rows from multiplying, a worked example on demo data, the fan-out cases a correct join still cannot prevent, and a note to forward to the person who asked.

[Google Sheets Tips](/blog/topics/google-sheets-tips) · Updated October 11, 2026 · [Ievgen Krasovytskyi](/team/ievgen-krasovytskyi) · 16 min read

![Joined Data Marts in Google Sheets: one report from several tables, no VLOOKUP chains](https://cdn.owox.ai/www/content-marketing-article/joinable-data-marts-google-sheets/thumbnail/1.png/public)

“Can I have one sheet with sales by product category, and the customer segment next to it? Refreshed every Monday.” The data exists. It sits in three or four tables in your warehouse: order lines, products, categories, orders, customers. **The sheet they asked for is a join, and Sheets has lookups, not joins.**

So the usual answer is a set of exported tabs held together by VLOOKUPs, which works on the day you build it. This guide is the other answer: declare the join once on Data Marts in OWOX Data Marts and deliver one report to Google Sheets, with the join done in your warehouse. It starts with why the category totals do not match, then what to declare, what the product guarantees, a worked example, and **the cases where a correctly built join still gives a number you should not send**. The last section is a note for the person who asked.

Product behaviour below is quoted from the [Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/) and the [data model design guide](https://docs.owox.com/docs/getting-started/best-practices/data-model-design/). The figures come from the \[Demo\] E-commerce project: small, clean demo data, one quarter, runs made on 11 October 2026 through the OWOX MCP server and checked with SQL in BigQuery. The calls and the check query are printed below so you can repeat them on your own data.

## Why category revenue does not match total revenue

The symptom: the category totals in the lookup-built sheet add up to something other than the store’s revenue, and nobody can say which number is wrong. Four causes, two of them in the lookups:

*   **Lower: one line per order.** A lookup from an orders tab into an order-lines tab takes the first line it finds, so a multi-line order’s revenue lands on one category, or part of it goes missing.
*   **Higher: the order total on every line.** Pull the order’s total onto each of its lines and a three-line order is counted three times. In the demo quarter, category totals built that way come to $2,159,805.17 against $941,162.88 of actual line value (the query is printed with the worked example).
*   **Two definitions of revenue.** The order-line value can include cancelled and returned orders, while finance reports completed orders only. In the demo, Item Revenue counts every order status and Item Net Revenue counts completed orders only (cancelled and returned lines are zero): $941,162.88 against $789,011.69.
*   **What the store total includes.** In the demo a line’s value is the price paid times the quantity, with no shipping or tax fields. A store platform’s sales total may add shipping and tax or subtract discounts and refunds; check its definition before comparing.

The fix in spreadsheet terms: **use the line-items export, not the orders export**, as the base. One row per order line is the finest grain; every line carries its own category, and revenue adds up to the total. Then pick one revenue column and say which. Counts of coarser things, such as orders, still cannot be added up across categories; the fan-out section shows why.

## Why the VLOOKUP chain breaks

The spreadsheet version of this report has one tab per table and a lookup per borrowed column: line to product, product to category, line to order, order to customer. Four exports, four lookups, and each can be wrong without showing an error.

*   **VLOOKUP returns one match.** Google’s [VLOOKUP help](https://support.google.com/docs/answer/3093318) says that when several rows match, the value from the first match is returned. Look up from an orders tab into an order-lines tab and every multi-line order shows one line. In the demo data that is 871 of the 1,921 orders placed in the second quarter of 2026.
*   **The column number does not move.** `VLOOKUP(A2, Products!A:F, 6, FALSE)` reads the sixth column. Add a column to the export and it reads a different one, silently.
*   **Each tab refreshes on its own.** If the orders export ran this morning and the products export last week, the lookup joins two different days.

[INDEX and MATCH](/blog/articles/index-and-match-google-sheets) or [VLOOKUP with several criteria](/blog/articles/vlookup-with-multiple-criteria-google-sheets) fix the column number. They do not fix the first-match rule or the refresh timing, because the problem is not the formula. **A join belongs where the tables live.**

The other usual answer, one wide SQL query exported to the sheet, invites the classic mistake: join orders to their lines and count rows, and the demo quarter has 3,384 “orders” instead of 1,921.

## The Turning Point

OWOX Data Marts

See your first report built in real time. _15 minutes._

1.  Connect your data warehouse
2.  Pick your metrics
3.  Get a live Google Sheets report

In the time it takes to write a ticket. Then imagine never writing that ticket again.

[Book a Demo](/book-a-call)

We'll use your actual use case

## What a joined Data Mart is

A joined Data Mart is a **relationship** declared on one Data Mart, the source, pointing at another, the target, on one or more pairs of fields. Then “any report on the source Data Mart can mix native and joined fields side by side”, picked in one Report Columns list, and the product writes the join.

A relationship **runs one way**, from the Data Mart you report from; if A joins B and B joins C, fields from C appear in reports on A too. **The source is the base**: “A report starts from one Data Mart, its base, and keeps all of its rows. Joined Data Marts only add columns.” Existing reports do not change until you pick a joined field.

Together they form a [data model](/product/data-modeling); for how to cut each Data Mart, see [Data Mart design with flat tables](/blog/articles/optimal-data-mart-design-flat-tables).

## What you have to declare

| You declare | Where | What it decides |
| --- | --- | --- |
| **Both Data Marts on the same storage** | the Data Marts themselves | “The same storage type and connection”; cross-storage joins are not supported. The connection counts, not the dataset: a Data Mart names its table in full (`projectId.datasetId.tableId`), so one storage serves any dataset its account can read. |
| **Join fields**, one or more pairs | the relationship’s Join Settings | Which columns match; several pairs are combined with AND. Type mismatches are blocked at save. |
| **A primary key** on each Data Mart | the Output Schema | The fields that make one row unique. Without it there is no Unique Count. “OWOX trusts the key you declare”: prove it (below). |
| **Dedup** for each joined field | the relationship’s Report Fields | How the target’s rows collapse to one value per key: ANY\_VALUE, SUM, AVG, MIN, MAX, COUNT, COUNT\_DISTINCT or STRING\_AGG. |
| **Output Alias, description** | the relationship | The group name readers see, and the business sentence behind the join. |

There is **no separate “one-to-many” setting**: cardinality lives in the primary keys and the Dedup (ANY\_VALUE for a lookup, SUM for a customer’s total of orders, MIN on the order date for the first purchase).

**Prove each primary key before you rely on it.** OWOX does not enforce uniqueness; turn on the **Primary key uniqueness** check in [Data Quality Checks](https://docs.owox.com/docs/getting-started/setup-guide/data-quality-checks/), or compare `COUNT(*)` with `COUNT(DISTINCT key)` in the warehouse, or add a dbt `unique` test. A duplicated key would do damage quietly: under a SUM Dedup both copies are added into one value (by the definition of SUM; we did not run it). A target in **Draft** blocks the relationship until published.

## How the product keeps rows from multiplying

For your data person: in the documentation’s words, the generated SQL is built bottom-up: “the deepest joined Data Marts are pre-aggregated by their join key first, then merged into their parent, and finally LEFT JOIN-ed into the source Data Mart. This guarantees the result row count never exceeds the source Data Mart’s row count.”

We ran that guarantee against the hand-written mistake. The demo’s Orders Data Mart has its own relationship to Purchases (“The lines this order is made of”), so the query starts from Orders and reads the lines through the join. One call to the MCP server (`query_data_mart` on Orders: the Orders metric, Sum of Purchases Item Revenue, Purchases Unique Count, order date 1 April to 30 June 2026) returned **1,921 orders, 3,384 lines and $941,162.88**. The same three figures come from the tables directly, and the orders were not multiplied by their lines. We ran a query, not a saved Sheets report, so the row count of a delivered tab is not something we observed. **Preview SQL** on the report, or any report run in Run History, shows the query that was sent to your warehouse, with the pre-aggregation CTEs and the LEFT JOINs.

![A joined report built from the Purchases Data Mart: Products and Product Category joined on product\_id and category\_id, Orders and Customers joined on order\_id and customer\_id, each collapsed to one row per join key before a LEFT JOIN, so the report has one row per order line](https://cdn.owox.ai/www/content-marketing-article/joinable-data-marts-google-sheets/screens/1.png/public)

## A worked example: sales by category from order lines

Step zero is a Data Mart per table. For an existing BigQuery table, a [table-based Data Mart](https://docs.owox.com/docs/getting-started/setup-guide/table-data-mart/) takes its full name and generates the Output Schema, where you mark the primary key; the demo’s Data Marts point at views the same way. The \[Demo\] E-commerce project has:

| Data Mart | One row is | Primary key |
| --- | --- | --- |
| Purchases (the base) | an order line | `purchase_id` |
| Products | a product | `product_id` |
| Product Category | a category | `category_id` |
| Orders | an order | `order_id` |
| Customers | a customer | `customer_id` |

The relationships, as the MCP server lists them: Purchases → **Products** on `product_id` (“The product sold on this line”), Products → **Product Category** on `category_id`, Purchases → **Orders** on `order_id` (“The order this line belongs to”), Orders → **Customers** on `customer_id`. Orders also points back at Purchases, which the second run below uses.

Every arrow in the first run goes from many rows to one, on the target’s full primary key. **The base is the finest grain in the chain**, so every joined value is a lookup: the case the Dedup ANY\_VALUE is documented for (“a join with one row per key”). The MCP server does not show the demo’s Dedup settings; in your project they are in the relationship’s Report Fields tab.

The report: Category Name from Product Category, Item Revenue with Sum, a Count of Purchase ID, and the Orders Unique Count, filtered to orders dated 1 April to 30 June 2026. Because the other columns are aggregated, Category Name becomes the grouping ([Report Aggregations](https://docs.owox.com/docs/getting-started/setup-guide/report-aggregations/)): the warehouse adds up the lines and the tab gets one row per category. Deliver plain line rows instead and a large store meets Google’s limit of 20 million cells per spreadsheet ([Drive file limits](https://support.google.com/drive/answer/37603)).

| Category | Item Revenue, sum | Order lines | Orders with this category |
| --- | --- | --- | --- |
| Electronics | $622,107.76 | 1,157 | 921 |
| Apparel | $161,027.84 | 1,121 | 914 |
| Sports | $79,782.04 | 555 | 504 |
| Home & Kitchen | $78,245.24 | 551 | 493 |
| **Total** | **$941,162.88** | **3,384** | **1,921, not 2,832** |

The rows and the revenue and line totals are what OWOX returned, in US dollars. The table is reproducible: this is the call, and a second, independent run of it on the same day returned the same rows.

```
{"tool": "query_data_mart", "data_mart": "Purchases",
 "fields": ["Product Category Category Name",
   "Item Revenue", "Purchase ID", "Orders Unique Count"],
 "aggregations": [{"Item Revenue": "SUM"},
   {"Purchase ID": "COUNT"}],
 "filters": [{"Orders Order Date": "between",
   "from": "2026-04-01", "to": "2026-06-30"}]}
```

Field names are shown by their display names; the call uses the technical ones (`line_revenue`, `orders_e_commerce__unique_count` and so on). The MCP server returns rows, not the SQL the product generated, and Preview SQL needs a signed-in session, so **the generated SQL is not printed here**. What follows is our own check in BigQuery, on the views behind the Data Marts. It gives the 1,921 orders, the 871 multi-line orders and the 538 / 167 / 13 split used in this article:

```
WITH lines AS (
  SELECT p.order_id, p.line_revenue,
         c.category_name
  FROM `owox-demo.ecommerce.purchases` p
  JOIN `owox-demo.ecommerce.orders` o
    USING (order_id)
  JOIN `owox-demo.ecommerce.products` pr
    USING (product_id)
  JOIN `owox-demo.ecommerce.product_category` c
    USING (category_id)
  WHERE o.order_date
    BETWEEN '2026-04-01' AND '2026-06-30'
),
per_order AS (
  SELECT order_id, COUNT(*) AS line_count,
    COUNT(DISTINCT category_name) AS categories,
    SUM(line_revenue) AS order_value
  FROM lines GROUP BY order_id
)
SELECT COUNT(*) AS orders,
  SUM(line_count) AS line_rows,
  ROUND(SUM(order_value), 2) AS order_value,
  COUNTIF(line_count > 1) AS multi_line,
  COUNTIF(categories = 2) AS two,
  COUNTIF(categories = 3) AS three,
  COUNTIF(categories = 4) AS four
FROM per_order
```

Result: 1,921 orders, 3,384 line rows, 941,162.88, 871 multi-line, 538 / 167 / 13. Swap the final SELECT for `SELECT ROUND(SUM(order_value), 2) FROM lines JOIN per_order USING (order_id)` and you get the order total repeated on every line: 2,159,805.17. Finance decides whether a report shows Item Revenue or Item Net Revenue ($789,011.69 for the quarter).

Customer segment comes through Orders from Customers; the data model guide notes that “the customer’s current segment belongs to the customer”, so a past quarter uses today’s segments.

## Where fan-out still gets you

The row guarantee protects the join, not **the question you ask of it**. The documentation says: “OWOX Data Marts writes the SQL for joins so that a joined row is not counted twice. What the SQL cannot know is whether the rows it connects belong together.”

### Run 1: adding up a column that is not additive

Look at the last column of the table again. 921 + 914 + 504 + 493 is 2,832. The quarter had 1,921 orders. **Nothing is wrong with the report**: 718 orders contain lines from more than one category (538 from two, 167 from three, 13 from four), and each is correctly counted under every category it touched: 538 + 2 × 167 + 3 × 13 = 911 extra. Type `=SUM()` under that column in the sheet and you send a number 911 orders too high.

The rule: a count of something coarser than the base (orders, customers) is true per row and cannot be summed across rows. Take that total from a report whose base is the thing you count, here the Orders Data Mart. The Report Aggregations page gives its header as “Unique Count (Orders) in Google Sheets, Orders Unique Count everywhere else”; the MCP response shows the second form, and we did not open a delivered tab.

### Run 2: grouping finer than the join

“Group at or above the grain of the join,” says the data model guide. Its example: spend grouped by keyword while revenue joins by campaign, so “each keyword shows the campaign’s whole revenue: every row is overstated, while Totals stay right”. Those Totals are the report’s own, computed in the warehouse and returned through the data API (the MCP server, HTTP destinations); the docs say they are not written into Google Sheets. A `=SUM()` over the rows in the sheet adds up the overstated values.

We ran this case on the demo, the other way round: Orders as the base, grouped by Category Name, which sits two joins away on the many side (Orders → Purchases → Products → Product Category). Same quarter, same call shape as above with `data_mart: Orders`.

| Category | Orders | Purchases Item Revenue, sum | True line value (table above) |
| --- | --- | --- | --- |
| Electronics | 610 | $448,227.41 | $622,107.76 |
| Sports | 504 | $244,827.23 | $79,782.04 |
| Apparel | 415 | $71,114.90 | $161,027.84 |
| Home & Kitchen | 392 | $176,993.34 | $78,245.24 |
| **Total** | **1,921** | **$941,162.88** | **$941,162.88** |

**Every total is right and every row is wrong.** Each order was placed under exactly one category, with its whole value: Sports shows three times its real sales, Apparel less than half. The response carried no warning. Which category an order lands under followed a pattern we could reproduce: adding `MAX(category_name) AS last_category` to `per_order` above and grouping by it returns these four rows to the cent, so each order went to the alphabetically last category it contains. That is one run on one setup; the collapse depends on the Dedup of the fields on the path, which we could not read. The lesson holds either way: **a report that reconciles in total can still be wrong in every row**, so check a row, not only the total.

### Four more cases, from the documentation

These we did not run; they are the documented behaviour.

*   **A key that covers only part of the target.** Several target rows collapse into one value: a list with STRING\_AGG, one arbitrary value with ANY\_VALUE. Join on the full primary key.
*   **A plain COUNT across the join** counts the base rows that found a match, not the joined rows. Use the joined Data Mart’s **Unique Count**, which needs a primary key.
*   **Rows the join leaves out.** The data model guide: “A joined row that matches no row of the base is left out, and an empty (NULL) key matches nothing.” So spend joined into an Orders report is the spend of campaigns that have orders. Make the Data Mart whose total must be complete the base.
*   **Sums through a chain.** A deeper field “is collapsed again at each Data Mart on the way up”; SUM and COUNT stay exact only while the Data Mart in between has one row per deeper key. A Sum chosen in the report’s columns is computed over the deeper Data Mart’s own rows and stays right.

So the honest summary: **the product prevents row multiplication by construction, and prevents double-counting only for relationships declared on the right keys, at the right grain, with a Dedup that matches the question.** Before a joined report goes to anyone, check one total against the Data Mart it came from, and read the generated SQL once.

One thing to look for in that SQL is where a **filter on a joined field** lands. The MCP server’s `query_data_mart` tool description is explicit for its own queries: filters are “post-join”, applied to the blended result, and “slices” are “pre-join”, narrowing a joined Data Mart before it is blended in. A post-join filter on a joined field drops base rows with no match. The docs pages do not say the same for a saved report’s filters, so read its SQL. And **BI tools handle the count problem differently**: Looker’s [symmetric aggregates](https://cloud.google.com/looker/docs/best-practices/understanding-symmetric-aggregates) count each order once in a total across joined rows, and its docs note this depends on “a unique primary key and the correct join relationship”. A sheet has no such total, so leave order or customer counts by category to the BI tool, or deliver the true total from a report based on Orders.

## Deliver the joined report to Google Sheets

The data travels from the source to your warehouse, then to the sheet. More in [Spreadsheets](/product/spreadsheets).

In OWOX Data Marts Cloud:

1.  **Add a Google Sheets destination** with **Connect Google Account** (OAuth); a service account key is the [documented alternative](https://docs.owox.com/docs/destinations/supported-destinations/google-sheets/) for unattended workflows.
2.  **Create the report**: on the source Data Mart’s Destinations tab, **\+ New Report**, with the link to the target tab including its `gid` ([first Data Mart guide](https://docs.owox.com/docs/getting-started/first-data-mart/)).
3.  **Pick the columns** in Report Columns: native fields first, each relationship a group under its Output Alias.
4.  **Schedule it** with a [Report Run trigger](https://docs.owox.com/docs/getting-started/setup-guide/report-triggers/): daily, weekly, monthly or at an interval.

A joined header carries the group’s name: “revenue (orders) in Google Sheets”. **Copy as Data Mart** turns a joined report’s SQL into a Data Mart of its own. See also [OWOX Data Marts vs Connected Sheets](/blog/articles/owox-data-marts-vs-connected-sheets) and [why cross-tool joins need a warehouse](/blog/articles/mcp-analytics-limitations-cross-tool-joins).

## A note for the person who asked

If you were asked for “one sheet with everything”, this part is for them. Copy it, but **fill in every bracket before you send it**: each one is a choice only you can make, and a note with a bracket left in it leaves them guessing which number to quote.

The \[Sales by category\] tab now comes from one report instead of several pasted tables. Everything in it comes from one query and refreshes together, every \[Monday at 7:00\], so the columns no longer drift apart. The report adds up every product sold in the period by category before it reaches the tab, so you get one row per category.

**Why your category revenue is higher than finance’s total.** \[Item Revenue\] counts everything ordered, cancelled and returned orders included. Finance’s revenue counts completed orders only. Which to use: finance and anything that reports results use \[Item Net Revenue\]; for demand, what customers tried to buy, use \[Item Revenue / Item Net Revenue\]. On Monday, quote \[column\]; the old sheet mixed the two.

**Do not add up the \[Orders\] column.** It counts an order under every category it contains: an order with a shirt and a phone appears under both. The real number of orders is in \[cell or tab\].

Where the tab shows customer segment, it is each customer’s segment today, not the one they had when they ordered, so last quarter by segment uses today’s segments.

The old sheet is retired from \[date\]. Numbers taken from it before then \[have been restated in the new tab / stay labelled as old-method figures\].

If a number looks wrong, send me the row. Every figure in the tab comes from a query I can open and show you.

## Your New Normal

Turn your data into decisions.

Governed data marts give you the clean foundation ML needs to actually work.

*   No AI hallucinations
*   Analyst-governed definitions
*   Every number traces to SQL

[Get started free](https://www.owox.com/app-signup)

## FAQ

## Frequently Asked Questions

Can a joined report double-count?

Its rows cannot multiply: joined Data Marts are collapsed to one row per join key before the join, so the result never has more rows than the Data Mart the report starts from ([Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/)). Its numbers can still mislead. A count of orders per product category is right on each row and too high when the rows are added up, because one order can contain several categories. Grouping finer than the join key, or joining on part of a key, overstates values too ([data model design guide](https://docs.owox.com/docs/getting-started/best-practices/data-model-design/)).

What are joined Data Marts in Google Sheets?

A joined Data Mart is a relationship an analyst declares on one Data Mart, pointing at another Data Mart on the same storage, on one or more pairs of matching fields. Any Google Sheets report on the first Data Mart can then pick the second one's columns next to its own, and OWOX Data Marts writes the join SQL that runs in your warehouse ([Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/)).

Do joined Data Marts have to be in the same warehouse?

Yes. Both Data Marts must live on the same storage, and every Data Mart in a chain on the same storage type and connection: cross-storage joins are not supported. The supported storages are Google BigQuery, Snowflake, AWS Redshift, AWS Athena and Databricks ([Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/)).

How do I tell OWOX that a relationship is one-to-many?

There is no separate cardinality setting. You declare a primary key on each Data Mart in its Output Schema, which says what one row is, and a Dedup for each joined field, which says how several matching rows collapse into one value: ANY\_VALUE for a lookup with one row per key, SUM for a total, MIN or MAX for the first or last date ([Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/)).

How can I check the SQL behind a joined report?

Click Preview SQL on the report, or on the report's row in the Data Mart's Destinations tab, to see the query the next run will send. Run History shows the SQL each past run sent to your storage, with the pre-aggregation steps and the LEFT JOINs ([Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts/)).

## Who wrote this

![Ievgen Krasovytskyi](https://cdn.owox.ai/www/webflow/68404586b341508a789a4aa5_.png/public)

[Ievgen Krasovytskyi](/team/ievgen-krasovytskyi) · Head of Marketing

Ievgen Krasovytskyi is the Head of Marketing at OWOX, leading strategy across content, SEO, product marketing, and AI-powered automation. With deep expertise in analytics infrastructure, data warehouses, and marketing technology, he builds systems that connect marketing performance to business outcomes. Ievgen writes about SaaS growth, analytics workflows, and the future of AI in marketing operations.

[LinkedIn](https://www.linkedin.com/in/ievgen-krasovytskyi-a38a1253/) · [All articles](/team/ievgen-krasovytskyi)

[Google Sheets Tips](/blog/topics/google-sheets-tips)

## Related articles

[Google Sheets Tips · ARRAYFORMULA in Google Sheets: how to use array formulas · October 2, 2026](/blog/articles/array-formulas-google-sheets)

[Google Sheets Tips · How to average in Google Sheets: AVERAGE and AVERAGEIF · October 2, 2026](/blog/articles/average-functions-google-sheets)

[Google Sheets Tips · How to make a bar graph in Google Sheets, with error bars · October 2, 2026](/blog/articles/bar-graphs-in-google-sheets)

[See all articles →](/blog/articles)

## Book a call

You are booked. The invitation is on its way to , with the calendar invite and the meeting link.

[Reschedule](#)

[Cancel](#)

## Links

Pages this page links to, on this site and on docs.owox.com. Where the page has a Markdown twin, its address follows the link.

- [Google Sheets Tips](https://www.owox.com/blog/topics/google-sheets-tips) — /blog/topics/google-sheets-tips.md
- [Ievgen Krasovytskyi](https://www.owox.com/team/ievgen-krasovytskyi) — /team/ievgen-krasovytskyi.md
- [Joinable Data Marts documentation](https://docs.owox.com/docs/getting-started/setup-guide/joinable-data-marts)
- [data model design guide](https://docs.owox.com/docs/getting-started/best-practices/data-model-design)
- [INDEX and MATCH](https://www.owox.com/blog/articles/index-and-match-google-sheets) — /blog/articles/index-and-match-google-sheets.md
- [VLOOKUP with several criteria](https://www.owox.com/blog/articles/vlookup-with-multiple-criteria-google-sheets) — /blog/articles/vlookup-with-multiple-criteria-google-sheets.md
- [Book a Demo](https://www.owox.com/book-a-call) — /book-a-call.md
- [data model](https://www.owox.com/product/data-modeling) — /product/data-modeling.md
- [Data Mart design with flat tables](https://www.owox.com/blog/articles/optimal-data-mart-design-flat-tables) — /blog/articles/optimal-data-mart-design-flat-tables.md
- [Data Quality Checks](https://docs.owox.com/docs/getting-started/setup-guide/data-quality-checks)
- [table-based Data Mart](https://docs.owox.com/docs/getting-started/setup-guide/table-data-mart)
- [Report Aggregations](https://docs.owox.com/docs/getting-started/setup-guide/report-aggregations)
- [Spreadsheets](https://www.owox.com/product/spreadsheets) — /product/spreadsheets.md
- [documented alternative](https://docs.owox.com/docs/destinations/supported-destinations/google-sheets)
- [first Data Mart guide](https://docs.owox.com/docs/getting-started/first-data-mart)
- [Report Run trigger](https://docs.owox.com/docs/getting-started/setup-guide/report-triggers)
- [OWOX Data Marts vs Connected Sheets](https://www.owox.com/blog/articles/owox-data-marts-vs-connected-sheets) — /blog/articles/owox-data-marts-vs-connected-sheets.md
- [why cross-tool joins need a warehouse](https://www.owox.com/blog/articles/mcp-analytics-limitations-cross-tool-joins) — /blog/articles/mcp-analytics-limitations-cross-tool-joins.md
- [Get started free](https://www.owox.com/app-signup)
- [Google Sheets Tips · ARRAYFORMULA in Google Sheets: how to use array formulas · October 2, 2026](https://www.owox.com/blog/articles/array-formulas-google-sheets) — /blog/articles/array-formulas-google-sheets.md
- [Google Sheets Tips · How to average in Google Sheets: AVERAGE and AVERAGEIF · October 2, 2026](https://www.owox.com/blog/articles/average-functions-google-sheets) — /blog/articles/average-functions-google-sheets.md
- [Google Sheets Tips · How to make a bar graph in Google Sheets, with error bars · October 2, 2026](https://www.owox.com/blog/articles/bar-graphs-in-google-sheets) — /blog/articles/bar-graphs-in-google-sheets.md
- [See all articles →](https://www.owox.com/blog/articles) — /blog/articles.md
