# Azure SQL Multiple Small DBs Could Use Elastic Pool

> Flags servers whose low-DTU databases would fit a cheaper elastic pool, sized from their combined DTU usage and the pool limits.

Source: https://zop.dev/integrations/azure/recommendations/azure-sql-multiple-small-dbs-could-use-elastic-pool

---

## Why separate DTU databases overpay for their quiet hours

A single database in the DTU model is sized for its own peak and pays for that size every hour.
The [elastic pool overview](https://learn.microsoft.com/en-us/azure/azure-sql/database/elastic-pool-overview)
describes the alternative: databases on one server share a set number of eDTUs at a set price, with
no per-database charge, and the pool is billed hourly at its size whatever the usage. Pool eDTUs
cost 1.5 times the per-DTU price of a single database, but because peaks rarely coincide, far fewer
of them are needed. Microsoft says savings can appear with as few as two S3 databases.

Servers that host one database per customer, per team or per environment are the usual case. Each
database idles most of the day and spikes briefly, and each is paying for its spike alone.

## Listing the databases a pool would absorb

```bash
az sql db list --resource-group my-rg --server my-server \
  --query "[?name!='master'].{name:name, sku:currentServiceObjectiveName, maxBytes:maxSizeBytes}" \
  -o table

az monitor metrics list --resource <database-resource-id> \
  --metric dtu_consumption_percent --aggregation Average Maximum \
  --interval PT1H --offset 30d
```

Pull the hourly series for every candidate. The question is not how busy each database is on its
own, but how busy they are together at the same moment.

## Pool sizing the way Microsoft describes it

Microsoft's estimate is the larger of (total databases x average DTU per database) and (concurrently
peaking databases x peak DTU per database), rounded up to the next pool size. ZopNight follows it
without guessing how many databases peak together:

1. There must be at least two poolable user databases on the server (master is excluded) and this
   database must average below 20% DTU over 30 days.
2. Each database's DTU percentage is converted to absolute DTUs using its own service objective,
   since 40% of an S0 and 40% of a P15 are very different amounts.
3. The absolute series are added point by point, and the maximum of that sum is the true combined
   peak.
4. The smallest pool pack is chosen that satisfies all three published limits: eDTUs, maximum
   databases per pool, and included storage for the databases' combined size.

## Servers where a pool is not proposed

Every input must be present. A database without a price, a DTU series, or a known size, or series
that do not line up in time, means no finding, because any of those gaps would understate the pool
and overstate the saving. Also refused: vCore databases, a mix of Standard and Premium on one
server, retired Premium RS, a requirement bigger than the largest pack, servers with more than 100
poolable databases, and any pool that is not actually cheaper than the databases on their own.

## Splitting one pool saving across several databases

```text
pool saving     = sum of standalone database costs - pool monthly cost
fraction        = 1 - pool monthly cost / sum of standalone costs
per-DB saving   = database monthly cost x fraction
```

One pool is one action, so the saving is shared rather than repeated on every database. The shares
add up to exactly the whole-pool saving, while each recommendation still describes the full pool.

## Moving the databases into a pool

1. Confirm the database list and the pool size in the recommendation.
2. Create the pool, for example
   `az sql elastic-pool create --resource-group my-rg --server my-server --name my-pool --edition Standard --dtu 100`.
3. Move each database in:
   `az sql db update --resource-group my-rg --server my-server --name my-db --elastic-pool my-pool`.
4. Watch the pool's eDTU usage for a week and adjust its size if it runs hot.
