Want to run real SQL against MySQL, PostgreSQL, or Snowflake from Google Sheets? This guide covers every method, including what QUERY() can't do.
Running actual SQL queries against a live database from Google Sheets requires either Apps Script with JDBC, Connected Sheets with BigQuery, or a third-party connector like brooked.io. Google Sheets' built-in QUERY() function is NOT real SQL. It uses a different query language that only works on data already in your spreadsheet.
The Problem: QUERY() Is Not SQL
Search "run SQL in Google Sheets" and you will find page after page about the QUERY() function. Those articles are technically correct but fundamentally misleading. The QUERY() function uses the Google Visualization API query language, a SQL-like syntax that only operates on data already loaded into a spreadsheet range. It cannot connect to a database. It cannot run a JOIN across two tables in PostgreSQL. It does not know your database exists.
Real data analysts hit this wall fast:
"I've been using QUERY() for years and just realized it can't touch my actual Postgres database. I need actual SQL, joins, aggregations, live data. What are my options?" (Stack Overflow)
"Actiondesk was perfect for this. You could write SQL directly against your database and have it land in a sheet on a schedule. Then they shut down and joined Datadog. Now I'm stuck." (Reddit r/googlesheets)
"Apps Script JDBC works but my query takes 8 minutes and Apps Script kills it at 6. I have to break it into chunks." (Reddit r/bigquery)
The loss of Actiondesk (which allowed direct SQL queries against production databases from a spreadsheet interface before being acquired by Datadog) left a real gap. The audience who needed that workflow (data analysts who live in spreadsheets but want SQL flexibility) has nowhere obvious to go.
This article covers every realistic path to running actual SQL against live MySQL, PostgreSQL, SQL Server, and Snowflake databases from within Google Sheets, along with honest tradeoffs for each.
Method 1: Google Sheets QUERY() Function
What it is: A spreadsheet formula that queries data already in your sheet using a SQL-like syntax.
What it is NOT: A database connector. It cannot run against an external database.
What QUERY() Can Do
=QUERY(A1:D100, "SELECT A, B WHERE C > 100 ORDER BY D DESC LIMIT 10")
The QUERY() function is genuinely powerful for working with data that is already inside a Google Sheet. It supports:
SELECT,WHERE,ORDER BY,LIMIT- Aggregate functions:
SUM(),AVG(),COUNT(),MAX(),MIN() GROUP BYandPIVOT- Basic
LABELfor renaming columns
What QUERY() Cannot Do
- Connect to MySQL, PostgreSQL, SQL Server, Snowflake, or any external database
- Execute real JOINs across database tables (only across ranges in the same sheet)
- Use subqueries or CTEs
- Write data back to a database (no INSERT, UPDATE, DELETE)
- Access live or real-time data (only what is statically in the sheet)
When to Use It
Use QUERY() when your data is already in Google Sheets and you want spreadsheet-native filtering, aggregation, or pivoting without building formulas from scratch. It is not a substitute for database SQL.
Method 2: Apps Script + JDBC (Real SQL)
What it is: Google Apps Script includes a JDBC service that lets you connect directly to MySQL, Microsoft SQL Server, and PostgreSQL databases and run genuine SQL.
Cost: Free (included with Google Workspace)
Skill level: Intermediate: requires JavaScript and database access credentials
Working Example
Here is a complete Apps Script function that runs a SQL JOIN against a PostgreSQL database and writes results to the active sheet:
function runSQLQuery() {
const conn = Jdbc.getConnection(
'jdbc:postgresql://your-db-host:5432/your_database',
'your_username',
'your_password'
);
const stmt = conn.createStatement();
const query = `
SELECT
o.order_id,
c.name AS customer_name,
c.email,
o.total_amount,
o.created_at
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY o.total_amount DESC
LIMIT 500
`;
const results = stmt.executeQuery(query);
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clearContents();
// Write headers
const metadata = results.getMetaData();
const numCols = metadata.getColumnCount();
const headers = [];
for (let i = 1; i <= numCols; i++) {
headers.push(metadata.getColumnName(i));
}
sheet.appendRow(headers);
// Write data rows
while (results.next()) {
const row = [];
for (let i = 1; i <= numCols; i++) {
row.push(results.getString(i));
}
sheet.appendRow(row);
}
conn.close();
}Real Limitations to Know
The 6-minute execution limit is the most common blocker. Apps Script kills any function after 6 minutes, no exceptions. If your query returns more than a few thousand rows or hits a slow database, you will get a timeout error. Workarounds include using LIMIT/OFFSET pagination across multiple timed runs, but this adds significant complexity.
Security concerns: Your database credentials are stored in the script or in Script Properties. Apps Script does not natively support secrets management. Anyone with edit access to the script can read those credentials.
IP allowlisting: Your database firewall likely needs to allowlist Google's IP ranges for Apps Script, which are not static and can change.
No Snowflake support: The JDBC service supports MySQL, PostgreSQL, and SQL Server, not Snowflake, BigQuery, or other cloud warehouses.
When to Use It
Apps Script JDBC is the right choice if you have a small-to-medium dataset (under ~2,000 rows), your database is MySQL or PostgreSQL, you have IT permission to open a firewall rule, and you want a free solution that you control entirely.
Method 3: Connected Sheets + BigQuery
What it is: Google's native feature that connects Google Sheets directly to BigQuery and lets you run real SQL (BigQuery SQL dialect) from within a sheet.
Cost: Requires BigQuery. BigQuery charges per query on demand (~$5 per TB scanned). Connected Sheets itself is included with Google Workspace Business Standard and above.
Skill level: Low to intermediate. The UI is clean, but you need a BigQuery project and your data needs to be there.
How It Works
- Open a Google Sheet and go to Data > Data connectors > Connect to BigQuery
- Select your Google Cloud project, dataset, and table
- Click Create custom query to write BigQuery SQL directly in the sheet
- Results load into a pivot-style view, or you can extract to a regular sheet range
The SQL support is full BigQuery SQL, real JOINs, window functions, CTEs, ARRAY_AGG, the works.
The Catch
Your data has to be in BigQuery. If your operational data lives in MySQL, PostgreSQL, or SQL Server, you need to first move it to BigQuery using a pipeline tool (like brooked.io), and then query it through Connected Sheets. That is a two-step architecture but it is also a very reliable one.
This approach does not work for Snowflake-native queries: for that you need a third-party connector.
When to Use It
Connected Sheets is ideal when your organization already uses BigQuery as a data warehouse. It is the most smooth experience for BigQuery users and the SQL dialect is the most complete of any native option.
Method 4: Third-Party Connectors with SQL Mode
Several tools have built a proper SQL editor experience directly into Google Sheets, connecting to databases without requiring code or dealing with JDBC timeouts.
brooked.io
brooked.io is a data integration platform that lets you write custom SQL against source databases (MySQL, PostgreSQL, SQL Server, Snowflake, and others) and schedule the results to land in Google Sheets on a defined interval. You write real SQL (JOINs, CTEs, window functions) and the platform handles the connection, execution, and delivery. No Apps Script, no 6-minute timeout, no credential storage in a spreadsheet.
Key differentiator: brooked.io supports Snowflake natively, which most of the alternatives do not.
Coefficient
Coefficient has a SQL editor add-on for Google Sheets that connects to Salesforce, PostgreSQL, MySQL, and Snowflake. It is well-regarded for ease of use and has a free tier for low-volume use. The SQL mode is a proper editor, not a formula workaround.
Castodia
Castodia (formerly Slemma's spreadsheet connector) focuses specifically on database-to-spreadsheet connections and supports scheduled refreshes. It is less well-known but competitive for PostgreSQL and MySQL use cases.
When to Use Third-Party Connectors
If you are hitting the Apps Script timeout, need Snowflake, want scheduled refreshes without managing cron jobs, or your team lacks developer resources, a third-party connector is the right path. The cost (typically $20–$50/month per user) is usually justified by the time saved managing workarounds.
Method 5: Python + gspread
What it is: A Python script that runs SQL against any database using a library like sqlalchemy or psycopg2, then writes the results to Google Sheets using the gspread library.
Cost: Free (infrastructure cost only)
Skill level: Developer, requires Python, database drivers, and a Google service account
Working Example
import gspread
import psycopg2
import pandas as pd
from google.oauth2.service_account import Credentials
# Database connection
conn = psycopg2.connect(
host="your-db-host",
database="your_database",
user="your_username",
password="your_password"
)
# Run real SQL - joins, aggregations, whatever you need
query = """
SELECT
c.name AS customer_name,
COUNT(o.order_id) AS total_orders,
SUM(o.total_amount) AS lifetime_value,
MAX(o.created_at) AS last_order_date
FROM customers c
JOIN orders o ON c.id = o.customer_id
WHERE c.signup_date >= '2024-01-01'
GROUP BY c.name
ORDER BY lifetime_value DESC
"""
df = pd.read_sql(query, conn)
conn.close()
# Write to Google Sheets
scope = ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive']
creds = Credentials.from_service_account_file('service_account.json', scopes=scope)
client = gspread.authorize(creds)
sheet = client.open("Your Sheet Name").sheet1
sheet.clear()
sheet.update([df.columns.values.tolist()] + df.values.tolist())
print(f"Written {len(df)} rows to Google Sheets")When to Use Python + gspread
This is the most flexible option. It works with any database that has a Python driver, supports any SQL complexity, and can be scheduled with cron or a workflow orchestrator. It is the right choice for engineering teams or data engineers who want full control and are comfortable maintaining code.
The tradeoff is maintenance overhead. Every credential rotation, schema change, or Google API update becomes your problem to manage.
Comparison Table
| Method | Real SQL? | Databases Supported | Code Required | Cost | Timeout Risk | Scheduled Refresh |
|---|---|---|---|---|---|---|
| QUERY() function | No (SQL-like) | Sheets data only | No | Free | No | No |
| Apps Script JDBC | Yes | MySQL, PostgreSQL, SQL Server | JavaScript | Free | Yes (6 min) | Via time triggers |
| Connected Sheets + BigQuery | Yes (BigQuery SQL) | BigQuery only | No | BigQuery costs apply | No | Auto |
| brooked.io | Yes | MySQL, PG, SQL Server, Snowflake, more | No | Paid | No | Yes |
| Coefficient | Yes | MySQL, PG, Snowflake, Salesforce | No | Free tier / Paid | No | Yes |
| Python + gspread | Yes | Any (driver-dependent) | Python | Free (infra) | No | Via cron/scheduler |
Troubleshooting
Apps Script says "Exceeded maximum execution time" Your query is returning too much data or your database is slow. Add a LIMIT clause first to verify the connection works, then paginate using LIMIT/OFFSET across multiple script runs triggered by time-based triggers.
JDBC connection refused Your database firewall is blocking Google's egress IPs. You need to allowlist Google's Apps Script IP ranges. Check the current list in Google's documentation as they are not static. Alternatively, use a VPN or bastion host, though that significantly complicates the setup.
QUERY() returns #N/A or #VALUE errors You are likely referencing a range that includes mixed data types in a column, or your date syntax is wrong. The QUERY() date format requires date 'YYYY-MM-DD' not standard SQL date syntax.
Connected Sheets shows "Access denied" on BigQuery The Google account running the sheet needs BigQuery Data Viewer or higher on the project. Ask your GCP admin to check IAM permissions.
Python gspread writes are slow for large datasets Use sheet.update() with a full 2D array rather than row-by-row append_row(). Batch writes are 10–50x faster for datasets over a few hundred rows.
The Bottom Line
The fundamental confusion in this space (QUERY() versus real SQL) costs analysts hours of frustration. Google's own documentation and most search results reinforce the confusion by ranking QUERY() content for "SQL in Google Sheets" queries.
Here is the honest breakdown:
- If your data is already in the sheet, QUERY() is excellent and you should use it
- If you have a small MySQL or PostgreSQL dataset and developer access, Apps Script JDBC works but come prepared for the timeout wall
- If your organization is BigQuery-native, Connected Sheets is the cleanest solution available
- If you need Snowflake, scheduled refreshes without code, or you are still looking for a replacement after Actiondesk shut down, a third-party connector is the right investment
- If you want maximum flexibility and are comfortable with Python, gspread gives you complete control
The Actiondesk-shaped hole in this market is real. Analysts who want to write SQL against production databases and have results land in a Google Sheet on a schedule (without writing and maintaining JavaScript or Python) have fewer options than they should.
Get Live Database Data Into Google Sheets Without the Headaches
brooked.io connects your MySQL, PostgreSQL, SQL Server, Snowflake, and other databases to Google Sheets with a built-in SQL editor. Write real SQL (JOINs, CTEs, aggregations) and schedule the results to refresh automatically. No Apps Script timeouts, no credential storage in spreadsheets, no Python environment to maintain.
Start your free trial at brooked.io →
Troubleshooting quick reference
Frequently asked questions
Can Google Sheets connect to a database natively?
Not to external operational databases (MySQL, PostgreSQL, SQL Server, Snowflake) without additional setup. The only native connection is Connected Sheets to BigQuery, which requires Google Workspace Business Standard or above and a BigQuery project. For all other databases, you need Apps Script JDBC, a third-party connector, or custom code.
Is the QUERY() function the same as SQL?
No. The QUERY() function uses the Google Visualization API query language, which is SQL-inspired but distinct. It supports a subset of SQL syntax and operates exclusively on data already loaded into a Google Sheet range. It has no knowledge of external databases and cannot execute JOINs across database tables.
What happened to Actiondesk?
Actiondesk was a tool that provided a spreadsheet-style interface for running SQL against live databases. It was acquired by Datadog in 2023 and shut down its standalone product. Former Actiondesk users need a replacement that supports direct SQL against operational databases: brooked.io and Coefficient are the closest equivalents.
How do I handle database credentials securely in Apps Script?
Store credentials in Script Properties (Project Settings > Script Properties) rather than hardcoding them in the script. This keeps them out of the code itself, though Script Properties are still readable by anyone with edit access to the script. For higher security requirements, use a third-party connector that handles authentication outside the spreadsheet environment.
Can I run SQL against Snowflake from Google Sheets?
Apps Script JDBC does not support Snowflake. Your options are: use a third-party connector that supports Snowflake (brooked.io, Coefficient), use Python with the snowflake-connector-python library and gspread, or load Snowflake data into BigQuery and use Connected Sheets.
How often can I refresh data from a database to Google Sheets?
Apps Script time-based triggers can run as frequently as every minute, but each run counts against your Apps Script quota. Third-party connectors typically offer refresh intervals from every 15 minutes to daily. For true real-time data, you would need a webhook-based architecture or a streaming pipeline, which is beyond the scope of spreadsheet-native tools.
Ready to connect your data to Google Sheets?
Brooked's free tier covers 100 imports per month with AI Analyst included, no credit card required.
Install Brooked free →

