From 3cd91f4ada815400f5e9493560dcd93feb0fb931 Mon Sep 17 00:00:00 2001 From: Andre Araujo Date: Tue, 23 Sep 2025 12:40:14 +1000 Subject: [PATCH 1/4] Added metric descriptions to README --- sql/2025/css/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sql/2025/css/README.md b/sql/2025/css/README.md index 72688cd864a..7613f8e05c5 100644 --- a/sql/2025/css/README.md +++ b/sql/2025/css/README.md @@ -18,3 +18,33 @@ [~google-doc]: https://docs.google.com/document/d/1FtntjUvqNT_66XtKQamZDPy0gI_kLZhkBaK7JJwE2ww [~google-sheets]: https://docs.google.com/spreadsheets/d/1jGINqaVnYrlu7ob4jvxtAafyBH8PCV0BX-UbTCgDsiM/edit [~chapter-markdown]: https://github.com/HTTPArchive/almanac.httparchive.org/tree/main/src/content/en/2025/css.md + +## Queries + +Notes: +* The metrics descriptions that mention "per year", will be reported for all the years from 2019 to 2025, unless explicitly mentioned otherwise +* Percentile metrics will be reported for the 10, 25, 50, 75, and 90th percentiles, unless explicitly mentioned otherwise + +### CSS composition + +#### Stylesheets + +- [ ] average and of stylesheets per page, per year +- [ ] percentiles of the number of stylesheets per page, per year +- [ ] largest amount of stylesheets loaded, per year +- [ ] average CSS size per page, per year +- [ ] percentiles of CSS file size, per year + +#### Lines of code + +- [ ] percentiles of lines of code per stylesheet, per year +- [ ] percentiles of lines of code per page, per year +- [ ] percentiles for atrules, rules, selectors and declarations per page, per year + +#### Embedded content + +- [ ] percentiles of embedded content size, per year + +#### Comments + +- [ ] percentiles of comments per page, per year From 0673353b7656c239efd25b099020a7e1f44a355d Mon Sep 17 00:00:00 2001 From: Andre Araujo Date: Tue, 23 Sep 2025 15:58:53 +1000 Subject: [PATCH 2/4] Added CSS 2025 stylesheet queries. --- sql/2025/css/README.md | 32 ++++------- sql/2025/css/stylesheet-metrics.sql | 57 +++++++++++++++++++ .../css/stylesheet-percentile-metrics.sql | 50 ++++++++++++++++ 3 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 sql/2025/css/stylesheet-metrics.sql create mode 100644 sql/2025/css/stylesheet-percentile-metrics.sql diff --git a/sql/2025/css/README.md b/sql/2025/css/README.md index 7613f8e05c5..dfac99d1833 100644 --- a/sql/2025/css/README.md +++ b/sql/2025/css/README.md @@ -27,24 +27,14 @@ Notes: ### CSS composition -#### Stylesheets - -- [ ] average and of stylesheets per page, per year -- [ ] percentiles of the number of stylesheets per page, per year -- [ ] largest amount of stylesheets loaded, per year -- [ ] average CSS size per page, per year -- [ ] percentiles of CSS file size, per year - -#### Lines of code - -- [ ] percentiles of lines of code per stylesheet, per year -- [ ] percentiles of lines of code per page, per year -- [ ] percentiles for atrules, rules, selectors and declarations per page, per year - -#### Embedded content - -- [ ] percentiles of embedded content size, per year - -#### Comments - -- [ ] percentiles of comments per page, per year +- Stylesheets + - [stylesheet-metrics.sql][stylesheet-metrics.sql] + - [stylesheet-percentile-metrics.sql][stylesheet-percentile-metrics.sql] +- Lines of code + - [ ] percentiles of lines of code per stylesheet, per year + - [ ] percentiles of lines of code per page, per year + - [ ] percentiles for atrules, rules, selectors and declarations per page, per year +- Embedded content + - [ ] percentiles of embedded content size, per year +- Comments + - [ ] percentiles of comments per page, per year diff --git a/sql/2025/css/stylesheet-metrics.sql b/sql/2025/css/stylesheet-metrics.sql new file mode 100644 index 00000000000..5cd9d8dd580 --- /dev/null +++ b/sql/2025/css/stylesheet-metrics.sql @@ -0,0 +1,57 @@ +#standardSQL +# - percentage of mobile pages, per year +# - average number of inline stylesheets per page, per year +# - average number of remote stylesheets per page, per year +# - average number of total of stylesheets per page, per year +# - largest amount of inline stylesheets loaded, per year +# - largest amount of remote stylesheets loaded, per year +# - largest amount of total of stylesheets loaded, per year +# - average CSS size per page, per year +# - largest CSS size per page, per year +CREATE TEMPORARY FUNCTION getStylesheets(otherMetrics JSON, elementCounts JSON) +RETURNS STRUCT LANGUAGE js AS ''' +try { + if (otherMetrics && otherMetrics.sass && otherMetrics.sass.stylesheets) { + // data from 2022 onwards + return otherMetrics.sass.stylesheets; + } else { + // data from before 2022 + return { + remote: (otherMetrics && otherMetrics.almanac && ('link-nodes' in otherMetrics.almanac)) ? otherMetrics.almanac['link-nodes'].reduce(function(a, e) { return (e.rel.toLowerCase() === 'stylesheet') ? a + 1 : a; }, 0) : null, + inline: (elementCounts && elementCounts.style) ? elementCounts.style : null + } + } +} catch (e) { + return null; +} +'''; + +WITH +basedata AS ( + SELECT + EXTRACT(YEAR FROM `date`) AS report_year, + client, + LAX_INT64(IFNULL(summary.bytesCss, summary.bytesCSS)) AS bytes_css, -- In 2022 property name was changed by bytesCSS to bytesCss + getStylesheets(custom_metrics.other, custom_metrics.element_count) AS stylesheets + FROM + `httparchive.crawl.pages` --TABLESAMPLE SYSTEM (0.01 PERCENT) + WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') +) + +SELECT + report_year, + COUNTIF(client = 'mobile') / COUNT(0) AS pct_mobile, + AVG(stylesheets.inline) AS avg_inline_stylesheets_per_page, + AVG(stylesheets.remote) AS avg_remote_stylesheets_per_page, + AVG(stylesheets.inline + stylesheets.remote) AS avg_stylesheets_per_page, + MAX(stylesheets.inline) AS max_inline_stylesheets_per_page, + MAX(stylesheets.remote) AS max_remote_stylesheets_per_page, + MAX(stylesheets.inline + stylesheets.remote) AS max_stylesheets_per_page, + AVG(bytes_css) AS avg_css_bytes_per_page, + MAX(bytes_css) AS max_css_bytes_per_page +FROM + basedata +GROUP BY + report_year +ORDER BY + report_year; diff --git a/sql/2025/css/stylesheet-percentile-metrics.sql b/sql/2025/css/stylesheet-percentile-metrics.sql new file mode 100644 index 00000000000..6d5790e1efb --- /dev/null +++ b/sql/2025/css/stylesheet-percentile-metrics.sql @@ -0,0 +1,50 @@ +#standardSQL +# - percentiles of the number of inline stylesheets per page, per year +# - percentiles of the number of remote stylesheets per page, per year +# - percentiles of the total number of stylesheets per page, per year +# - percentiles of CSS file size, per year +CREATE TEMPORARY FUNCTION getStylesheets(otherMetrics JSON, elementCounts JSON) +RETURNS STRUCT LANGUAGE js AS ''' +try { + if (otherMetrics && otherMetrics.sass && otherMetrics.sass.stylesheets) { + // data from 2022 onwards + return otherMetrics.sass.stylesheets; + } else { + // data from before 2022 + return { + remote: (otherMetrics && otherMetrics.almanac && ('link-nodes' in otherMetrics.almanac)) ? otherMetrics.almanac['link-nodes'].reduce(function(a, e) { return (e.rel.toLowerCase() === 'stylesheet') ? a + 1 : a; }, 0) : null, + inline: (elementCounts && elementCounts.style) ? elementCounts.style : null + } + } +} catch (e) { + return null; +} +'''; + +WITH +basedata AS ( + SELECT + EXTRACT(YEAR FROM `date`) AS report_year, + client, + LAX_INT64(IFNULL(summary.bytesCss, summary.bytesCSS)) AS bytes_css, -- In 2022 property name was changed by bytesCSS to bytesCss + getStylesheets(custom_metrics.other, custom_metrics.element_count) AS stylesheets + FROM + `httparchive.crawl.pages` --TABLESAMPLE SYSTEM (0.01 PERCENT) + WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') +) + +SELECT + report_year, + percentile, + APPROX_QUANTILES(stylesheets.inline, 1000)[OFFSET(percentile * 10)] AS num_inline_stylesheets_per_page, + APPROX_QUANTILES(stylesheets.remote, 1000)[OFFSET(percentile * 10)] AS num_remote_stylesheets_per_page, + APPROX_QUANTILES(stylesheets.inline + stylesheets.remote, 1000)[OFFSET(percentile * 10)] AS num_stylesheets_per_page, + APPROX_QUANTILES(bytes_css, 1000)[OFFSET(percentile * 10)] AS css_bytes_per_page +FROM + basedata +CROSS JOIN + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile +GROUP BY + report_year, percentile +ORDER BY + report_year, percentile; From 290b4db6eb440e8812d33dc0ba58a058f463bcf7 Mon Sep 17 00:00:00 2001 From: Andre Araujo Date: Tue, 23 Sep 2025 21:24:35 +1000 Subject: [PATCH 3/4] Added CSS 2025 stylesheet queries. --- sql/2025/css/stylesheet-metrics.sql | 37 +++---------------- .../css/stylesheet-percentile-metrics.sql | 29 ++------------- 2 files changed, 10 insertions(+), 56 deletions(-) diff --git a/sql/2025/css/stylesheet-metrics.sql b/sql/2025/css/stylesheet-metrics.sql index 5cd9d8dd580..e20972718e3 100644 --- a/sql/2025/css/stylesheet-metrics.sql +++ b/sql/2025/css/stylesheet-metrics.sql @@ -1,38 +1,17 @@ #standardSQL # - percentage of mobile pages, per year -# - average number of inline stylesheets per page, per year -# - average number of remote stylesheets per page, per year -# - average number of total of stylesheets per page, per year -# - largest amount of inline stylesheets loaded, per year -# - largest amount of remote stylesheets loaded, per year -# - largest amount of total of stylesheets loaded, per year +# - average number of stylesheets per page, per year +# - largest amount of stylesheets loaded, per year # - average CSS size per page, per year # - largest CSS size per page, per year -CREATE TEMPORARY FUNCTION getStylesheets(otherMetrics JSON, elementCounts JSON) -RETURNS STRUCT LANGUAGE js AS ''' -try { - if (otherMetrics && otherMetrics.sass && otherMetrics.sass.stylesheets) { - // data from 2022 onwards - return otherMetrics.sass.stylesheets; - } else { - // data from before 2022 - return { - remote: (otherMetrics && otherMetrics.almanac && ('link-nodes' in otherMetrics.almanac)) ? otherMetrics.almanac['link-nodes'].reduce(function(a, e) { return (e.rel.toLowerCase() === 'stylesheet') ? a + 1 : a; }, 0) : null, - inline: (elementCounts && elementCounts.style) ? elementCounts.style : null - } - } -} catch (e) { - return null; -} -'''; - WITH basedata AS ( SELECT EXTRACT(YEAR FROM `date`) AS report_year, client, LAX_INT64(IFNULL(summary.bytesCss, summary.bytesCSS)) AS bytes_css, -- In 2022 property name was changed by bytesCSS to bytesCss - getStylesheets(custom_metrics.other, custom_metrics.element_count) AS stylesheets + LAX_INT64(IFNULL(summary.reqCss, summary.reqCSS)) AS remote_css, + IFNULL(LAX_INT64(custom_metrics.element_count.style), 0) AS inline_css FROM `httparchive.crawl.pages` --TABLESAMPLE SYSTEM (0.01 PERCENT) WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') @@ -41,12 +20,8 @@ basedata AS ( SELECT report_year, COUNTIF(client = 'mobile') / COUNT(0) AS pct_mobile, - AVG(stylesheets.inline) AS avg_inline_stylesheets_per_page, - AVG(stylesheets.remote) AS avg_remote_stylesheets_per_page, - AVG(stylesheets.inline + stylesheets.remote) AS avg_stylesheets_per_page, - MAX(stylesheets.inline) AS max_inline_stylesheets_per_page, - MAX(stylesheets.remote) AS max_remote_stylesheets_per_page, - MAX(stylesheets.inline + stylesheets.remote) AS max_stylesheets_per_page, + AVG(remote_css + inline_css) AS avg_stylesheets_per_page, + MAX(remote_css + inline_css) AS max_stylesheets_per_page, AVG(bytes_css) AS avg_css_bytes_per_page, MAX(bytes_css) AS max_css_bytes_per_page FROM diff --git a/sql/2025/css/stylesheet-percentile-metrics.sql b/sql/2025/css/stylesheet-percentile-metrics.sql index 6d5790e1efb..236776e1ffa 100644 --- a/sql/2025/css/stylesheet-percentile-metrics.sql +++ b/sql/2025/css/stylesheet-percentile-metrics.sql @@ -1,33 +1,14 @@ #standardSQL -# - percentiles of the number of inline stylesheets per page, per year -# - percentiles of the number of remote stylesheets per page, per year -# - percentiles of the total number of stylesheets per page, per year +# - percentiles of the number of stylesheets per page, per year # - percentiles of CSS file size, per year -CREATE TEMPORARY FUNCTION getStylesheets(otherMetrics JSON, elementCounts JSON) -RETURNS STRUCT LANGUAGE js AS ''' -try { - if (otherMetrics && otherMetrics.sass && otherMetrics.sass.stylesheets) { - // data from 2022 onwards - return otherMetrics.sass.stylesheets; - } else { - // data from before 2022 - return { - remote: (otherMetrics && otherMetrics.almanac && ('link-nodes' in otherMetrics.almanac)) ? otherMetrics.almanac['link-nodes'].reduce(function(a, e) { return (e.rel.toLowerCase() === 'stylesheet') ? a + 1 : a; }, 0) : null, - inline: (elementCounts && elementCounts.style) ? elementCounts.style : null - } - } -} catch (e) { - return null; -} -'''; - WITH basedata AS ( SELECT EXTRACT(YEAR FROM `date`) AS report_year, client, LAX_INT64(IFNULL(summary.bytesCss, summary.bytesCSS)) AS bytes_css, -- In 2022 property name was changed by bytesCSS to bytesCss - getStylesheets(custom_metrics.other, custom_metrics.element_count) AS stylesheets + LAX_INT64(IFNULL(summary.reqCss, summary.reqCSS)) AS remote_css, + IFNULL(LAX_INT64(custom_metrics.element_count.style), 0) AS inline_css FROM `httparchive.crawl.pages` --TABLESAMPLE SYSTEM (0.01 PERCENT) WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') @@ -36,9 +17,7 @@ basedata AS ( SELECT report_year, percentile, - APPROX_QUANTILES(stylesheets.inline, 1000)[OFFSET(percentile * 10)] AS num_inline_stylesheets_per_page, - APPROX_QUANTILES(stylesheets.remote, 1000)[OFFSET(percentile * 10)] AS num_remote_stylesheets_per_page, - APPROX_QUANTILES(stylesheets.inline + stylesheets.remote, 1000)[OFFSET(percentile * 10)] AS num_stylesheets_per_page, + APPROX_QUANTILES(remote_css + inline_css, 1000)[OFFSET(percentile * 10)] AS num_stylesheets_per_page, APPROX_QUANTILES(bytes_css, 1000)[OFFSET(percentile * 10)] AS css_bytes_per_page FROM basedata From 8ffbe08f73973903728d4c6a2e22c300a0385dd9 Mon Sep 17 00:00:00 2001 From: Andre Araujo Date: Fri, 26 Sep 2025 00:48:05 +1000 Subject: [PATCH 4/4] Added CSS 2025 stylesheet queries. --- sql/2025/css/stylesheet-comments-metrics.sql | 55 ++++++++++ .../css/stylesheet-lines-of-code-metrics.sql | 102 ++++++++++++++++++ .../css/stylesheet-percentile-metrics.sql | 37 +++++-- 3 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 sql/2025/css/stylesheet-comments-metrics.sql create mode 100644 sql/2025/css/stylesheet-lines-of-code-metrics.sql diff --git a/sql/2025/css/stylesheet-comments-metrics.sql b/sql/2025/css/stylesheet-comments-metrics.sql new file mode 100644 index 00000000000..3a8f9208f7b --- /dev/null +++ b/sql/2025/css/stylesheet-comments-metrics.sql @@ -0,0 +1,55 @@ +#standardSQL +# - average of stylesheet comments per page, per year +# - maximum of stylesheet comments per page, per year +CREATE TEMPORARY FUNCTION getComments(css STRING) +RETURNS INT64 +LANGUAGE js +AS ''' + +try { + if (css === null) + return 0; + + const comments = css.match(/\\/\\*.*?\\*\\//g); + if (comments === null) + return 0; + else + return comments.length; +} catch (e) { + return null; +} +'''; + +WITH +basedata AS ( + SELECT + EXTRACT(YEAR FROM `date`) AS report_year, + page, + getComments(response_body) AS comments + FROM + `httparchive.crawl.requests` TABLESAMPLE SYSTEM (0.01 PERCENT) + WHERE + `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') AND + type = 'css' +), + +per_page AS ( + SELECT + report_year, + page, + SUM(comments) AS comments + FROM basedata + GROUP BY + report_year, page +) + +SELECT + report_year, + AVG(comments) AS avg_comments, + MAX(comments) AS max_comments +FROM + per_page +GROUP BY + report_year +ORDER BY + report_year; diff --git a/sql/2025/css/stylesheet-lines-of-code-metrics.sql b/sql/2025/css/stylesheet-lines-of-code-metrics.sql new file mode 100644 index 00000000000..408e49593d6 --- /dev/null +++ b/sql/2025/css/stylesheet-lines-of-code-metrics.sql @@ -0,0 +1,102 @@ +#standardSQL +# - average of at-rules per page, per year +# - average of selectors per page, per year +# - average of declarations per page, per year +# - average of lines of code per page, per year +# - maximum of at-rules per page, per year +# - maximum of selectors per page, per year +# - maximum of declarations per page, per year +# - maximum of lines of code per page, per year +CREATE TEMPORARY FUNCTION getSourceLocStats(parsedCss JSON) +RETURNS STRUCT +LANGUAGE js +AS ''' +const AT_RULE_TYPES = [ + 'charset', + 'custom-media', + 'document', + 'font-face', + 'host', + 'import', + 'keyframes', + 'media', + 'namespace', + 'page', + 'supports', +] + +function processNode(node) { + let atRules = 0; + let selectors = 0; + let declarations = 0; + if (AT_RULE_TYPES.includes(node.type)) + atRules += 1; + if (node.type === 'rule' && node.selectors !== undefined) { + selectors += node.selectors.length; + } + if ((node.type === 'rule' || node.type === 'keyframe') && node.declarations !== undefined) { + declarations += node.declarations.length; + } + if (node.type === 'stylesheet' || node.type === 'media' || node.type === 'keyframes') { + const rules = (node.type === 'stylesheet') ? node.stylesheet.rules : (node.type === 'keyframes') ? node.keyframes : node.rules; + for (const rule of rules) { + const r = processNode(rule); + atRules += r.atRules; + selectors += r.selectors; + declarations += r.declarations; + } + } + return { + atRules: atRules, + selectors: selectors, + declarations: declarations + }; +} + +try { + return processNode(parsedCss); +} catch (e) { + throw e; + return {atRules: 1, selectors: 2, declarations: 3}; +} +'''; + +WITH +basedata AS ( + SELECT + EXTRACT(YEAR FROM `date`) AS report_year, + page, + getSourceLocStats(css) AS source_loc_stats + FROM + `httparchive.crawl.parsed_css` --TABLESAMPLE SYSTEM (0.01 PERCENT) + WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') +), + +per_page AS ( + SELECT + report_year, + page, + SUM(source_loc_stats.AtRules) AS at_rules, + SUM(source_loc_stats.selectors) AS selectors, + SUM(source_loc_stats.declarations) AS declarations + FROM basedata + GROUP BY + report_year, page +) + +SELECT + report_year, + AVG(at_rules) AS avg_at_rules, + AVG(selectors) AS avg_selectors, + AVG(declarations) AS avg_declarations, + AVG(at_rules + selectors + declarations) AS avg_lines_of_code, + MAX(at_rules) AS max_at_rules, + MAX(selectors) AS max_selectors, + MAX(declarations) AS max_declarations, + MAX(at_rules + selectors + declarations) AS max_lines_of_code +FROM + per_page +GROUP BY + report_year +ORDER BY + report_year; diff --git a/sql/2025/css/stylesheet-percentile-metrics.sql b/sql/2025/css/stylesheet-percentile-metrics.sql index 236776e1ffa..38497f71160 100644 --- a/sql/2025/css/stylesheet-percentile-metrics.sql +++ b/sql/2025/css/stylesheet-percentile-metrics.sql @@ -12,18 +12,39 @@ basedata AS ( FROM `httparchive.crawl.pages` --TABLESAMPLE SYSTEM (0.01 PERCENT) WHERE `date` IN ('2025-07-01', '2024-07-01', '2023-07-01', '2022-07-01', '2021-07-01', '2020-07-01', '2019-07-01') +), + +percentiles AS ( + SELECT + report_year, + percentile, + APPROX_QUANTILES(remote_css + inline_css, 1000)[OFFSET(percentile * 10)] AS num_stylesheets_per_page, + APPROX_QUANTILES(bytes_css, 1000)[OFFSET(percentile * 10)] AS css_bytes_per_page + FROM + basedata + CROSS JOIN + UNNEST([10, 25, 50, 75, 90, 100]) AS percentile + GROUP BY + report_year, percentile ) SELECT report_year, - percentile, - APPROX_QUANTILES(remote_css + inline_css, 1000)[OFFSET(percentile * 10)] AS num_stylesheets_per_page, - APPROX_QUANTILES(bytes_css, 1000)[OFFSET(percentile * 10)] AS css_bytes_per_page + SUM(CASE WHEN percentile = 10 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p10, + SUM(CASE WHEN percentile = 25 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p25, + SUM(CASE WHEN percentile = 50 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p50, + SUM(CASE WHEN percentile = 75 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p75, + SUM(CASE WHEN percentile = 90 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p90, + SUM(CASE WHEN percentile = 100 THEN num_stylesheets_per_page ELSE 0 END) AS num_stylesheets_per_page_p100, + SUM(CASE WHEN percentile = 10 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p10, + SUM(CASE WHEN percentile = 25 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p25, + SUM(CASE WHEN percentile = 50 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p50, + SUM(CASE WHEN percentile = 75 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p75, + SUM(CASE WHEN percentile = 90 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p90, + SUM(CASE WHEN percentile = 100 THEN css_bytes_per_page ELSE 0 END) AS css_bytes_per_page_p100 FROM - basedata -CROSS JOIN - UNNEST([10, 25, 50, 75, 90, 100]) AS percentile + percentiles GROUP BY - report_year, percentile + report_year ORDER BY - report_year, percentile; + report_year