From 877474b7f04768a570bdfe53a77cc6b2ab9babb6 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Thu, 27 Aug 2026 22:47:09 -0400 Subject: [PATCH] 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) --- agent/includes/inc_all_client.php | 320 ++++++++++++++---------------- 1 file changed, 148 insertions(+), 172 deletions(-) diff --git a/agent/includes/inc_all_client.php b/agent/includes/inc_all_client.php index d0f9f93f0..18b6c5170 100644 --- a/agent/includes/inc_all_client.php +++ b/agent/includes/inc_all_client.php @@ -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 + * (_client_id, _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']); } }