---
title: "QUERY with CONCATENATE in Google Sheets: join two columns"
canonical: "https://www.owox.com/blog/articles/query-with-concatenate-google-sheets"
updated: "2026-10-05"
---

# QUERY with CONCATENATE in Google Sheets: how to concatenate two columns

The QUERY language cannot join columns: select A & B returns an error. This guide shows the three ways that work: concatenate the columns before the query, join the columns of the query result, or collect the result into one cell, plus the simpler formulas for cases where no QUERY is needed.

[Google Sheets Tips](/blog/topics/google-sheets-tips) · Updated October 2, 2026 · [Vadym Kramarenko](/team/vadym-kramarenko) · 8 min read

![QUERY with CONCATENATE in Google Sheets: how to concatenate two columns](https://cdn.owox.ai/www/webflow/69f32abd10eccc802151151a_USE-CONCATENATE-AND-QUERY.png/public)

The QUERY language in Google Sheets has no concatenation operator: `"select B & C"` returns #VALUE!. To concatenate two columns in a QUERY, join them **before** the query, inside curly braces, and wrap the formula in ARRAYFORMULA:

> `=ARRAYFORMULA(QUERY({C3:C12 & " - " & D3:D12, D3:D12}, "select Col1 where Col2 > 2000", 0))`

The formula returns one column, sales rep and amount joined with a dash, for the rows where the amount is above 2,000.

![Google Sheets with a sales table in B2:D12 and the formula =ARRAYFORMULA(QUERY({C3:C12 & “ - “ & D3:D12, D3:D12}, “select Col1 where Col2 > 2000”, 0)) in F3, returning six joined values such as Sarah - 2300](https://cdn.owox.ai/www/content-marketing-article/query-with-concatenate-google-sheets/screens/2.png/public)

There are three ways to combine the two functions, depending on where the join happens:

| You need | Approach |
| --- | --- |
| A joined column that the query can also filter or sort by | Concatenate before the query |
| The columns of the query result joined row by row | Join after the query |
| All rows of the query result in one cell | TEXTJOIN around the query |

## What each function does

> `=QUERY(data, query, [headers])`

[QUERY](/blog/articles/query-function-google-sheets) filters, sorts and summarizes a range with a query string. Its language can select columns, compare them and aggregate them, but it cannot build a new text value from two columns.

> `=CONCATENATE(string1, [string2, ...])`

[CONCATENATE](/blog/articles/concatenate-and-ampersand-google-sheets) joins text. The ampersand (&) does the same and is shorter: `=B3 & " " & C3`.

## Concatenate before the query

Build the joined column first and hand it to QUERY as part of the data:

> `=ARRAYFORMULA(QUERY({C3:C12 & " - " & D3:D12, D3:D12}, "select Col1 where Col2 > 2000", 0))`

*   **{C3:C12 & “ - “ & D3:D12, D3:D12}:** a table built in the formula. Its first column is the joined text, its second column is the amount.
*   **“select Col1 where Col2 > 2000”:** when the data is built in curly braces and not taken from the sheet, columns are named Col1, Col2 and so on, in the order they appear in the braces. Letters such as C and D do not work here.
*   **0:** the data has no header row.
*   **ARRAYFORMULA:** makes the ampersand work on the whole range. Without it the formula returns an error.

Because the joined column is part of the data, the query can use it like any other column: sort by it with `order by Col1`, or filter it with `where Col1 contains 'Sarah'`.

## 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

## Join the columns of the query result

The second way runs the query first and joins its columns afterwards.

### All columns of each row

This formula joins the cells of every row with a space:

> `=TRANSPOSE(QUERY(TRANSPOSE(B3:C12),,6^6))`

![Google Sheets with product names in column B, codes in column C and the formula =TRANSPOSE(QUERY(TRANSPOSE(B3:C12),,6^6)) in D3 returning the name and the code of each row in one cell, for example SmartWatch Pro 101](https://cdn.owox.ai/www/webflow/6786f69c75b380f9905e1c12_61480.png/public)

It relies on the third argument of QUERY, the number of header rows. When a range has several header rows, QUERY merges them into one, with a space between the values. The formula turns that into a join:

*   **TRANSPOSE(B3:C12):** flips the range, so each row becomes a column.
*   **QUERY(…, , 6^6):** no query string, and 6^6 (46,656) header rows, more than the data has. QUERY treats every row as a header and merges each column into a single cell.
*   **TRANSPOSE:** flips the result back into a column.

### Only the rows that match a condition

Put a query inside to filter the rows first. This formula keeps the products with the code 101, joins name and code, and replaces the spaces with underscores:

> `=ARRAYFORMULA(SUBSTITUTE(TRIM(TRANSPOSE(QUERY(TRANSPOSE(QUERY(B3:C12, "select * where C contains '101'")),,COLUMNS(QUERY(B3:C12, "select * where C contains '101'"))))), " ", "_"))`

![Google Sheets with a nested formula in D3 that filters the products with the code 101 with QUERY, joins name and code and replaces spaces with underscores, returning values such as SmartWatch\_Pro\_101](https://cdn.owox.ai/www/webflow/6786f69d939f6ae9babc239b_61484.png/public)

*   **QUERY(B3:C12, “select \* where C contains ‘101’”):** the rows with the code 101.
*   **TRANSPOSE and the outer QUERY:** the header-row join from the previous example.
*   **TRIM:** removes extra spaces.
*   **SUBSTITUTE(…, “ “, “\_”):** replaces every space with an underscore, including the spaces inside product names: SmartWatch Pro becomes SmartWatch\_Pro.

A shorter formula for the same job uses BYROW, which applies a function to each row of the query result, and [TEXTJOIN](/blog/articles/join-textjoin-functions-google-sheets), which takes the separator as an argument:

> `=BYROW(QUERY(B3:C12, "select * where C contains '101'", 0), LAMBDA(row, TEXTJOIN("_", TRUE, row)))`

Here the separator goes only between the columns, and the product names keep their spaces.

## Collect the query result into one cell

To list the values a query returns in a single cell, wrap the query in TEXTJOIN:

> `=TEXTJOIN(", ", TRUE, QUERY(C3:D12, "select C where D > 2000", 0))`

*   **“, “:** the separator.
*   **TRUE:** skip empty cells.
*   **QUERY(…):** the sales reps whose amount is above 2,000.

The result is one cell: Sarah, John, Mike, John, Sarah, Sarah. JOIN works the same way for a single column: `=JOIN(", ", QUERY(C3:D12, "select C where D > 2000", 0))`.

## When you do not need QUERY

If there is nothing to filter, sort or aggregate, the join is simpler without QUERY.

### ARRAYFORMULA with an ampersand

> `=ARRAYFORMULA(B3:B12 & "_" & C3:C12)`

![Google Sheets with the formula =ARRAYFORMULA(B3:B12&“\_”&C3:C12) in D3 returning the product name and the code joined with an underscore for all ten rows](https://cdn.owox.ai/www/webflow/6786f69e79c126c9043025a2_61488.png/public)

One formula in the first cell joins the two columns for every row. See the guide to [ARRAYFORMULA](/blog/articles/array-formulas-google-sheets) for how this works.

### CONCATENATE for one row

> `=CONCATENATE(B3, " ", C3)`

![Google Sheets with the formula =CONCATENATE(B3,” “,C3) in D3 returning SmartWatch Pro 101, copied down for all rows](https://cdn.owox.ai/www/webflow/6786f69d1dc178742477c3cf_61494.png/public)

CONCATENATE joins the cells you list, with the separators you put between them. Copy the formula down for the other rows.

### IF with CONCATENATE for a condition

> `=IF(C3=101, CONCATENATE(C3, "_", B3), "")`

![Google Sheets with the formula =IF(C3=101, CONCATENATE(C3, “\_”, B3), “”) in column D returning 101\_SmartWatch Pro for the rows with the code 101 and an empty cell for the others](https://cdn.owox.ai/www/webflow/6786f69d448fff4ca3bbf754_61492.png/public)

[IF](/blog/articles/if-function-google-sheets) joins the code and the name only where the code equals 101 and leaves the other cells empty.

### ARRAYFORMULA with CONCAT

> `=ARRAYFORMULA(CONCAT(B3:B12, C3:C12))`

![Google Sheets with the formula =ARRAYFORMULA(CONCAT(B3:B12,C3:C12)) in D3 returning the product name and the code joined without a separator, for example SmartWatch Pro101](https://cdn.owox.ai/www/webflow/6786f69d72a607a461432236_61496.png/public)

CONCAT joins exactly two values and has no room for a separator, so the result runs together: SmartWatch Pro101. Use the ampersand when you need a separator.

## Errors and how to fix them

**⚠️ Error:** #VALUE! for a query such as `"select B & C"`.

✅ **Solution:** The query language has no & operator and no CONCATENATE. Join the columns before the query, in curly braces, or after it.

**⚠️ Error:** The formula with curly braces returns an error.

✅ **Solution:** Check two things. The formula has to be wrapped in ARRAYFORMULA, and the query has to use Col1, Col2 instead of column letters.

**⚠️ Error:** Formula parse error after pasting a formula from a web page or a document.

✅ **Solution:** The quotes were replaced with typographic ones (“ ” or ‘ ’). Retype them as straight quotes in the formula bar.

**⚠️ Error:** The joined values have double or trailing spaces.

✅ **Solution:** Empty cells in the range can leave a space behind. Wrap the result in [TRIM](/blog/articles/trim-clean-t-functions-google-sheets).

## Related Google Sheets functions

Functions that are often used next to QUERY and CONCATENATE, each with its own guide:

*   [QUERY with IMPORTRANGE](/blog/articles/query-with-importrange-google-sheets): Queries a range that lives in another spreadsheet.
*   [QUERY date filtering](/blog/articles/date-filtering-with-query-google-sheets): Filters rows by a date or a date range inside a QUERY formula.
*   [FILTER](/blog/articles/google-sheets-filter-function): Extracts the rows that meet a condition, without a query string.
*   [VLOOKUP](/blog/articles/vlookup-in-google-sheets): Finds a value in a table by a key.
*   [XLOOKUP](/blog/articles/key-differences-of-xlookup-vs-vlookup): A lookup that can search in any direction.
*   [Pivot tables](/blog/articles/pivot-tables-google-sheets): Summarize a table by category without formulas.
*   [REPT](/blog/articles/rept-function-google-sheets): Repeats text a set number of times.
*   [LEFT/RIGHT/MID](/blog/articles/left-right-mid-functions-google-sheets): Extracts a set number of characters from the start, end or middle of a text string.

## Joined keys are a sign that the data needs a better source

Columns are usually concatenated in a sheet for one of two reasons: to build a label for a report, or to build a key, such as campaign plus date, that lets one export be matched with another. The second case is a join done by hand, and it breaks as soon as one of the exports changes its naming.

The [OWOX Data Marts extension for Google Sheets](https://workspace.google.com/marketplace/app/owox_data_marts/94902851409) covers that half. A data analyst publishes a data mart (a table or an SQL query in your data warehouse) and makes it available for reports. In Sheets you open the extension, pick the data mart, tick the columns you want and run it. The rows land in a tab, ready for your formulas.

![Google Sheets with the OWOX Data Marts extension open beside an ad spend table: the Unified Ad Spend data mart, nine of fourteen columns ticked and a Save & Run button – the rows in the sheet come from a governed data mart, not a pasted export](https://cdn.owox.ai/www/content-marketing-article/query-with-concatenate-google-sheets/screens/1.png/public)

What changes for the person who maintains the sheet:

*   **Refresh replaces re-export.** _Refresh current report_ in the extension menu re-runs the same pull, and a scheduled refresh keeps the tab current without anyone touching it.
*   **The source is written on the sheet.** The header cell of an imported tab carries a note with the data mart it came from, the time of the import and a link to the report.
*   **No SQL and no warehouse access needed.** The analyst decides which data marts and columns are available. You choose from that list.

The extension does not write or change the analyst’s SQL. When the match between two sources is made there once, the sheet receives the columns already side by side, and concatenation goes back to being a formatting task. The extension comes with the OWOX Cloud plans of [OWOX Data Marts](https://www.owox.com/app-signup); [spreadsheet reporting](/features/spreadsheet-reporting) describes both ways data reaches a sheet.

## 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 CONCATENATE be used inside a QUERY statement?

No. The QUERY language has no concatenation operator, so "select A & B" returns an error. Join the columns before the query, =ARRAYFORMULA(QUERY({A2:A & " " & B2:B, C2:C}, "select Col1 where Col2 > 100", 0)), or join the columns of the result afterwards with TEXTJOIN or the ampersand.

Why does my attempt to concatenate QUERY results return an error?

The usual causes are an ampersand or CONCATENATE written inside the query string, which the query language does not support; a formula with curly braces that is not wrapped in ARRAYFORMULA; column letters used where Col1 and Col2 are required; or typographic quotes pasted from a document.

Can I use QUERY to fetch data from multiple columns and then concatenate them in 1 column?

Yes, you can use QUERY to fetch data from multiple columns and then concatenate them into 1 column. After retrieving the data using QUERY, you can use the & operator or the CONCATENATE function to combine the values from different columns into a single column.

How can I concatenate rows returned by a QUERY function into a single cell?

You can concatenate rows returned by a QUERY function into a single cell with the TEXTJOIN function. First, use QUERY to retrieve the desired rows, then wrap the QUERY function with TEXTJOIN, specifying the delimiter you want to use between each row.

## Who wrote this

![Vadym Kramarenko](https://cdn.owox.ai/www/webflow/6798e703afdf787334941cde_Vadym-Kramarenko.png/public)

[Vadym Kramarenko](/team/vadym-kramarenko) · Growth Marketing Manager

Vadym Kramarenko is a Growth Marketing Manager at OWOX, where he drives user acquisition and product-led growth strategies. He hosts the OWOX podcast, interviewing analytics professionals about data-driven marketing, attribution, and reporting best practices. Vadym specializes in turning complex analytics concepts into practical, actionable marketing frameworks.

[LinkedIn](https://www.linkedin.com/in/vadim-kramarenko/) · [All articles](/team/vadym-kramarenko)

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

## Related articles

[Google Sheets Tips · Pivot tables in Google Sheets: how to create and use them · October 2, 2026](/blog/articles/pivot-tables-google-sheets)

[Google Sheets Tips · UNIQUE function in Google Sheets: syntax, examples and errors · October 2, 2026](/blog/articles/unique-function-google-sheets)

[Google Sheets Tips · ARRAYFORMULA in Google Sheets: how to use array formulas · October 2, 2026](/blog/articles/array-formulas-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
- [Vadym Kramarenko](https://www.owox.com/team/vadym-kramarenko) — /team/vadym-kramarenko.md
- [QUERY](https://www.owox.com/blog/articles/query-function-google-sheets) — /blog/articles/query-function-google-sheets.md
- [CONCATENATE](https://www.owox.com/blog/articles/concatenate-and-ampersand-google-sheets) — /blog/articles/concatenate-and-ampersand-google-sheets.md
- [Book a Demo](https://www.owox.com/book-a-call) — /book-a-call.md
- [TEXTJOIN](https://www.owox.com/blog/articles/join-textjoin-functions-google-sheets) — /blog/articles/join-textjoin-functions-google-sheets.md
- [ARRAYFORMULA](https://www.owox.com/blog/articles/array-formulas-google-sheets) — /blog/articles/array-formulas-google-sheets.md
- [IF](https://www.owox.com/blog/articles/if-function-google-sheets) — /blog/articles/if-function-google-sheets.md
- [TRIM](https://www.owox.com/blog/articles/trim-clean-t-functions-google-sheets) — /blog/articles/trim-clean-t-functions-google-sheets.md
- [QUERY with IMPORTRANGE](https://www.owox.com/blog/articles/query-with-importrange-google-sheets) — /blog/articles/query-with-importrange-google-sheets.md
- [QUERY date filtering](https://www.owox.com/blog/articles/date-filtering-with-query-google-sheets) — /blog/articles/date-filtering-with-query-google-sheets.md
- [FILTER](https://www.owox.com/blog/articles/google-sheets-filter-function) — /blog/articles/google-sheets-filter-function.md
- [VLOOKUP](https://www.owox.com/blog/articles/vlookup-in-google-sheets) — /blog/articles/vlookup-in-google-sheets.md
- [XLOOKUP](https://www.owox.com/blog/articles/key-differences-of-xlookup-vs-vlookup) — /blog/articles/key-differences-of-xlookup-vs-vlookup.md
- [Pivot tables](https://www.owox.com/blog/articles/pivot-tables-google-sheets) — /blog/articles/pivot-tables-google-sheets.md
- [REPT](https://www.owox.com/blog/articles/rept-function-google-sheets) — /blog/articles/rept-function-google-sheets.md
- [LEFT/RIGHT/MID](https://www.owox.com/blog/articles/left-right-mid-functions-google-sheets) — /blog/articles/left-right-mid-functions-google-sheets.md
- [OWOX Data Marts](https://www.owox.com/app-signup)
- [spreadsheet reporting](https://www.owox.com/features/spreadsheet-reporting) — /features/spreadsheet-reporting.md
- [Google Sheets Tips · UNIQUE function in Google Sheets: syntax, examples and errors · October 2, 2026](https://www.owox.com/blog/articles/unique-function-google-sheets) — /blog/articles/unique-function-google-sheets.md
- [See all articles →](https://www.owox.com/blog/articles) — /blog/articles.md
