Speed up client context pages - badge counts are now one query per table instead of one per number (43 -> 27 queries per page load, invoices scanned once instead of eight times)

This commit is contained in:
johnnyq
2026-08-27 22:47:09 -04:00
parent f980893cb1
commit 877474b7f0

View File

@@ -100,207 +100,183 @@ if (isset($_GET['client_id'])) {
}
$client_tags_display = implode('', $client_tag_name_display_array);
//Add up all the payments for the invoice and get the total amount paid to the invoice
$sql_invoice_amounts = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_amounts FROM invoices WHERE invoice_client_id = $client_id AND invoice_status != 'Draft' AND invoice_status != 'Cancelled' AND invoice_status != 'Non-Billable'");
$row = mysqli_fetch_assoc($sql_invoice_amounts);
/*
* Client badge counts and money totals.
*
* These feed the client side nav badges and the client header card, so
* every page in the client context pays for them before it renders a
* byte. They used to be forty separate queries, and none of the
* *_client_id columns they filter on carries an index, so each one was
* a full table scan - invoices alone was scanned eight times per page
* load, tickets twice, domains / certificates / software three times
* each.
*
* Same numbers, one query per table. Conditions that used to sit in a
* WHERE clause are conditional aggregates instead, so a table is read
* once and every figure taken from it comes out of that single pass.
* COALESCE is needed because SUM() over no rows is NULL where COUNT()
* was 0, and these are printed straight into the nav.
*
* Deliberately NOT merged any further: folding the remaining
* single-table counts into one SELECT of scalar subqueries would save
* round trips but not scans, and the scans are what costs. Indexes on
* (<entity>_client_id, <entity>_archived_at) are the other half of
* this and are a schema change, not a code one.
*/
// Invoices - money total plus every status count in one pass.
// The money total ignores invoice_archived_at, matching the original
// query; every count filters on it. Keeping both in one scan means the
// archived test has to move out of the WHERE and into the aggregates.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COALESCE(SUM(CASE WHEN invoice_status NOT IN ('Draft', 'Cancelled', 'Non-Billable') THEN invoice_amount END), 0) AS invoice_amounts,
COALESCE(SUM(invoice_archived_at IS NULL), 0) AS num_invoices,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status = 'Draft'), 0) AS num_invoices_draft,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status = 'Sent'), 0) AS num_invoices_sent,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status = 'Viewed'), 0) AS num_invoices_viewed,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status = 'Partial'), 0) AS num_invoices_partial,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status = 'Paid'), 0) AS num_invoices_paid,
COALESCE(SUM(invoice_archived_at IS NULL AND invoice_status IN ('Sent', 'Viewed', 'Partial')), 0) AS num_invoices_open
FROM invoices WHERE invoice_client_id = $client_id"));
$invoice_amounts = floatval($row['invoice_amounts']);
$num_invoices = intval($row['num_invoices']);
$num_invoices_draft = intval($row['num_invoices_draft']);
$num_invoices_sent = intval($row['num_invoices_sent']);
$num_invoices_viewed = intval($row['num_invoices_viewed']);
$num_invoices_partial = intval($row['num_invoices_partial']);
$num_invoices_paid = intval($row['num_invoices_paid']);
$num_invoices_open = intval($row['num_invoices_open']);
$sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_client_id = $client_id");
$row = mysqli_fetch_assoc($sql_amount_paid);
// Payments - amount paid and the payment half of the income count.
// The sum ignores payment_archived_at, the count does not, same as before.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COALESCE(SUM(payment_amount), 0) AS amount_paid,
COALESCE(SUM(payment_archived_at IS NULL), 0) AS num_payments
FROM payments, invoices
WHERE payment_invoice_id = invoice_id AND invoice_client_id = $client_id"));
$amount_paid = floatval($row['amount_paid']);
$num_payments = intval($row['num_payments']);
$balance = $invoice_amounts - $amount_paid;
//Get Monthly Recurring Total
$sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_monthly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'month' AND recurring_invoice_client_id = $client_id");
$row = mysqli_fetch_assoc($sql_recurring_monthly_total);
// Revenues that are not the far side of a transfer.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(revenue_id) AS num
FROM revenues LEFT JOIN transfers ON transfer_revenue_id = revenue_id
WHERE transfer_id IS NULL AND revenue_archived_at IS NULL AND revenue_client_id = $client_id"));
$num_income = $num_payments + intval($row['num']);
// Recurring invoices - the monthly and yearly totals and the count.
// The two totals key off recurring_invoice_status, the count off
// recurring_invoice_archived_at, so neither test can sit in the WHERE.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COALESCE(SUM(CASE WHEN recurring_invoice_status = 1 AND recurring_invoice_frequency = 'month' THEN recurring_invoice_amount END), 0) AS recurring_monthly_total,
COALESCE(SUM(CASE WHEN recurring_invoice_status = 1 AND recurring_invoice_frequency = 'year' THEN recurring_invoice_amount END), 0) AS recurring_yearly_total,
COALESCE(SUM(recurring_invoice_archived_at IS NULL), 0) AS num_recurring_invoices
FROM recurring_invoices WHERE recurring_invoice_client_id = $client_id"));
$recurring_monthly_total = floatval($row['recurring_monthly_total']);
//Get Yearly Recurring Total
$sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_yearly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'year' AND recurring_invoice_client_id = $client_id");
$row = mysqli_fetch_assoc($sql_recurring_yearly_total);
$recurring_yearly_total = floatval($row['recurring_yearly_total']) / 12;
$recurring_monthly = $recurring_monthly_total + $recurring_yearly_total;
$num_recurring_invoices = intval($row['num_recurring_invoices']);
// Get Credit Balance
$sql_credit_balance = mysqli_query($mysqli, "SELECT SUM(credit_amount) AS credit_balance FROM credits WHERE credit_client_id = $client_id");
$row = mysqli_fetch_assoc($sql_credit_balance);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COALESCE(SUM(credit_amount), 0) AS credit_balance
FROM credits WHERE credit_client_id = $client_id"));
$credit_balance = floatval($row['credit_balance']);
// Badge Counts
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('contact_id') AS num FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id"));
$num_contacts = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('location_id') AS num FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id"));
$num_locations = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('asset_id') AS num FROM assets WHERE asset_archived_at IS NULL AND asset_client_id = $client_id"));
$num_assets = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS num FROM tickets WHERE ticket_archived_at IS NULL AND ticket_closed_at IS NULL AND ticket_status != 4 AND ticket_client_id = $client_id"));
$num_active_tickets = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS num FROM tickets WHERE ticket_archived_at IS NULL AND ticket_closed_at IS NOT NULL AND ticket_client_id = $client_id"));
$num_closed_tickets = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_ticket_id') AS num FROM recurring_tickets WHERE recurring_ticket_client_id = $client_id"));
$num_recurring_tickets = $row['num'];
// Active Project Count
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('project_id') AS num FROM projects WHERE project_archived_at IS NULL AND project_completed_at IS NULL AND project_client_id = $client_id"));
$num_active_projects = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('service_id') AS num FROM services WHERE service_client_id = $client_id"));
$num_services = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('vendor_id') AS num FROM vendors WHERE vendor_archived_at IS NULL AND vendor_client_id = $client_id"));
$num_vendors = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('credential_id') AS num FROM credentials WHERE credential_archived_at IS NULL AND credential_client_id = $client_id"));
$num_credentials = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('network_id') AS num FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id"));
$num_networks = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('rack_id') AS num FROM racks WHERE rack_archived_at IS NULL AND rack_client_id = $client_id"));
$num_racks = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('domain_id') AS num FROM domains WHERE domain_archived_at IS NULL AND domain_client_id = $client_id"));
$num_domains = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('certificate_id') AS num FROM certificates WHERE certificate_archived_at IS NULL AND certificate_client_id = $client_id"));
$num_certificates = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('software_id') AS num FROM software WHERE software_archived_at IS NULL AND software_client_id = $client_id"));
$num_software = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE (invoice_status = 'Sent' OR invoice_status = 'Viewed' OR invoice_status = 'Partial') AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_open = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_status = 'Draft' AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_draft = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_status = 'Sent' AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_sent = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_status = 'Viewed' AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_viewed = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_status = 'Partial' AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_partial = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_status = 'Paid' AND invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices_paid = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('invoice_id') AS num FROM invoices WHERE invoice_archived_at IS NULL AND invoice_client_id = $client_id"));
$num_invoices = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('quote_id') AS num FROM quotes WHERE quote_archived_at IS NULL AND quote_client_id = $client_id"));
$num_quotes = $row['num'];
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_invoice_id') AS num FROM recurring_invoices WHERE recurring_invoice_archived_at IS NULL AND recurring_invoice_client_id = $client_id"));
$num_recurring_invoices = $row['num'];
// Tickets - active and closed in one pass.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
(SELECT COUNT(payment_id) FROM payments, invoices WHERE payment_invoice_id = invoice_id AND payment_archived_at IS NULL AND invoice_client_id = $client_id)
+ (SELECT COUNT(revenue_id) FROM revenues LEFT JOIN transfers ON transfer_revenue_id = revenue_id WHERE transfer_id IS NULL AND revenue_archived_at IS NULL AND revenue_client_id = $client_id)
AS num"));
$num_income = $row['num'];
COALESCE(SUM(ticket_closed_at IS NULL AND ticket_status != 4), 0) AS num_active_tickets,
COALESCE(SUM(ticket_closed_at IS NOT NULL), 0) AS num_closed_tickets
FROM tickets WHERE ticket_archived_at IS NULL AND ticket_client_id = $client_id"));
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('file_id') AS num FROM files WHERE file_archived_at IS NULL AND file_client_id = $client_id"));
$num_files = $row['num'];
$num_active_tickets = intval($row['num_active_tickets']);
$num_closed_tickets = intval($row['num_closed_tickets']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('document_id') AS num FROM documents WHERE document_archived_at IS NULL AND document_client_id = $client_id"));
$num_documents = $row['num'];
// Domains - total plus both expiry buckets in one pass.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COUNT(domain_id) AS num_domains,
COALESCE(SUM(domain_expire IS NOT NULL AND domain_expire < CURRENT_DATE + INTERVAL 45 DAY), 0) AS num_domains_expiring_warning,
COALESCE(SUM(domain_expire IS NOT NULL AND (domain_expire < CURRENT_DATE OR domain_expire < CURRENT_DATE + INTERVAL 7 DAY)), 0) AS num_domains_urgent
FROM domains WHERE domain_archived_at IS NULL AND domain_client_id = $client_id"));
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('event_id') AS num FROM calendar_events WHERE event_client_id = $client_id"));
$num_calendar_events = $row['num'];
$num_domains = intval($row['num_domains']);
$num_domains_expiring_warning = intval($row['num_domains_expiring_warning']);
$num_domains_urgent = intval($row['num_domains_urgent']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('trip_id') AS num FROM trips WHERE trip_archived_at IS NULL AND trip_client_id = $client_id"));
$num_trips = $row['num'];
// Certificates - total plus both expiry buckets in one pass.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COUNT(certificate_id) AS num_certificates,
COALESCE(SUM(certificate_expire IS NOT NULL AND certificate_expire < CURRENT_DATE + INTERVAL 7 DAY), 0) AS num_certificates_expiring,
COALESCE(SUM(certificate_expire IS NOT NULL AND (certificate_expire < CURRENT_DATE OR certificate_expire < CURRENT_DATE + INTERVAL 1 DAY)), 0) AS num_certificates_expired
FROM certificates WHERE certificate_archived_at IS NULL AND certificate_client_id = $client_id"));
// Expiring Items
$num_certificates = intval($row['num_certificates']);
$num_certificates_expiring = intval($row['num_certificates_expiring']);
$num_certificates_expired = intval($row['num_certificates_expired']);
// Count Domains Expiring within 45 Days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('domain_id') AS num FROM domains
WHERE domain_client_id = $client_id
AND domain_expire IS NOT NULL
AND domain_expire < CURRENT_DATE + INTERVAL 45 DAY
AND domain_archived_at IS NULL"
));
$num_domains_expiring_warning= intval($row['num']);
// Software - total plus both expiry buckets in one pass.
// The 45-day window is what the original query used; the comment above
// it said 90 days and was wrong.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT
COUNT(software_id) AS num_software,
COALESCE(SUM(software_expire IS NOT NULL AND software_expire < CURRENT_DATE + INTERVAL 45 DAY), 0) AS num_software_expiring,
COALESCE(SUM(software_expire IS NOT NULL AND (software_expire < CURRENT_DATE OR software_expire < CURRENT_DATE + INTERVAL 7 DAY)), 0) AS num_software_expired
FROM software WHERE software_archived_at IS NULL AND software_client_id = $client_id"));
// Count Domains Expired or within 7 days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('domain_id') AS num FROM domains
WHERE domain_client_id = $client_id
AND domain_expire IS NOT NULL
AND (
domain_expire < CURRENT_DATE
OR domain_expire < CURRENT_DATE + INTERVAL 7 DAY
)
AND domain_archived_at IS NULL"
));
$num_domains_urgent = intval($row['num']);
$num_software = intval($row['num_software']);
$num_software_expiring = intval($row['num_software_expiring']);
$num_software_expired = intval($row['num_software_expired']);
// Count Certificates Expiring within 7 Days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('certificate_id') AS num FROM certificates
WHERE certificate_client_id = $client_id
AND certificate_expire IS NOT NULL
AND certificate_expire < CURRENT_DATE + INTERVAL 7 DAY
AND certificate_archived_at IS NULL"
));
$num_certificates_expiring = intval($row['num']);
// One count each, one table each - nothing to fold together.
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS num FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id"));
$num_contacts = intval($row['num']);
// Count Certificates Expired or within 7 days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('certificate_id') AS num FROM certificates
WHERE certificate_client_id = $client_id
AND certificate_expire IS NOT NULL
AND (
certificate_expire < CURRENT_DATE
OR certificate_expire < CURRENT_DATE + INTERVAL 1 DAY
)
AND certificate_archived_at IS NULL"
));
$num_certificates_expired = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(location_id) AS num FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id"));
$num_locations = intval($row['num']);
// Count Software Expiring within 90 Days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('software_id') AS num FROM software
WHERE software_client_id = $client_id
AND software_expire IS NOT NULL
AND software_expire < CURRENT_DATE + INTERVAL 45 DAY
AND software_archived_at IS NULL"
));
$num_software_expiring = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(asset_id) AS num FROM assets WHERE asset_archived_at IS NULL AND asset_client_id = $client_id"));
$num_assets = intval($row['num']);
// Count Software Expired or within 14 days
$row = mysqli_fetch_assoc(mysqli_query(
$mysqli,
"SELECT COUNT('software_id') AS num FROM software
WHERE software_client_id = $client_id
AND software_expire IS NOT NULL
AND (
software_expire < CURRENT_DATE
OR software_expire < CURRENT_DATE + INTERVAL 7 DAY
)
AND software_archived_at IS NULL"
));
$num_software_expired = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_ticket_id) AS num FROM recurring_tickets WHERE recurring_ticket_client_id = $client_id"));
$num_recurring_tickets = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(project_id) AS num FROM projects WHERE project_archived_at IS NULL AND project_completed_at IS NULL AND project_client_id = $client_id"));
$num_active_projects = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(service_id) AS num FROM services WHERE service_client_id = $client_id"));
$num_services = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(vendor_id) AS num FROM vendors WHERE vendor_archived_at IS NULL AND vendor_client_id = $client_id"));
$num_vendors = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(credential_id) AS num FROM credentials WHERE credential_archived_at IS NULL AND credential_client_id = $client_id"));
$num_credentials = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(network_id) AS num FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id"));
$num_networks = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(rack_id) AS num FROM racks WHERE rack_archived_at IS NULL AND rack_client_id = $client_id"));
$num_racks = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(quote_id) AS num FROM quotes WHERE quote_archived_at IS NULL AND quote_client_id = $client_id"));
$num_quotes = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(file_id) AS num FROM files WHERE file_archived_at IS NULL AND file_client_id = $client_id"));
$num_files = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(document_id) AS num FROM documents WHERE document_archived_at IS NULL AND document_client_id = $client_id"));
$num_documents = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(event_id) AS num FROM calendar_events WHERE event_client_id = $client_id"));
$num_calendar_events = intval($row['num']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(trip_id) AS num FROM trips WHERE trip_archived_at IS NULL AND trip_client_id = $client_id"));
$num_trips = intval($row['num']);
}
}