Safety & WorkforceFree generatorPDF, Excel, Word exportManufacturing

Safety Incident Analysis SQL Query Generator

Most EHS systems and ERP safety modules hold incidents in a table that anyone with read access can query. This generator writes five commonly needed analyses from your own table and column names, so you can paste them into a SQL client, a BI tool or a report definition.

Fill in

Your table or view name. These queries use generic names, not a specific EHS product.

Assumed to hold 1 for recordable and 0 otherwise.

Generated code
Download:

Incidents and recordables by department

SELECT department AS department,
       COUNT(*) AS total_incidents,
       SUM(CASE WHEN is_recordable = 1 THEN 1 ELSE 0 END) AS recordables
FROM ehs_incident
WHERE site_code = 'PLANT01'
  AND incident_date >= '2026-01-01'
  AND incident_date <= '2026-09-30'
GROUP BY department
ORDER BY recordables DESC, total_incidents DESC;

Monthly trend by incident type

SELECT EXTRACT(YEAR FROM incident_date) AS yr,
       EXTRACT(MONTH FROM incident_date) AS mth,
       incident_type AS incident_type,
       COUNT(*) AS incidents
FROM ehs_incident
WHERE site_code = 'PLANT01'
  AND incident_date >= '2026-01-01'
  AND incident_date <= '2026-09-30'
GROUP BY EXTRACT(YEAR FROM incident_date), EXTRACT(MONTH FROM incident_date), incident_type
ORDER BY yr, mth, incident_type;

Body part pareto with cumulative share

WITH by_part AS (
  SELECT body_part AS body_part, COUNT(*) AS cnt
  FROM ehs_incident
  WHERE site_code = 'PLANT01'
    AND incident_date >= '2026-01-01'
    AND incident_date <= '2026-09-30'
  GROUP BY body_part
)
SELECT body_part,
       cnt,
       ROUND(100.0 * cnt / SUM(cnt) OVER (), 1) AS pct,
       ROUND(100.0 * SUM(cnt) OVER (ORDER BY cnt DESC) / SUM(cnt) OVER (), 1) AS cumulative_pct
FROM by_part
ORDER BY cnt DESC;

Last recordable and days since (PostgreSQL syntax)

SELECT MAX(incident_date) AS last_recordable,
       CURRENT_DATE - MAX(incident_date) AS days_since_last_recordable
FROM ehs_incident
WHERE site_code = 'PLANT01'
  AND is_recordable = 1;

-- SQL Server: DATEDIFF(day, MAX(incident_date), GETDATE())
-- Oracle: TRUNC(SYSDATE) - TRUNC(MAX(incident_date))

Repeat locations (departments with 3 or more incidents in a month)

SELECT department AS department,
       EXTRACT(YEAR FROM incident_date) AS yr,
       EXTRACT(MONTH FROM incident_date) AS mth,
       COUNT(*) AS incidents
FROM ehs_incident
WHERE site_code = 'PLANT01'
  AND incident_date >= '2026-01-01'
  AND incident_date <= '2026-09-30'
GROUP BY department, EXTRACT(YEAR FROM incident_date), EXTRACT(MONTH FROM incident_date)
HAVING COUNT(*) >= 3
ORDER BY incidents DESC;

Table and column names are placeholders: replace them with those in your own EHS database or ERP safety module. EXTRACT and window functions work in PostgreSQL, Oracle and recent MySQL; in SQL Server use YEAR() and MONTH(). Test on a copy of the data and keep personal information out of reports.

Read-only EHS reporting over your data

Optional: we can discuss a private, read-only reporting or question-answering layer over your incident and ERP data.

Optional. The tool and every export work without this. No spam.

How to use it

Fill in the table and column names exactly as they appear in your database, set the site code and date range, and copy each query. Run them with a read-only account. If your data model splits incidents and injuries into separate tables, join them on the incident key before using the pareto query. The queries use generic names so they work with in-house tables, spreadsheets loaded into a database, or exports from a safety app.

What each query answers

The department query shows where incidents cluster. The monthly trend shows whether a campaign is working. The body part pareto, with cumulative percent, points to which injury types make up most of the cases, which is useful when choosing between glove, guarding or ergonomic projects. The repeat location query surfaces departments that need a closer look. The days since query feeds a plant scoreboard.

  • Use leading indicators next to lagging ones where you can
  • Count near misses in a separate query so they are not mixed with injuries
  • Do not publish small cells that could identify an individual
  • Agree on recordable definitions with your regulator rules first

Dialect and data quality notes

Date arithmetic differs by database, so the days since query shows three variants. Free-text body part fields need cleaning before a pareto is meaningful, a pick list is far better. Check that late-entered incidents are dated by the event, not the report date. Compare the output against the OSHA 300 log or your national equivalent as a control total.

Frequently Asked Questions

Will these queries run against my ERP?

They will once you replace the placeholder names with the real table and column names. Some ERPs have no EHS table at all, so you may need your own table or a view that standardizes the fields used here.

Can I calculate TRIR with this?

These queries produce the counts. TRIR also needs hours worked, which usually sit in HR or payroll. Join or enter that value and apply the rate formula, 200,000 times recordables divided by hours worked.

Why use a view rather than the raw table?

A view lets you present stable column names, filter out test records and restrict personal data. It also isolates your reports when the vendor changes the table design during upgrades.

Is it safe to expose injury data in dashboards?

Only with care. Health information about identifiable people is sensitive and may be regulated. Aggregate by department and month, hide small counts and restrict access by role.

If your incident data lives in an ERP, a separate EHS tool and spreadsheets, we can help bring it into one governed dataset with read-only access and a clear audit trail.