Snowflake bills compute by the warehouse, so the bill tells you which warehouse spent the credits but not who spent them. To see Snowflake cost by user or by role, join QUERY_ATTRIBUTION_HISTORY, which gives the compute credits of each query, to QUERY_HISTORY, which adds the role. Then decide how to share out the idle time that belongs to no query. This guide gives the SQL for each step, from credit usage by warehouse to cost per user, role, query tag and team. Every fact and figure in this guide was checked against Snowflake’s documentation.
This article is part of our guide to Snowflake cost monitoring.
Why can’t warehouse metering tell you who spent the credits?
Warehouse metering records credits per warehouse per hour, with no user or role attached. When two teams share one warehouse, the hourly total cannot say which team caused it. You need query-level attribution for that, plus a rule for the idle time between queries, which no query caused.
This is the question behind “one warehouse, two teams, who pays for the credits?”. If you only need the account total first, start with how to check Snowflake costs. The rest of this guide works from the warehouse down to the person.
How do you see credit usage by warehouse?
Use WAREHOUSE_METERING_HISTORY in ACCOUNT_USAGE. It has one row per warehouse per hour for the last 365 days, with the compute credits used and the part of them attributed to queries. The difference between those two columns is idle time. The view can lag by up to 3 hours.
-- Credit usage by warehouse, last full calendar month
SELECT warehouse_name,
SUM(credits_used_compute) AS compute_credits,
SUM(credits_attributed_compute_queries) AS query_credits,
SUM(credits_used_compute)
- SUM(credits_attributed_compute_queries) AS idle_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY warehouse_name
ORDER BY compute_credits DESC;
Use CREDITS_USED_COMPUTE rather than CREDITS_USED. CREDITS_USED adds cloud services credits before Snowflake’s daily adjustment, so it can be higher than what you are billed. CREDITS_ATTRIBUTED_COMPUTE_QUERIES is NULL for Adaptive Warehouses, so the idle column is NULL for them too.
How do you calculate Snowflake cost by user and by role?
QUERY_ATTRIBUTION_HISTORY gives each query’s compute credits (CREDITS_ATTRIBUTED_COMPUTE), its Query Acceleration credits, the user and the query tag. It has no role column, so join it to QUERY_HISTORY on QUERY_ID to get ROLE_NAME. Both views are in ACCOUNT_USAGE and keep 365 days.
-- Credits by user and role, last full calendar month
SELECT qah.user_name,
qh.role_name,
COUNT(*) AS queries,
SUM(qah.credits_attributed_compute) AS compute_credits,
SUM(COALESCE(qah.credits_used_query_acceleration, 0)) AS qas_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY qah
JOIN SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY qh
ON qh.query_id = qah.query_id
WHERE qah.start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND qah.start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
-- also limit QUERY_HISTORY so the join does not scan a full year
AND qh.start_time >= DATEADD(DAY, -1, DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE)))
GROUP BY qah.user_name, qh.role_name
ORDER BY compute_credits DESC;
ROLE_NAME is the role that was active in the session when the query ran. Drop it from the SELECT and GROUP BY to get cost per user only, or drop USER_NAME to get cost per role.
Know what the attributed number leaves out, per Snowflake’s documentation:
| Included in CREDITS_ATTRIBUTED_COMPUTE | Not included |
|---|---|
| Warehouse compute for the query’s execution | Warehouse idle time |
| Resizing and multi-cluster scaling effects | Queries of about 100 ms or less |
| A share of the warehouse when queries run at the same time, by weighted resource use | Storage, data transfer, cloud services, serverless features, AI tokens |
| Jobs on Adaptive Warehouses (use QUERY_METERING_HISTORY) |
The view can lag by up to 8 hours and has data from mid-August 2024. A role needs the USAGE_VIEWER or GOVERNANCE_VIEWER database role to read it. For the full method behind per-query numbers, see our guide to Snowflake cost per query attribution.
How do you allocate idle time back to users?
Spread each warehouse’s billed compute across its users in proportion to their attributed credits. A user with 50% of a warehouse’s query credits takes 50% of its bill, idle time included. This is an allocation rule you choose, not a measurement: no query caused the idle time, so any split of it is a policy decision.
Snowflake’s cost attribution guide shows this split across all warehouses at once. The version below does it per warehouse, so a user is only charged idle time on warehouses they actually used.
-- Allocate each warehouse's billed compute to its users, last full calendar month
WITH wh AS (
SELECT warehouse_id, warehouse_name, SUM(credits_used_compute) AS billed_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY warehouse_id, warehouse_name
),
usr AS (
SELECT warehouse_id, user_name, SUM(credits_attributed_compute) AS measured_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY
WHERE start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY warehouse_id, user_name
)
SELECT u.user_name,
w.warehouse_name,
u.measured_credits,
DIV0(u.measured_credits, SUM(u.measured_credits) OVER (PARTITION BY u.warehouse_id))
* w.billed_credits AS allocated_credits
FROM usr u
JOIN wh w ON w.warehouse_id = u.warehouse_id
ORDER BY allocated_credits DESC;
Show both columns in reports. The measured number is what the user’s queries used; the allocated number is their share of the bill. A warehouse that ran with no attributed queries at all, or an Adaptive Warehouse, does not appear here; report it as its own line.
How do you see cost by query tag and by object tag?
Use query tags when one user runs work for many teams, and object tags on users or warehouses when each belongs to one team. Snowflake recommends object tags for chargeback and showback. Query tags show up in QUERY_ATTRIBUTION_HISTORY directly; object tags come from the TAG_REFERENCES view.
QUERY_TAG is a parameter of up to 2,000 characters that can be set for the account, a user or a session. Group by it, keeping untagged work visible:
SELECT COALESCE(NULLIF(query_tag, ''), 'untagged') AS tag,
SUM(credits_attributed_compute) AS compute_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY
WHERE start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY tag
ORDER BY compute_credits DESC;
For object tags, create a tag such as COST_CENTER, set it on each user, and join TAG_REFERENCES on DOMAIN = ‘USER’ and the user name:
SELECT COALESCE(tr.tag_value, 'untagged') AS cost_center,
SUM(qah.credits_attributed_compute) AS compute_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY qah
LEFT JOIN SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES tr
ON tr.domain = 'USER'
AND tr.object_name = qah.user_name
AND tr.tag_name = 'COST_CENTER'
AND tr.object_deleted IS NULL
WHERE qah.start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND qah.start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY cost_center
ORDER BY compute_credits DESC;
TAG_REFERENCES records only direct tag assignments, not inherited ones, and can lag by up to 2 hours. For how to set tags from dbt, Python and BI tools, see Snowflake query tags.
What should you do about service accounts and shared users?
Give each tool, pipeline or team its own Snowflake user, and set a default query tag on it. A single service account shared by many jobs shows up as one large user row, which says nothing about which job spent the money. Shared logins for people have the same problem.
Practical steps:
- Create one service user per tool or team with
TYPE = SERVICE, which Snowflake defines as a user that “interacts with Snowflake without human intervention”. QUERY_HISTORY’s USER_TYPE column then lets you split human from automated spend. - Set a default tag on the user, for example
ALTER USER svc_dbt SET QUERY_TAG = 'team=analytics';, and override it per job withALTER SESSION SET QUERY_TAG. - For dbt, set
query_tagper model or folder; see how to reduce the Snowflake cost of dbt models. - Replace shared human logins with named users. Attribution can only be as specific as the user and tag on the query.
How do you show cost by warehouse per team?
If each team has its own warehouse, tag the warehouse and charge the team its full metered cost, idle time included. No query-level join is needed, and the numbers add up to the warehouse bill. This is the simplest model, though it costs more warehouses to run.
SELECT COALESCE(tr.tag_value, 'untagged') AS team,
SUM(wmh.credits_used_compute) AS compute_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY wmh
LEFT JOIN SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES tr
ON tr.object_id = wmh.warehouse_id
AND tr.domain = 'WAREHOUSE'
AND tr.tag_name = 'COST_CENTER'
WHERE wmh.start_time >= DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE))
AND wmh.start_time < DATE_TRUNC('MONTH', CURRENT_DATE)
GROUP BY team
ORDER BY compute_credits DESC;
The tag_name filter matters: without it, a warehouse with two tags is counted twice.
How do you turn credits into dollars?
Multiply credits by your price per credit. On demand in AWS US East, a credit costs $2.00 on Standard, $3.00 on Enterprise and $4.00 on Business Critical. Contract prices differ, so use your own rate. For actual billed amounts in your currency, the organization-level view USAGE_IN_CURRENCY_DAILY reports usage per account per day, with a lag of up to 72 hours.
Here is an example of the allocation query’s output for one shared warehouse in a month, priced at $3.00 per credit (Enterprise, on demand). All numbers are made up for illustration.
| User (example) | Role (example) | Measured credits | Allocated credits | Allocated cost at $3.00 |
|---|---|---|---|---|
| ANALYST_ANA | ANALYST | 450 | 600 | $1,800 |
| SVC_DBT | TRANSFORMER | 300 | 400 | $1,200 |
| SVC_BI | REPORTING | 150 | 200 | $600 |
| Total | 900 | 1,200 | $3,600 |
In this example the warehouse billed 1,200 compute credits and queries account for 900, so 300 credits (25%) were idle time, shared out in proportion to use. To turn team totals into cost per customer, product or pipeline, see data platform unit economics.
Should you use showback or chargeback?
Showback reports each team’s cost without billing it; chargeback moves the cost into the team’s budget. Start with showback while tagging coverage is low and the untagged line is large, because charging teams for numbers they cannot trace causes arguments, not savings. Move to chargeback once most credits carry a team, and agree the idle-time rule first. More in chargeback vs showback. Once owners are known, give each a limit; see Snowflake budgets vs resource monitors.
How Seemore helps with cost by user, role and warehouse
The Seemore platform attributes cost by job, user, domain and data product, so you do not have to maintain the joins above. Real Time Monitoring shows every connected account live, down to the exact query behind a spike. You can also ask Seemore Assistant about your environment in plain language instead of writing the SQL above.
Frequently asked questions
How do I see Snowflake cost per user?
Sum CREDITS_ATTRIBUTED_COMPUTE by USER_NAME in ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY. That gives each user’s measured query compute. To include idle time, allocate each warehouse’s CREDITS_USED_COMPUTE in proportion to those credits, then multiply by your price per credit.
How do I get Snowflake cost by role?
QUERY_ATTRIBUTION_HISTORY has no role column. Join it to QUERY_HISTORY on QUERY_ID and group by ROLE_NAME, which is the role that was active in the session when the query ran.
Why do user credits not add up to the warehouse bill?
Attributed credits leave out warehouse idle time and queries of about 100 ms or less. The gap between CREDITS_USED_COMPUTE and CREDITS_ATTRIBUTED_COMPUTE_QUERIES in WAREHOUSE_METERING_HISTORY is that unattributed part.
One warehouse, two teams: who pays for the credits?
Tag each user or query with its team, sum the attributed credits per team, then split the warehouse’s idle time in proportion to those totals. Agree the split rule in advance, because it is a policy, not a measurement.
How fresh is Snowflake's per-user cost data?
QUERY_ATTRIBUTION_HISTORY can lag by up to 8 hours, WAREHOUSE_METERING_HISTORY by up to 3 hours, QUERY_HISTORY by up to 45 minutes and TAG_REFERENCES by up to 2 hours. For investigating a fresh spike, see how to trace a Snowflake cost spike to the query and user.
Does per-user attribution work on Adaptive Warehouses?
Not with QUERY_ATTRIBUTION_HISTORY, which has no records for jobs on Adaptive Warehouses. Snowflake points to QUERY_METERING_HISTORY for per-query usage there.