Averages are only the beginning. This in-depth lesson teaches you how to use SQL's statistical aggregation functions — PERCENTILE_CONT, STDDEV, VAR_SAMP, and more — to analyze data distributions, detect outliers, and build professional distribution reports. Covers PostgreSQL, BigQuery, and SQL Server with real-world examples.

You've got a table of customer order values. Your boss asks: "What's a typical order size?" You return with AVG(order_total) — $147. She nods, then asks, "But are most orders actually near $147, or is that being dragged up by a few big spenders?" You don't have an answer. That's the moment you realize that averages, alone, are liars.
Statistical aggregation functions — percentiles, median, standard deviation, and variance — are the tools that give averages context. They tell you about the shape of your data, not just its center. An average of $147 with a standard deviation of $12 means something completely different from an average of $147 with a standard deviation of $340. The first distribution is tight and predictable. The second is chaotic, and your business decisions should reflect that.
By the end of this lesson, you'll be able to use SQL's statistical aggregation functions to perform meaningful distributional analysis directly in your database, without exporting to Python or R. You'll understand when and why to use each function, how to handle the syntax quirks across PostgreSQL, BigQuery, and SQL Server, and how to combine these functions into analytical queries that actually answer business questions.
What you'll learn:
PERCENTILE_CONT and PERCENTILE_DISC work and when to choose between themSTDDEV and VARIANCE measure and how to interpret their output in a business contextGROUP BY and window functions for segment-level analysisYou should be comfortable with standard SQL aggregation (GROUP BY, HAVING, SUM, AVG, COUNT), window functions (OVER, PARTITION BY, ORDER BY), and subqueries or CTEs. If window functions are shaky for you, take a pass through the window functions lesson in this learning path before continuing here.
We'll use PostgreSQL syntax as our primary dialect, with explicit callouts for BigQuery and SQL Server where syntax diverges meaningfully.
Before diving into the functions themselves, it's worth building the right mental model for why you need them.
Consider a SaaS company's monthly revenue per customer. Suppose you have:
The average monthly revenue per customer comes out to roughly $195. But $195 describes almost nobody in that dataset. The median — the value where half your customers are above and half are below — is $50. That's a fundamentally different story.
This is the classic problem with right-skewed distributions: a small number of high values pull the mean upward, making the average a poor representation of the "typical" case. Revenue data, response times, order sizes, bug counts, page load times — these are almost always right-skewed in production datasets.
Standard deviation and variance add another dimension: they tell you how spread out your data is around the center. A customer support team averaging 4.2 minutes per ticket resolution sounds fine — until you learn the standard deviation is 18 minutes, meaning some customers are waiting well over an hour while the average looks acceptable.
SQL gives you two percentile functions, and understanding the difference between them will save you a debugging session.
PERCENTILE_CONT computes a continuous percentile — it interpolates between adjacent values when the exact percentile falls between two data points. This is what most statistical software uses by default.
The syntax uses an ordered set aggregate, which looks a little unusual if you haven't seen it before:
SELECT
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total) AS median_order,
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY order_total) AS p25,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY order_total) AS p75,
PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY order_total) AS p90,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY order_total) AS p95,
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY order_total) AS p99
FROM orders
WHERE order_date >= '2024-01-01';
The WITHIN GROUP (ORDER BY ...) clause tells SQL how to sort your values before computing the percentile. This is required — the percentile concept is meaningless without an ordering.
If you have 10 values and ask for the 50th percentile, there's no single value that sits exactly at the midpoint. PERCENTILE_CONT averages the 5th and 6th values (once sorted) to give you a smooth interpolated result.
PERCENTILE_DISC returns a discrete percentile — the actual nearest value that exists in your dataset. If the exact percentile falls between two values, it returns the lower of the two.
SELECT
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY order_total) AS median_order,
PERCENTILE_DISC(0.25) WITHIN GROUP (ORDER BY order_total) AS p25,
PERCENTILE_DISC(0.75) WITHIN GROUP (ORDER BY order_total) AS p75
FROM orders
WHERE order_date >= '2024-01-01';
Use PERCENTILE_CONT when:
Use PERCENTILE_DISC when:
Tip: For financial reporting,
PERCENTILE_DISCis often more defensible — you can point to an actual transaction in your system. For statistical modeling and analysis,PERCENTILE_CONTis usually the right call.
The real power comes when you combine percentile functions with GROUP BY to compare distributions across segments.
SELECT
customer_segment,
COUNT(*) AS order_count,
ROUND(AVG(order_total)::numeric, 2) AS avg_order,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total) AS median_order,
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY order_total) AS p25,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY order_total) AS p75,
PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY order_total) AS p90
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01'
GROUP BY customer_segment
ORDER BY median_order DESC;
This query tells you not just that "enterprise customers spend more on average" — it shows you where in the distribution they differ. Maybe enterprise and SMB customers have similar medians, but enterprise customers have a much higher p90, driven by occasional large bulk orders. That's an insight an average alone would hide.
The median is just the 50th percentile, and PERCENTILE_CONT(0.5) is the cleanest way to compute it in modern SQL. But you'll encounter legacy code and edge cases where you need alternative approaches, so it's worth knowing the landscape.
Before PERCENTILE_CONT was widely available, analysts computed median using a ranking trick:
WITH ranked_orders AS (
SELECT
order_total,
ROW_NUMBER() OVER (ORDER BY order_total) AS rn,
COUNT(*) OVER () AS total_count
FROM orders
WHERE order_date >= '2024-01-01'
)
SELECT
AVG(order_total) AS median_order
FROM ranked_orders
WHERE rn IN (
FLOOR((total_count + 1) / 2.0),
CEIL((total_count + 1) / 2.0)
);
This works but is more verbose and harder to extend to multiple percentiles. If your database supports PERCENTILE_CONT, use it.
BigQuery uses PERCENTILE_CONT and PERCENTILE_DISC as window functions, not aggregate functions. The syntax looks different:
-- BigQuery syntax
SELECT DISTINCT
customer_segment,
PERCENTILE_CONT(order_total, 0.5) OVER (PARTITION BY customer_segment) AS median_order,
PERCENTILE_CONT(order_total, 0.75) OVER (PARTITION BY customer_segment) AS p75,
PERCENTILE_CONT(order_total, 0.90) OVER (PARTITION BY customer_segment) AS p90
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01';
Note the argument order is reversed in BigQuery: the column comes first, the percentile fraction second. The DISTINCT is needed because the window function returns a value for every row.
SQL Server uses the same WITHIN GROUP syntax as PostgreSQL:
-- SQL Server / PostgreSQL compatible
SELECT
customer_segment,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total) AS median_order
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01'
GROUP BY customer_segment;
Warning: In BigQuery,
PERCENTILE_DISCdoes not supportNULLvalues in the column being evaluated. Filter them out explicitly with aWHEREclause or aCASEexpression.
Standard deviation tells you how far, on average, individual values deviate from the mean. A small standard deviation means your data clusters tightly around the average. A large one means your data is spread out — and your average is less reliable as a summary.
SQL offers two variants:
STDDEV (also written STDDEV_SAMP): Sample standard deviation — uses n-1 in the denominator. Use this when your data is a sample drawn from a larger population.STDDEV_POP: Population standard deviation — uses n in the denominator. Use this when your dataset is the entire population you're analyzing.In practice, if you're analyzing all orders from last year, you're looking at the whole population of last year's orders, so STDDEV_POP is technically correct. But unless your dataset is very small, the difference between the two is negligible. Most analysts just use STDDEV out of habit, and it's fine.
SELECT
customer_segment,
COUNT(*) AS order_count,
ROUND(AVG(order_total)::numeric, 2) AS avg_order,
ROUND(STDDEV(order_total)::numeric, 2) AS stddev_order,
ROUND(STDDEV(order_total) / NULLIF(AVG(order_total), 0) * 100, 1) AS cv_pct
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01'
GROUP BY customer_segment
ORDER BY cv_pct DESC;
The last column — cv_pct — is the coefficient of variation (CV): standard deviation divided by the mean, expressed as a percentage. CV is more useful than raw standard deviation for comparing spread across groups with different means. A $50 standard deviation on a $150 average order (33% CV) is very different from a $50 standard deviation on a $1,500 average order (3.3% CV).
Here's a worked example. Suppose your query returns:
| customer_segment | avg_order | stddev_order | cv_pct |
|---|---|---|---|
| enterprise | 1,240 | 890 | 71.8% |
| smb | 340 | 85 | 25.0% |
| consumer | 47 | 18 | 38.3% |
Enterprise customers have a very high CV — 71.8%. That tells you the enterprise segment is heterogeneous. Some enterprise customers have modest orders, others have massive ones. Treating "enterprise" as a single cohort for pricing or capacity planning will lead you astray.
SMB customers are much more predictable. A 25% CV means the average is actually a good representation of a typical SMB order.
Variance is the square of standard deviation. It's less intuitive to interpret directly (a variance of 79,210 square dollars is hard to contextualize), but it has mathematical properties that make it useful in certain contexts.
SELECT
customer_segment,
ROUND(AVG(order_total)::numeric, 2) AS avg_order,
ROUND(VAR_SAMP(order_total)::numeric, 2) AS variance_order,
ROUND(STDDEV(order_total)::numeric, 2) AS stddev_order
FROM orders
JOIN customers USING (customer_id)
GROUP BY customer_segment;
Like standard deviation, there are two variants: VAR_SAMP (sample variance, n-1 denominator) and VAR_POP (population variance, n denominator). Some databases also accept VARIANCE as an alias for VAR_SAMP.
The most common practical use of variance in SQL analysis is when you need to compare relative variability across different time periods or experiments. Because variance is additive for independent random variables, statisticians prefer it for formal tests. For day-to-day data analysis, standard deviation is more interpretable.
One useful application: detecting performance regressions in engineering metrics.
WITH weekly_metrics AS (
SELECT
DATE_TRUNC('week', event_timestamp) AS week_start,
AVG(response_time_ms) AS avg_response_time,
STDDEV(response_time_ms) AS stddev_response_time,
VAR_SAMP(response_time_ms) AS variance_response_time,
COUNT(*) AS request_count
FROM api_events
WHERE event_timestamp >= NOW() - INTERVAL '12 weeks'
GROUP BY DATE_TRUNC('week', event_timestamp)
)
SELECT
week_start,
ROUND(avg_response_time::numeric, 1) AS avg_ms,
ROUND(stddev_response_time::numeric, 1) AS stddev_ms,
ROUND(variance_response_time::numeric, 0) AS variance_ms,
request_count,
-- Flag weeks where variance spiked more than 50% above the 12-week average
CASE
WHEN variance_response_time > 1.5 * AVG(variance_response_time) OVER ()
THEN 'HIGH VARIANCE ⚠️'
ELSE 'normal'
END AS variance_status
FROM weekly_metrics
ORDER BY week_start;
This query gives you a weekly performance overview and automatically flags weeks where variance spiked — even if the average looked okay. A deployment that causes occasional 5-second timeouts might not move the weekly average much, but it will absolutely show up in variance.
The real analytical power comes from using these functions as window functions, which lets you compute statistics within a partition without collapsing your rows. This enables row-level analysis relative to group statistics.
A z-score tells you how many standard deviations away from the mean a given value is. Values beyond ±2 or ±3 standard deviations are candidates for outlier investigation.
WITH order_stats AS (
SELECT
order_id,
customer_id,
order_total,
customer_segment,
AVG(order_total) OVER (PARTITION BY customer_segment) AS segment_avg,
STDDEV(order_total) OVER (PARTITION BY customer_segment) AS segment_stddev
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01'
)
SELECT
order_id,
customer_id,
customer_segment,
order_total,
ROUND(segment_avg::numeric, 2) AS segment_avg,
ROUND(segment_stddev::numeric, 2) AS segment_stddev,
ROUND(
(order_total - segment_avg) / NULLIF(segment_stddev, 0),
2
) AS z_score
FROM order_stats
WHERE ABS((order_total - segment_avg) / NULLIF(segment_stddev, 0)) > 3
ORDER BY ABS((order_total - segment_avg) / NULLIF(segment_stddev, 0)) DESC;
This surfaces orders that are statistical outliers within their segment. An enterprise order for $250 might be normal overall, but if the enterprise segment average is $1,200 with a standard deviation of $300, that $250 order has a z-score of about -3.2 — and worth investigating for a data entry error, a discount applied incorrectly, or a contract issue.
You can also use PERCENT_RANK() and CUME_DIST() as window functions to compute where each row falls within its group:
SELECT
customer_id,
order_id,
order_total,
customer_segment,
ROUND(PERCENT_RANK() OVER (
PARTITION BY customer_segment
ORDER BY order_total
) * 100, 1) AS percentile_within_segment,
ROUND(CUME_DIST() OVER (
PARTITION BY customer_segment
ORDER BY order_total
) * 100, 1) AS cumulative_pct
FROM orders
JOIN customers USING (customer_id)
WHERE order_date >= '2024-01-01'
ORDER BY customer_segment, order_total;
PERCENT_RANK() returns a value from 0 to 1 representing the relative rank (0 = lowest, 1 = highest). CUME_DIST() returns the fraction of rows with values less than or equal to the current row. The difference is subtle: PERCENT_RANK treats the lowest value as 0th percentile; CUME_DIST treats it as having some non-zero percentile.
Let's put everything together into a query you'd actually use in a real analytics workflow. This is a full distribution profile report for orders, broken out by segment and month.
WITH monthly_orders AS (
SELECT
DATE_TRUNC('month', o.order_date) AS order_month,
c.customer_segment,
o.order_total
FROM orders o
JOIN customers c USING (customer_id)
WHERE o.order_date >= '2023-01-01'
AND o.order_date < '2025-01-01'
AND o.order_total IS NOT NULL
AND o.order_status != 'cancelled'
),
distribution_summary AS (
SELECT
order_month,
customer_segment,
COUNT(*) AS order_count,
ROUND(AVG(order_total)::numeric, 2) AS avg_order,
ROUND(STDDEV(order_total)::numeric, 2) AS stddev_order,
ROUND(
(STDDEV(order_total) / NULLIF(AVG(order_total), 0) * 100)::numeric, 1
) AS cv_pct,
ROUND(MIN(order_total)::numeric, 2) AS min_order,
ROUND(PERCENTILE_CONT(0.10) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS p10,
ROUND(PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS p25,
ROUND(PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS median_order,
ROUND(PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS p75,
ROUND(PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS p90,
ROUND(PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY order_total)::numeric, 2) AS p99,
ROUND(MAX(order_total)::numeric, 2) AS max_order,
ROUND(
(PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY order_total)
- PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY order_total))::numeric, 2
) AS iqr
FROM monthly_orders
GROUP BY order_month, customer_segment
)
SELECT
TO_CHAR(order_month, 'YYYY-MM') AS month,
customer_segment,
order_count,
avg_order,
median_order,
-- Skew indicator: if mean >> median, distribution is right-skewed
ROUND((avg_order - median_order)::numeric, 2) AS mean_minus_median,
stddev_order,
cv_pct,
p10,
p25,
p75,
p90,
p99,
iqr,
min_order,
max_order
FROM distribution_summary
ORDER BY order_month, customer_segment;
The mean_minus_median column is particularly useful. When the mean is significantly higher than the median, you have a right-skewed distribution — a few large orders pulling the average up. When they're close, your distribution is roughly symmetric. This gives you an instant skewness signal without computing formal skewness statistics.
The IQR (interquartile range, p75 minus p25) is another robust spread measure — it's not affected by extreme outliers the way standard deviation is, because it only looks at the middle 50% of your data.
Work through this exercise using either a PostgreSQL or BigQuery sandbox. If you don't have a dataset handy, you can create one using the setup script below.
-- PostgreSQL version
CREATE TABLE employee_salaries AS
SELECT
employee_id,
department,
job_level,
base_salary,
years_at_company
FROM (
VALUES
(1, 'Engineering', 'junior', 72000, 1),
(2, 'Engineering', 'junior', 68000, 2),
(3, 'Engineering', 'mid', 98000, 3),
(4, 'Engineering', 'mid', 105000, 4),
(5, 'Engineering', 'mid', 112000, 5),
(6, 'Engineering', 'senior', 145000, 7),
(7, 'Engineering', 'senior', 162000, 9),
(8, 'Engineering', 'senior', 178000, 11),
(9, 'Engineering', 'principal', 220000, 14),
(10, 'Engineering', 'principal', 265000, 16),
(11, 'Sales', 'junior', 55000, 1),
(12, 'Sales', 'junior', 58000, 2),
(13, 'Sales', 'mid', 78000, 4),
(14, 'Sales', 'mid', 82000, 3),
(15, 'Sales', 'senior', 115000, 8),
(16, 'Sales', 'senior', 138000, 10),
(17, 'Marketing', 'junior', 61000, 1),
(18, 'Marketing', 'mid', 85000, 4),
(19, 'Marketing', 'senior', 120000, 7),
(20, 'Marketing', 'principal', 195000, 12)
) AS t(employee_id, department, job_level, base_salary, years_at_company);
Task 1: Department-Level Distribution
Write a query that returns, for each department:
Task 2: Outlier Detection
Using window functions, identify any employees whose salary is more than 2 standard deviations from the mean for their department and job level combination. Return their employee_id, department, job_level, base_salary, and their z-score.
Task 3: Interpret the Results
Look at your Task 1 results. Which department shows the highest coefficient of variation, and what does that suggest about salary structure in that department? How does the Engineering department's median compare to its mean, and what does the difference tell you?
For Task 1, you should see that Engineering has the highest CV (salary varies enormously by level), and its mean is pulled above its median by the principal-level salaries. For Task 2, depending on how your data shakes out with 20 rows, you may or may not find statistical outliers — but the query logic is the key deliverable.
This is the most common syntax error with PERCENTILE_CONT:
-- WRONG - this will fail
SELECT PERCENTILE_CONT(0.5, order_total) FROM orders;
-- RIGHT
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total) FROM orders;
The WITHIN GROUP (ORDER BY ...) clause is not optional. The error message from PostgreSQL ("function percentile_cont... does not exist") can be misleading — it's not a name problem, it's a syntax problem.
If a segment has zero variance (all values are identical), STDDEV returns 0. Dividing by it will return NULL in PostgreSQL (due to the way it handles division by zero) or throw an error in other dialects. Always wrap divisors with NULLIF:
-- Safe z-score calculation
(order_total - AVG(order_total) OVER (PARTITION BY segment))
/ NULLIF(STDDEV(order_total) OVER (PARTITION BY segment), 0)
Standard deviation is unreliable on small samples. If a group has 3 or 4 rows, the standard deviation is mathematically valid but statistically meaningless. Always check your counts:
-- Guard against small-sample stddev interpretations
SELECT
customer_segment,
COUNT(*) AS n,
STDDEV(order_total) AS stddev_order,
CASE WHEN COUNT(*) < 30 THEN 'insufficient sample' ELSE 'reliable' END AS reliability
FROM orders
GROUP BY customer_segment;
Warning: A common analysis mistake is sorting a table by coefficient of variation and acting on the highest CV without noticing it's a segment with 4 orders. Always filter for minimum sample size before drawing conclusions from distributional statistics.
In BigQuery, if you try to use the WITHIN GROUP syntax, you'll get an error. BigQuery requires the window function syntax. Conversely, if you try the BigQuery syntax in PostgreSQL, it won't work either. Know your dialect.
-- PostgreSQL / SQL Server
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total)
-- BigQuery (window function form, requires OVER clause or workaround)
PERCENTILE_CONT(order_total, 0.5) OVER (PARTITION BY customer_segment)
All of these functions (PERCENTILE_CONT, STDDEV, VAR_SAMP) ignore NULL values by default — which is usually what you want, but it can distort your count-based reasoning. If 30% of your order_total values are NULL (perhaps for cancelled or draft orders), your percentile calculations are based on 70% of the rows, but COUNT(*) still counts all rows.
-- Make the NULL situation explicit
SELECT
COUNT(*) AS total_rows,
COUNT(order_total) AS non_null_rows,
COUNT(*) - COUNT(order_total) AS null_rows,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY order_total) AS median_order
FROM orders;
Standard deviation is in the same units as your original data. A standard deviation of $500 on order revenue is meaningful. A standard deviation of 500 milliseconds on API response time is a major problem. A standard deviation of 0.5 on a 1-5 customer satisfaction rating is very different from 0.5 on a 1-100 NPS score. Always interpret standard deviation in the context of the metric's range and business impact.
Ordered set aggregates like PERCENTILE_CONT require sorting the data within each group, which is O(n log n). For large tables, this can be expensive.
A few strategies:
Filter early. Always push date range and status filters before the aggregation layer. Don't compute percentiles on a full fact table if you only need last quarter.
Pre-aggregate when possible. If you need distribution summaries on a daily or weekly basis and your table has billions of rows, consider materializing the aggregation results into a summary table via a scheduled job.
Use approximate functions for exploration. Some databases offer approximate percentile functions that are much faster for exploratory analysis on huge datasets:
-- BigQuery: approximate percentiles using HyperLogLog-like approach
SELECT
APPROX_QUANTILES(order_total, 100)[OFFSET(50)] AS approx_median,
APPROX_QUANTILES(order_total, 100)[OFFSET(90)] AS approx_p90
FROM orders;
PostgreSQL's pg_tdigest extension provides similar approximate percentiles if you're willing to add an extension.
Avoid computing the same percentile multiple times. In the full report query above, we computed IQR by subtracting p25 from p75 in a single query pass. A common mistake is writing separate subqueries for each percentile — that's multiple sorts of the same data.
You now have a complete toolkit for distributional analysis in SQL. Here's a quick reference for the functions covered:
| Function | What it computes | Sample vs. Population |
|---|---|---|
PERCENTILE_CONT(f) WITHIN GROUP (ORDER BY col) |
Interpolated percentile | N/A |
PERCENTILE_DISC(f) WITHIN GROUP (ORDER BY col) |
Nearest actual value percentile | N/A |
STDDEV(col) or STDDEV_SAMP(col) |
Standard deviation | Sample (n-1) |
STDDEV_POP(col) |
Standard deviation | Population (n) |
VAR_SAMP(col) or VARIANCE(col) |
Variance | Sample (n-1) |
VAR_POP(col) |
Variance | Population (n) |
PERCENT_RANK() |
Relative rank as 0–1 fraction | Window function |
CUME_DIST() |
Cumulative distribution 0–1 | Window function |
The key mental shift is this: never let a mean stand alone in your analysis. Every average deserves a standard deviation next to it and a median check. When those three numbers are close together, your distribution is well-behaved and your average is trustworthy. When they diverge, you have a story to tell.
Where to go from here:
WIDTH_BUCKET() or NTILE() to visualize distributions without leaving SQLCORR(y, x) and REGR_SLOPE let you measure linear relationships between numeric columns directly in your databaseSTDDEV with ROWS BETWEEN clauses to build moving averages and rolling standard deviations for time-series analysisThe goal is to make your SQL outputs more than just summaries — they should tell you the shape of your data, where it concentrates, and where it surprises you.