Google Sheets: How to Remove Headers from QUERY Result

The Google Sheets function “QUERY” is one of the handiest functions in a Google Sheets wizard’s toolbox. And one of the great things about QUERY is that it can interpret the first row of data as a header row. But what if you don’t want the header row at all?

There are a few ways to prevent headers from being generated in the output.

But before we get into the weeds, let’s take a look at the raw dataset we’ll be working with.

ABC
1NameGenderAge
2BobMale22
3SharonFemale33
4JerryMale44
5MarthaFemale55

Exclude headers from input

First, you can simply exclude the header row from the input and set the third parameter in your QUERY function to 0 (which tells the QUERY function that there are 0 header rows in the range).

And here’s our query to remove the header row by excluding it from our range and setting the headers parameter to 0.

=QUERY(A2:C, "SELECT A, B, C", 0)

And here’s the result we get.

BobMale22
SharonFemale33
JerryMale44
MarthaFemale55

Notice that the range in our query is set to A2:C rather than A:C. Also, notice the addition of the third parameter which is set to 0.

Exclude headers from output

The second option is to use the entire range and apply an OFFSET clause to your query, again also setting the third parameter in your function to 0.

=QUERY(A:C, "SELECT A, B, C OFFSET 1", 0)
BobMale22
SharonFemale33
JerryMale44
MarthaFemale55

The OFFSET clause skips the first X number of rows in the result or your query. For example, OFFSET 3 would skip the first 3 rows returned in the resulting dataset. In our case, we’re skipping just one row in the result: the header row.

In this particular instance, we’re treating the first row as though it weren’t a header row. Here’s what I mean. We’ve included all rows A:C. And when we set the third parameter to 0, we’re telling the QUERY function that there are no header rows in that range. Then, through our OFFSET clause, we tell QUERY, “Go and get all the data from A:C. But when you print out the result, don’t print out the first row.”

These two solutions are both great solutions. However, there is one case where the QUERY function will generate headers automatically even if you remove the header row from your input, set headers to 0, and apply an OFFSET clause. That case is when using data manipulation functions such as SUM(), AVG(), or NOW(). When using these functions, QUERY will automatically add a header row specifying the function used. And no amount of changing the range, setting headers to 0, or applying offsets will get rid of it.

=QUERY(A2:C, "SELECT B, AVG(C) GROUP BY B OFFSET 1", 0)
avg
Female49.5
Male27.5

Fortunately, there’s a workaround. It’s a bit of a hack, really, but it gets the job done.

Remove auto-generated headers

To remove the automatically generated header row from your result when using a data manipulation function with QUERY, set an empty LABEL for each of the data manipulation functions like so:

=QUERY(A2:C, "SELECT B, AVG(C) WHERE NOT B = '' GROUP BY B LABEL AVG(C) ''", 0)

Here we’ve excluded the header row from our input range (A2:C). We’re applying the AVG() aggregation function to column C. We’re only retrieving those rows that have data (WHERE NOT B = ”). And, to get rid of that pesky header that gets automatically generated (because we’re using the AVG() function), we’re applying an empty LABEL to each instance of data manipulation, i.e. AVG(C). Now that all headers are empty, Google Sheets prints out a range with no headers.

Female49.5
Male27.5

Outstanding! There it is. A QUERY result in our Google Sheet that has no headers at all (even when using data manipulation functions). Awesome.

Subscribe to Learn More Google Sheets Secrets

27 thoughts on “Google Sheets: How to Remove Headers from QUERY Result”

  1. This also works. It takes the inner QUERY as a range and lists all except the 1st row:

    =QUERY(QUERY(A2:C, “SELECT B, AVG(C)”, 0), “SELECT * OFFSET 1”, 0)

    Reply
    • Hey Gabor. Good suggestion. Your solution just requires a little tweaking in order to work. You’ll need a GROUP BY clause in the inner QUERY (since you’re using an aggregate function), and a WHERE clause to eliminate empty rows.

      =QUERY(QUERY(A2:C, "SELECT B, AVG(C) WHERE NOT B = '' GROUP BY B", 0), "SELECT * OFFSET 1", 0)

      Reply
  2. Brilliant! A perfect, and perfectly described fix to dealing with query headers.
    Thanks so much for the direct and succinct solution.

    Reply
  3. Perhaps these suggestions don’t work for pivots within a query such as:

    = QUERY(‘Class hours’!A2:C11,”select A, sum(C) WHERE A= ‘” & A5 & “‘ group by A pivot B”,0)

    Here, I am referencing the content of A5 within “Class hours” tab in order to create a pivot table with content from column C in the same tab. The pivot columns are labelled with the contents of the column B on which the pivot is performed.

    There are two problems: (a) the actual result, i.e. SUM(C) is returned on row below, one columns to the right, while the pivot labels are returned in the same row as the query function cell, (b) in the next row, I need to pull in the content of A6 to perform the same query. This I can’t do as the previous query has taken up two rows.

    I came across the following possible workaround that works to change date formats https://infoinspired.com/google-docs/spreadsheet/format-query-pivot-header-row-in-google-sheets/

    This has parallels to my issue, but doesn’t solve the problem. Any suggestions will be really great. Thanks.

    Reply
    • Wrapping your query inside another query should get the job done. In your inner query, set the label of column A to an empty value and set the “headers” parameter to 1. In your outer query, set the “headers” parameter to 0.

      Using the example table in my post, the formula might look like:
      =QUERY(QUERY(A:C,"SELECT A, SUM(C) WHERE A != '' GROUP BY A PIVOT B LABEL A ''",1),"SELECT * WHERE Col1 != ''",0)

      Using your formula:
      =QUERY(QUERY('Class hours'!A2:C11,"select A, sum(C) WHERE A= '" & A5 & "' group by A pivot B label A ''",1),"SELECT * WHERE Col1 != ''",0)

      Reply
  4. Hi,
    I am having to issues trying to complete a task:
    (1) How do you remove headers from an imported table using the following: =transpose(importhtml(“https://wallmine.com/asx/”&A6,”Table”,$B$4)).
    A6=ticker and $B$4 =table number. I need the header for the first row only, then from row three I need only the data, as I have multiple stocks I need the data on.
    (2) As I have 19 table to get data on, is there a way to add all tables ie 1:19 in the one function rather than doing 19 importhtml functions for each row. I tried to use Arrayformula, but it appears that you cannot use this with the importhtml function.

    Reply
  5. Hi Dave,
    As I have now found the xPaths to enable me to reduce the number of xPaths from 19 to two, , I am happy with that, so you can now close this issue.

    Regards,

    Reply
    • XLOOKUP is virtually the same as VLOOKUP. However, VLOOKUP does not support “last to first item” searching, i.e. to return the last result rather than the first. So, to return the last result, you’d simply sort the range by row number in descending order. Then perform a VLOOKUP.

      =VLOOKUP(D43,SORT($D$44:$E$46,ROW($D$44:$D$46),FALSE),2,FALSE)

      Reply
  6. My query just suddenly stopped bringing over the header row – messing up our “live” spreadsheets. I can’t figure out why it is doing this. The formula has been live and working fine for months.

    Reply
  7. This is great. Can you help me remove the header from my output? My data sheet is Google Form Responses and looks like this:
    Col A -Timestamp
    B-Select Name- 8th Graders
    C-Select Name- 7th Graders
    D-Choose an Activity
    E & F- Blank
    G- Combined Names from Columns B & C
    My formula is trying to pull the names from col G and the activity (D) sorted and unique and filtered by today’s date
    This is what I have and it works but I want it without headers:
    =Unique(QUERY(Responses!A:G,”select G, D where A >= date ‘”&TEXT(TODAY(),”yyyy-mm-dd”)&”‘ order by G”,1))

    Any suggestions? So much appreciated (been working on this formula all weekend)

    Reply
  8. Late to the party but wanted to take a moment to express my gratitude for the solution to remove auto-generated headers. Extremely helpful, thank you.

    Reply
  9. To remove the headers from min(), max(), etc… rather than run two queries you can enforce a blank label to remove the header:

    =query({A1:B10}, select max(Col1) where Col2 = ‘mydata’ label max(Col1) ””,0)

    Reply
    • This is done in your SELECT statement.

      SELECT B, C

      The SELECT statement above would select columns B and C, skipping column A.

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.