Problem/Motivation
The webform_statistics view experiences severe performance issues when dealing with large numbers of webform submissions (300k+). The view can take excessive time to load (30+ seconds) and may result in 500 errors when accessing statistics pages.
Root Causes:
- Missing database indexes: No indexes on
createdandlangcodecolumns, causing full table scans - No default filters: Initial page load queries all submissions without date restrictions
- Inefficient aggregation: Using AVG() instead of MAX() for date sorting in grouped queries
- Excessive cache overhead: Tag-based caching adds overhead with large datasets
Performance Impact:
Database query analysis with 377,485 submissions:
EXPLAIN SELECT webform_id, langcode, FROM_UNIXTIME(created, '%Y-%m-%d'), COUNT(*) FROM webform_submission GROUP BY webform_id, langcode, FROM_UNIXTIME(created, '%Y-%m-%d'); Result: Full table scan | 377,485 rows | Using temporary; Using filesort
Steps to reproduce
- Install webform_statistics module
- Create multiple webforms in different languages
- Accumulate 300,000+ webform submissions across webforms and languages
- Navigate to
/admin/structure/webform/submissions/statistics - Navigate to
/admin/structure/webform/submissions/statistics_day - Observe slow page load times (30+ seconds) or PHP timeout/500 errors
- Check database slow query log to confirm full table scans
Proposed resolution
1. Add Database Indexes (Update Hook)
Create update hook webform_statistics_update to add three indexes:
idx_createdonwebform_submission.createdidx_langcodeonwebform_submission.langcodeidx_webform_lang_createdcomposite index on(webform_id, langcode, created)
2. Optimize View Configuration
Update config/install/views.view.webform_statistics.yml:
- Add default
created_fromfilter value:-90 dayswith typeoffset - Change sort
group_typefromavgtomax - Change sort order from
ASCtoDESC - Change cache type from
tagtonone - Add header text informing users about the default date filter
Expected Performance Improvement:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Rows scanned (with 90-day filter) | 377,485 | ~2,000 | 99.4% reduction |
| Result groups | 7,610 | ~105 | 98.6% reduction |
| Page load time | 30+ sec / timeout | <2 seconds | 93%+ faster |
Remaining tasks
- Review and test patch with large datasets (>100k submissions)
- Verify index creation works on different database backends (MySQL, PostgreSQL, SQLite)
- Test that existing sites with indexes don't error on update hook
- Confirm all view displays inherit optimizations (by_day, by_week, by_month, charts)
- Validate that users can still clear the default date filter to see all submissions
- Create release with update notes about performance improvements
User interface changes
Added Elements:
- Header message: Warning notification displayed at top of statistics pages: "By default, this view shows submissions from the last 90 days. Use the filters below to adjust the date range or filter by specific webforms."
Modified Behavior:
- Default date filter: Statistics views now default to showing submissions from last 90 days instead of all time
- Sort order: Results now sorted by most recent submissions first (DESC) instead of oldest first (ASC)
- Filter behavior: Users can clear the "Date from" filter to view all submissions if needed
No Changes To:
- Exposed filter options and controls
- Chart displays and visualizations
- Field labels and formatting
- Permission requirements
API changes
None. This patch does not introduce any API changes. All changes are internal optimizations to database queries and view configuration.
Data model changes
Database Indexes Added:
Three new indexes added to the webform_submission table (managed by the webform module):
| Index Name | Columns | Type | Purpose |
|---|---|---|---|
idx_created |
created | Single column | Optimize date range filtering |
idx_langcode |
langcode | Single column | Optimize language filtering |
idx_webform_lang_created |
webform_id, langcode, created | Composite | Optimize most common query pattern combining all three filters |
Schema Impact:
- No data migration required: Indexes only affect query performance, not data structure
- Storage overhead: Minimal (~5-10% of table size for index storage)
- Write performance: Negligible impact on insert/update operations for webform submissions
- Backwards compatible: Sites without the indexes will continue to function (just slower)
- Rollback safe: Indexes can be safely dropped if needed without data loss
Update Hook:
function webform_statistics_update_*()
Creates indexes with existence checks to prevent errors on re-run or if indexes already exist.
| Comment | File | Size | Author |
|---|---|---|---|
| #7 | WebformStatistics_duplicatemessages.png | 151.3 KB | juanjol |
| #7 | WebformStatistics_filterdontwork.png | 76.56 KB | juanjol |
Issue fork webform_statistics-3558675
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
eduardo morales albertiFix applied, ready to review
Comment #4
eduardo morales albertiRemoved index on webform to avoid altering contrib tables.
Comment #6
juanjolPerfect, thanks Eduardo! I agree it's better not to alter indexes in other modules' tables. Perhaps we should open an issue in the webform queue to request those indexes be added there.
Regarding the MR, with your permission I've updated the hook_update to apply the view changes on sites with the module already installed. I'll proceed to merge the MR, thanks!
Comment #7
juanjolAfter a second review, I've noticed that date filtering has stopped working when filtering by date range, and some messages appear duplicated, as shown in the screenshots. I'm moving this back to "Needs work" for now so we can review it carefully.
Comment #8
eduardo morales albertiReady to review.
Changes:
Comment #9
juanjolThank you! Now all is working as expected, ready to be merged.
Comment #11
juanjol