Order by slow on joined large table - mysql

I have 3 large table around 1 table have 1,000,000 rows other 2 have 500,000 rows.
When I use ORDER BY in query it takes 15 seconds (without ORDER BY 0.001s).
I already try add index on invoice.created_at and invoice.invoice_id
But still take a lot of time.
SELECT
`i`.*,
GROUP_CONCAT(DISTINCT ii.item_id SEPARATOR ',') AS item_id,
GROUP_CONCAT(DISTINCT it.transaction_id SEPARATOR ',') AS transactions,
SUM(DISTINCT IF(ii.status = 1, ii.subtotal, 0)) AS subtotal,
SUM(DISTINCT it.subtotal) AS total_paid_amount,
SUM(DISTINCT it.additional_fee) AS total_additional_fee_amount,
SUM(ii.quantity) AS total_quantity,
(total_amount - SUM(DISTINCT COALESCE(it.amount, 0))) AS total_balance
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
AND `it`.`process_status` = 1
AND `it`.`status` = 1
WHERE
`i`.`status` = '1'
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Query with EXPLAIN:
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
| 1 | SIMPLE | i | NULL | index | PRIMARY,customer_id,code,invoice_id_2,created_at | PRIMARY | 4 | NULL | 473309 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | ii | NULL | ref | invoice_id | invoice_id | 5 | test.i.invoice_id | 2 | 100.00 | NULL |
| 1 | SIMPLE | it | NULL | ref | invoice_id,status | invoice_id | 5 | test.i.invoice_id | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+------------------------------+--------+----------+----------------------------------------------+
I try to remove SUM ,GROUP_CONCAT and WHERE CASE,
But still need 10 sec
SELECT
`i`.*
FROM
`invoices` `i`
LEFT JOIN
`invoices_items` `ii` ON `ii`.`invoice_id` = `i`.`invoice_id`
LEFT JOIN
`invoices_transactions` `it` ON `it`.`invoice_id` = `i`.`invoice_id`
GROUP BY `i`.`invoice_id`
ORDER BY `i`.`created_at` DESC
LIMIT 50
Create Table:
CREATE TABLE `invoices` (
`invoice_id` int NOT NULL AUTO_INCREMENT,
`warehouse_id` tinyint DEFAULT NULL,
`invoice_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i NOT NULL DEFAULT 'C',
`invoice_date` date DEFAULT NULL,
`customer_id` int DEFAULT NULL,
`contact_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_no` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`contact_email` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`address` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`total_amount` deimal(10,2) DEFAULT '0.00',
`total_cost` deimal(20,2) DEFAULT NULL,
`delivery_type` tinyint DEFAULT '0',
`remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i,
`payment_status` tinyint(1) NOT NULL DEFAULT '3',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`updated_by` int NOT NULL,
`confirmed_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '2'
PRIMARY KEY (`invoice_id`),
KEY `customer_id` (`customer_id`),
KEY `code` (`code`),
KEY `invoice_id_2` (`invoice_id`,`created_at`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=513697 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_items` (
`item_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int NOT NULL,
`warehouse_id` int DEFAULT NULL,
`product_id` int DEFAULT NULL,
`variant_id` int DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`quantity` int DEFAULT '1',
`unit_price` deimal(10,2) DEFAULT NULL,
`cost` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`serial_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`StockoutDate` datetime DEFAULT NULL,
`ReturnDate` datetime DEFAULT NULL,
`Return_StockLocationID` tinyint DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
`priority` int NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
KEY `invoice_id` (`invoice_id`),
KEY `variant_id` (`variant_id`),
KEY `quantity` (`quantity`),
KEY `unit_price` (`unit_price`),
KEY `subtotal` (`subtotal`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `priority` (`priority`),
KEY `variant_id_2` (`variant_id`,`quantity`),
KEY `variant_id_3` (`variant_id`,`name`,`quantity`),
KEY `product_id` (`product_id`),
KEY `StockoutDate` (`StockoutDate`)
) ENGINE=InnoDB AUTO_INCREMENT=1200951 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;
CREATE TABLE `invoices_transactions` (
`transaction_id` int NOT NULL AUTO_INCREMENT,
`invoice_id` int DEFAULT NULL,
`warehouse_id` int DEFAULT NULL,
`invoice_date` date DEFAULT NULL,
`payment_id` int DEFAULT NULL,
`balance` deimal(10,2) DEFAULT NULL,
`amount` deimal(10,2) DEFAULT NULL,
`additional_rate` deimal(10,2) DEFAULT NULL,
`additional_fee` deimal(10,2) DEFAULT NULL,
`subtotal` deimal(10,2) DEFAULT NULL,
`process_status` int DEFAULT '1',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`payment_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_i DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '1',
`CreatedByID` int DEFAULT NULL,
`ModifiedByID` int DEFAULT NULL,
`priority` int DEFAULT '0',
PRIMARY KEY (`transaction_id`),
KEY `invoice_id` (`invoice_id`),
KEY `payment_status` (`payment_status`),
KEY `created_at` (`created_at`),
KEY `updated_at` (`updated_at`),
KEY `status` (`status`),
KEY `amount` (`amount`),
KEY `additional_fee` (`additional_fee`),
KEY `subtotal` (`subtotal`),
KEY `transaction_id` (`transaction_id`,`amount`,`additional_fee`,`subtotal`),
KEY `invoice_id_2` (`invoice_id`,`process_status`,`status`),
KEY `process_status` (`process_status`),
KEY `transaction_id_2` (`transaction_id`,`invoice_id`,`amount`,`additional_fee`,`subtotal`,`process_status`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=543606 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_i;

Let's find the 50 invoices first, then reach for the rest of the stuff:
SELECT ... (all the current stuff)
FROM ( SELECT invoice_id
FROM invoices
WHERE status = 1
ORDER BY created_at DESC
LIMIT 50
) AS ids
JOIN invoices AS i USING(invoice_id)
LEFT JOIN ... ON ...
LEFT JOIN ... ON ...
GROUP BY invoice_id
ORDER BY created_id DESC
These composite indexes may help:
i: (status, created_at, invoice_id)
it: (status, process_status, invoice_id)
ii: (invoice_id, quantity, subtotal, status, item_id)
Redundant index:
KEY `variant_id` (`variant_id`) -- since this is the prefix of others
Because of PRIMARY KEY(transaction_id), the keys starting with transaction_id are redundant.
Summary:
I provided a "covering index" with the columns in the right order to allow finding the 50 ids directly from that index.
The rest of the work is limited to the 50 rows (or sets of rows, since I suspect that the rest are many:1 with respect to invoices.)
Your EXPLAIN shows lots of "Rows" and a "filesort" -- indicating that it gathered everything, then did the group by, then sorted, and finally, peeled of 50.
I suspect the "filesort" was really two sorts. See EXPLAIN FORMAT=JSON ... to find out.

Related

Improve a query from Explain results

I have a complex query that is dynamically assembled based upon search criteria. However, in its simplest form, it is still very slow. The main table it runs against has ~10M records. I ran an explain against a 'base' query and the first row of the explain looks bad (at least to a novice dba like me). I have read a couple tutorials about EXPLAIN, but I still am unsure how to fix the query. So, the first row of the results seems to indicate the problem, but I don't know what to do with it. I couldn't make a composite key that long even if I wanted to and some of the field names in that possible_keys column are not even in the patients table. Any help will be greatly appreciated.
id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,SIMPLE,patients,range,"PRIMARY,location,appt_date,status,radiologist,contract,lastname,paperwork,images_archived,hash,created,document_attached,all_images_archived,last_image_archived,modality,study_uid,company,second_access,firstname,report_delivered,ssn,order_entry_status,dob,tech,doctor,mobile_facility,accession,location_appt_date,location_created,location_lastname,ref,person_seq",location_appt_date,55,NULL,573534,"Using index condition; Using where; Using temporary; Using filesort"
1,SIMPLE,receivable_transactions,ref,patient_seq,patient_seq,4,ris-dev.patients.seq,1,NULL
1,SIMPLE,patients_dispatch,ref,patient_seq,patient_seq,4,ris-dev.patients.seq,1,NULL
1,SIMPLE,mobile_facility,ref,"unique_index,name,location",unique_index,115,"ris-dev.patients.mobile_facility,const",1,"Using where"
1,SIMPLE,mobile_facility_service_areas,eq_ref,PRIMARY,PRIMARY,4,ris-dev.mobile_facility.service_area,1,NULL
Edit: same EXPLAIN, but reformatted to be easier to read:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE patients range PRIMARY location_appt_date 55 NULL 573534 Using index condition; Using where; Using temporary; Using filesort
location
appt_date
status
radiologist
contract
lastname
paperwork
images_archived
hash
created
document_attached
all_images_archived
last_image_archived
modality
study_uid
company
second_access
firstname
report_delivered
ssn
order_entry_status
dob
tech
doctor
mobile_facility
accession
location_appt_date
location_created
location_lastname
ref
person_seq
1 SIMPLE receivable_transactions ref patient_seq patient_seq 4 ris-dev.patients.seq 1 NULL
1 SIMPLE patients_dispatch ref patient_seq patient_seq 4 ris-dev.patients.seq 1 NULL
1 SIMPLE mobile_facility ref unique_index unique_index 115 ris-dev.patients.mobile_facility,const 1 Using where
name
location
1 SIMPLE mobile_facility_service_areas eq_ref PRIMARY PRIMARY 4 ris-dev.mobile_facility.service_area 1 NULL
The explain is setup against the following query and table structures.
SELECT patients.fax_in_queue, patients.modality, patients.stat, patients.created, patients.seq, patients.lastname,
patients.firstname, patients.appt_date, patients.status, patients.contract, patients.location, patients.unique_hash,
patients.images_archived, patients.report_delivered, patients.doctor, patients.mobile_facility, patients.history,
patients.dob, patients.all_images_archived, patients.order_entry_status, patients.tech, patients.radiologist,
patients.last_image_archived, patients.state, patients.ss_comments, patients.completed, patients.report_status,
patients.have_paperwork, patients.facility_room_number, patients.facility_station_name, patients.facility_bed,
patients.findings_level, patients.document_attached, patients.study_start, patients.company, patients.accession,
patients.number_images, patients.client_number_images, patients.sex, patients.threshhold , GROUP_CONCAT(CONCAT(CONCAT(receivable_transactions.modifier, " "),
receivable_transactions.description) SEPARATOR ", ") AS rt_desc , patients_dispatch.seq AS doc_seq, patients_dispatch.requisition_last_sent,
patients_dispatch.requisition_signed_by_file_seq, patients_dispatch.requisition_signed, patients_dispatch.order_reason, patients_dispatch.order_comments,
patients_dispatch.order_taken, patients_dispatch.order_tech_last_notified, patients_dispatch.order_tech_in_transit, patients_dispatch.order_tech_in,
patients_dispatch.order_tech_out, patients_dispatch.order_tech_ack, patients_dispatch.addr1 AS d_addr1, patients_dispatch.addr2 AS d_addr2,
patients_dispatch.city AS d_city, patients_dispatch.state AS d_state, patients_dispatch.zip AS d_zip, CONCAT(patients.status, order_tech_out,
order_tech_in, order_tech_in_transit) as pseudo_status , mobile_facility.requisition_fax, mobile_facility.station_list, mobile_facility.address1 as mf_addr1,
mobile_facility.address2 as mf_addr2, mobile_facility.city as mf_city, mobile_facility.state as mf_state, mobile_facility.zip as mf_zip,
mobile_facility.phone as mf_phone, mobile_facility.phone2 as mf_phone2, mobile_facility_service_areas.name as mf_service_area
FROM patients LEFT JOIN receivable_transactions ON patients.seq = receivable_transactions.patient_seq
LEFT JOIN patients_dispatch ON patients.seq = patients_dispatch.patient_seq
LEFT JOIN mobile_facility ON patients.location = mobile_facility.location AND patients.mobile_facility = mobile_facility.name
LEFT JOIN mobile_facility_service_areas ON mobile_facility.service_area = mobile_facility_service_areas.seq
WHERE patients.location = "XYZCompany" AND ((patients.appt_date >= '2020-03-19' AND patients.appt_date <= '2020-03-19 23:59:59')
OR (patients.appt_date <= '2020-03-19' AND patients.status < 'X'))
GROUP BY patients.seq DESC
ORDER BY patients.status, patients.order_entry_status, pseudo_status, patients.order_entry_status,patients.lastname);
CREATE TABLE `patients` (
`seq` int(11) NOT NULL AUTO_INCREMENT,
`person_seq` int(11) NOT NULL,
`firstname` varchar(20) NOT NULL DEFAULT '',
`lastname` varchar(30) NOT NULL DEFAULT '',
`middlename` varchar(20) NOT NULL DEFAULT '',
`ref` varchar(50) NOT NULL DEFAULT '',
`location` varchar(50) NOT NULL DEFAULT '',
`doctor` varchar(50) NOT NULL,
`radiologist` varchar(20) NOT NULL DEFAULT '',
`contract` varchar(50) NOT NULL,
`history` mediumtext NOT NULL,
`dob` varchar(15) NOT NULL DEFAULT '0000-00-00',
`appt_date` date NOT NULL DEFAULT '0000-00-00',
`status` tinyint(4) NOT NULL DEFAULT '0',
`tech` varchar(50) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`ss_comments` mediumtext NOT NULL,
`mobile_facility` varchar(60) NOT NULL DEFAULT '',
`facility_room_number` varchar(50) NOT NULL,
`facility_bed` varchar(20) NOT NULL,
`facility_station_name` varchar(50) NOT NULL,
`stat` tinyint(4) NOT NULL DEFAULT '0',
`have_paperwork` tinyint(4) NOT NULL DEFAULT '0',
`completed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`sex` char(1) NOT NULL DEFAULT '',
`unique_hash` varchar(100) NOT NULL DEFAULT '',
`number_images` int(11) NOT NULL DEFAULT '0',
`client_number_images` int(11) NOT NULL,
`images_archived` tinyint(4) NOT NULL DEFAULT '0',
`completed_fax` varchar(10) NOT NULL DEFAULT '0' COMMENT 'This is the number the completed report is faxed to.',
`report_delivered` tinyint(4) NOT NULL DEFAULT '0',
`report_delivered_time` datetime NOT NULL,
`document_attached` tinyint(4) NOT NULL DEFAULT '0',
`modality` varchar(3) NOT NULL,
`last_image_archived` datetime NOT NULL,
`all_images_archived` tinyint(4) NOT NULL DEFAULT '0',
`fax_in_queue` varchar(12) NOT NULL,
`accession` varchar(100) NOT NULL,
`study_uid` varchar(100) NOT NULL,
`order_entry_status` tinyint(4) NOT NULL,
`compare_to` varchar(15) NOT NULL,
`state` varchar(3) NOT NULL,
`company` int(11) NOT NULL,
`second_access` varchar(50) NOT NULL,
`threshhold` datetime NOT NULL,
`report_status` tinyint(4) NOT NULL,
`second_id` varchar(50) NOT NULL,
`rad_alerted` tinyint(4) NOT NULL,
`assigned` datetime NOT NULL,
`findings_level` tinyint(4) NOT NULL,
`report_viewed` tinyint(4) NOT NULL,
`study_received` datetime NOT NULL,
`study_start` datetime NOT NULL,
`study_end` datetime NOT NULL,
`completed_email` varchar(50) NOT NULL,
`completed_send` varchar(255) NOT NULL,
`ssn` varchar(12) NOT NULL,
`exorder_number` varchar(30) NOT NULL,
`exvisit_number` varchar(30) NOT NULL,
`row_updated` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`seq`),
KEY `location` (`location`),
KEY `appt_date` (`appt_date`),
KEY `status` (`status`),
KEY `radiologist` (`radiologist`),
KEY `contract` (`contract`),
KEY `lastname` (`lastname`),
KEY `paperwork` (`have_paperwork`),
KEY `images_archived` (`images_archived`),
KEY `hash` (`unique_hash`),
KEY `created` (`created`),
KEY `document_attached` (`document_attached`),
KEY `all_images_archived` (`all_images_archived`),
KEY `last_image_archived` (`last_image_archived`),
KEY `modality` (`modality`),
KEY `study_uid` (`study_uid`),
KEY `company` (`company`),
KEY `second_access` (`second_access`),
KEY `firstname` (`firstname`),
KEY `report_delivered` (`report_delivered`),
KEY `ssn` (`ssn`),
KEY `order_entry_status` (`order_entry_status`),
KEY `dob` (`dob`),
KEY `tech` (`tech`),
KEY `doctor` (`doctor`),
KEY `mobile_facility` (`mobile_facility`),
KEY `accession` (`accession`),
KEY `location_appt_date` (`location`,`appt_date`),
KEY `location_created` (`location`,`created`),
KEY `location_lastname` (`location`,`lastname`),
KEY `ref` (`ref`),
KEY `person_seq` (`person_seq`)
) ENGINE=InnoDB AUTO_INCREMENT=10242952 DEFAULT CHARSET=latin1;
CREATE TABLE `receivable_transactions` (
`seq` int(11) NOT NULL AUTO_INCREMENT,
`patient_seq` int(11) NOT NULL DEFAULT '0',
`cptcode` varchar(15) NOT NULL DEFAULT '',
`modifier` char(2) NOT NULL DEFAULT '',
`description` varchar(100) NOT NULL DEFAULT '',
`amount` decimal(6,2) NOT NULL DEFAULT '0.00',
`type` char(2) NOT NULL DEFAULT '',
`transaction` varchar(10) NOT NULL DEFAULT '',
`radiologist` varchar(20) NOT NULL DEFAULT '',
`status` tinyint(4) NOT NULL DEFAULT '0',
`completed` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`report_meta_seq` int(11) NOT NULL DEFAULT '0',
`report_header` varchar(255) NOT NULL,
`report_body` blob NOT NULL,
`report_impression` mediumtext NOT NULL,
`report_hide` tinyint(4) NOT NULL,
`radiologist_group` varchar(50) NOT NULL,
`addendum` int(4) NOT NULL DEFAULT '0',
`addendum_type` varchar(20) NOT NULL,
`peer_review` int(4) NOT NULL DEFAULT '0',
`qa_reason` varchar(255) NOT NULL DEFAULT '',
`qa_agree` decimal(2,1) NOT NULL DEFAULT '0.0',
`findings` tinyint(4) NOT NULL,
`comments` mediumtext NOT NULL,
`company` int(11) NOT NULL,
`row_updated` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`seq`),
KEY `patient_seq` (`patient_seq`),
KEY `cptcode` (`cptcode`),
KEY `transaction` (`transaction`),
KEY `type` (`type`),
KEY `created` (`created`),
KEY `radiologist` (`radiologist`),
KEY `status` (`status`),
KEY `report_meta_seq` (`report_meta_seq`),
KEY `Billing Check Dropdown` (`status`,`completed`),
KEY `qa_agree` (`qa_agree`),
KEY `peer_review` (`peer_review`),
KEY `addendum` (`addendum`),
KEY `company` (`company`),
KEY `completed` (`completed`)
) ENGINE=InnoDB AUTO_INCREMENT=9380351 DEFAULT CHARSET=latin1;
CREATE TABLE `patients_dispatch` (
`seq` int(11) NOT NULL AUTO_INCREMENT,
`patient_seq` int(11) NOT NULL,
`order_taken` datetime NOT NULL,
`order_taken_by` varchar(50) NOT NULL,
`order_person_calling` varchar(50) NOT NULL,
`order_supervising_physician` varchar(50) NOT NULL,
`order_trip_count` tinyint(4) NOT NULL,
`order_trip_count_max` tinyint(4) NOT NULL,
`order_trip_visit` tinyint(4) NOT NULL,
`order_tech_in` datetime NOT NULL,
`order_tech_out` datetime NOT NULL,
`order_ssn` varchar(12) NOT NULL,
`order_service_request_time` datetime NOT NULL,
`order_reason` varchar(255) NOT NULL,
`order_tech_ack` datetime NOT NULL,
`order_tech_assigned` datetime NOT NULL,
`order_tech_last_notified` datetime NOT NULL,
`requisition_last_sent` datetime NOT NULL,
`requisition_signed` datetime NOT NULL,
`requisition_signed_by` varchar(50) NOT NULL,
`requisition_signed_by_text` varchar(75) NOT NULL,
`requisition_signed_by_file_seq` int(11) NOT NULL,
`order_comments` mediumtext NOT NULL,
`order_tech_in_transit` datetime NOT NULL,
`fasting` tinyint(1) NOT NULL,
`collection_time` time DEFAULT NULL,
`addr1` varchar(100) NOT NULL,
`addr2` varchar(100) NOT NULL,
`city` varchar(30) NOT NULL,
`state` varchar(3) NOT NULL,
`zip` varchar(12) NOT NULL,
`phone` varchar(15) NOT NULL,
`mileage_start` int(11) NOT NULL,
`mileage_end` int(11) NOT NULL,
PRIMARY KEY (`seq`),
KEY `patient_seq` (`patient_seq`)
) ENGINE=InnoDB AUTO_INCREMENT=2261091 DEFAULT CHARSET=latin1;
CREATE TABLE `mobile_facility` (
`seq` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(60) NOT NULL,
`location` varchar(50) DEFAULT NULL,
`address1` varchar(50) NOT NULL,
`address2` varchar(50) NOT NULL,
`city` varchar(50) NOT NULL,
`state` varchar(2) NOT NULL,
`zip` varchar(10) NOT NULL,
`phone` varchar(15) NOT NULL,
`phone2` varchar(15) NOT NULL,
`fax` varchar(110) NOT NULL,
`rads_can_read` text NOT NULL,
`rads_cant_read` text NOT NULL,
`only_techs` text NOT NULL,
`never_modalities` varchar(255) NOT NULL COMMENT 'A serialized list of modalities a facility may not use.',
`station_list` mediumtext NOT NULL,
`email` varchar(255) NOT NULL,
`misc1` varchar(255) NOT NULL,
`latitude` float NOT NULL DEFAULT '0',
`longitude` float NOT NULL DEFAULT '0',
`affiliation` int(11) NOT NULL COMMENT 'mobile_facility_affiliations seq',
`branch` int(11) NOT NULL COMMENT 'mobile_facility_branches seq',
`service_area` int(11) NOT NULL COMMENT 'mobile_facility_service_areas seq',
`other_id` varchar(50) NOT NULL COMMENT 'Usually used for HL7',
`facility_type` varchar(2) DEFAULT NULL,
`no_stat` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Should the facility allow stat priority on patients?',
`facility_notes` varchar(512) DEFAULT NULL,
`requisition_fax` varchar(110) NOT NULL,
`report_template` text NOT NULL,
`all_orders_stat` tinyint(1) NOT NULL,
`sms_notification` varchar(15) NOT NULL,
`tat` varchar(10) NOT NULL,
`npi` varchar(15) NOT NULL,
`NMXR` tinyint(4) NOT NULL DEFAULT '0',
`billing_type` varchar(10) NOT NULL,
`salesman` varchar(75) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`default_bill_to` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`seq`),
UNIQUE KEY `unique_index` (`name`,`location`),
KEY `name` (`name`),
KEY `location` (`location`)
) ENGINE=InnoDB AUTO_INCREMENT=155104 DEFAULT CHARSET=latin1;
CREATE TABLE `mobile_facility_service_areas` (
`seq` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`location` varchar(50) NOT NULL,
PRIMARY KEY (`seq`)
) ENGINE=InnoDB AUTO_INCREMENT=841 DEFAULT CHARSET=latin1;
It's only using the index on location, but that only narrows down the search to about a half a million rows. You'd like it to use an index to further narrow down by the appt_date.
However, the use of OR in your WHERE clause is causing a problem. It can't decide how to use the index.
Here's what I suggest:
Drop the index on location because it's redundant with the other indexes that have location as their first column.
Replace the index on location_appt_date with an index on location_appt_date_status.
ALTER TABLE patients
DROP KEY location,
DROP KEY location_appt_date,
ADD KEY location_appt_date_status (location, appt_date, status);
Refactor the query to use UNION instead of OR:
SELECT ... (all the columns you have) ...
FROM (
SELECT * FROM patients USE INDEX (location_appt_date_status)
WHERE location = 'XYZCompany' AND appt_date >= '2020-03-19' AND appt_date < '2020-03-20'
UNION
SELECT * FROM patients USE INDEX (location_appt_date_status)
WHERE location = 'XYZCompany' AND appt_date <= '2020-03-19' AND status < 'X'
) AS p
LEFT JOIN receivable_transactions FORCE INDEX (patient_seq)
ON p.seq = receivable_transactions.patient_seq
LEFT JOIN patients_dispatch FORCE INDEX (patient_seq)
ON p.seq = patients_dispatch.patient_seq
INNER JOIN mobile_facility FORCE INDEX (unique_index)
ON p.location = mobile_facility.location AND p.mobile_facility = mobile_facility.name
INNER JOIN mobile_facility_service_areas FORCE INDEX (PRIMARY)
ON mobile_facility.service_area = mobile_facility_service_areas.seq
GROUP BY p.seq
ORDER BY p.status, p.order_entry_status, pseudo_status, p.order_entry_status, p.lastname
You might not need all the USE INDEX() / FORCE INDEX() optimizer hints I used. I did those because I was testing with empty tables, and that can confuse the optimizer.
Let me focus on the part that affects optimization the most:
FROM patients AS p
LEFT JOIN receivable_transactions AS rt ON p.seq = rt.patient_seq
LEFT JOIN patients_dispatch AS pd ON p.seq = pd.patient_seq
LEFT JOIN mobile_facility AS mf ON p.location = mf.location
AND p.mobile_facility AS mf = mf.name
LEFT JOIN mobile_facility_service_areas AS sa ON mf.service_area = sa.seq
WHERE p.location = "XYZCompany"
AND ((p.appt_date >= '2020-03-19'
AND p.appt_date <= '2020-03-19 23:59:59')
OR (p.appt_date <= '2020-03-19'
AND p.status < 'X')
)
GROUP BY p.seq DESC
ORDER BY p.status, p.order_entry_status, pseudo_status, p.order_entry_status,
p.lastname);
The biggest issue is the OR. It often prevents most optimizations. The usual fix is to turn it into a UNION:
( SELECT ...
FROM .. JOIN ..
WHERE p.location = "XYZCompany"
AND p.appt_date >= '2020-03-19'
AND p.appt_date < '2020-03-19' + INTERVAL 1 DAY
...
)
UNION ALL
( SELECT ...
FROM .. JOIN ..
WHERE p.location = "XYZCompany"
AND p.appt_date <= '2020-03-19'
AND p.status < 'X'
...
)
Each select can benefit from this composite index on patients:
(location, appt_date, status)
The < 'X' is problematic because two ranges (appt_date and status) cannot both be used effectively. What are the possible values of status? If there is only one value before 'X', say 'M', then this would be much better: p.status = 'M' together with another index: (location, status, appt_date)
SELECT lots of stuff, then GROUP BY p.seq -- This is probably create strange results. (Search for ONLY_FULL_GROUP_BY for more discussion). It may be better to first get the patients.seq values (since that is all you are filtering on), then join to the other tables. This would eliminate the GROUP BY, or at least force you to deal with which row to fetch from each of the other tables.
range location_appt_date 55 573534 Using index condition; Using where; Using temporary; Using filesort -- says
55 = 2+50 (for varchar(50)) + 3 (for date) -- neither is NULL.
Based on the 55, I wonder if it is so well optimized that the OR->UNION is not needed.
"Using index condition" is internally called ICP (Index Condition Pushdown) if you want further understanding.
"Using filesort" may be an understatement -- There are probably two sorts, one for GROUP BY, one for ORDER BY. EXPLAIN FORMAT=JSON SELECT ... would make it clear. (And hence my hint that the GROUP BY should be avoided.
You have some redundant indexes (not relevant to much other than disk space): INDEX(a,b), INDEX(a) --> toss INDEX(a).
patients has an awful number of indexes.
The other tables seem to have adequate indexes for your query.

MySQL Slow ORDER BY when done on JOIN value?

I have this query:
SELECT
c.*,
cv.views
FROM
content AS c
JOIN
content_views AS cv ON cv.content = c.record_num
WHERE
c.enabled = 1
ORDER BY
cv.views
Quite simple, but it's really slow... Is there a way to make it faster ?
This is my EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE c ref enabled_2,enabled enabled 4 const 23947 Using temporary; Using filesort
1 SIMPLE cv eq_ref PRIMARY PRIMARY 4 c.record_num 1
EDIT 2016-02-24
Please note that usually, I use a LIMIT so the number of records returned in the EXPLAIN isn't entirely accurate, however for the sake of simplicity and because the performance doesn't change with the LIMIT or without it, I have removed it.
As requested in the comments, this is the result of my SHOW CREATE TABLE. As you can see, one of my table is MyISAM while the other is InnoDB.
CREATE TABLE `content` (
`title` varchar(255) NOT NULL DEFAULT '',
`filename` varchar(255) NOT NULL DEFAULT '',
`filename_2` varchar(255) NOT NULL,
`filename_3` varchar(255) NOT NULL,
`orig_filename` varchar(255) NOT NULL,
`trailer_filename` varchar(255) NOT NULL,
`thumbnail` varchar(255) NOT NULL DEFAULT '',
`embed` text NOT NULL,
`description` text NOT NULL,
`paysite` int(11) NOT NULL DEFAULT '0',
`keywords` varchar(255) NOT NULL,
`model` varchar(255) NOT NULL DEFAULT '',
`scheduled_date` date NOT NULL DEFAULT '0000-00-00',
`date_added` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`encoded_date` datetime NOT NULL,
`rating` int(5) NOT NULL DEFAULT '0',
`length` int(11) NOT NULL DEFAULT '0',
`submitter` int(11) NOT NULL DEFAULT '0',
`ip` varchar(15) NOT NULL,
`approved` int(11) NOT NULL DEFAULT '0',
`hotlinked` varchar(1024) NOT NULL,
`plug_url` varchar(255) NOT NULL,
`enabled` int(11) NOT NULL DEFAULT '0',
`main_thumb` int(11) NOT NULL DEFAULT '3',
`xml` varchar(32) NOT NULL,
`photos` int(11) NOT NULL DEFAULT '0',
`mobile` varchar(255) NOT NULL,
`modeltmp` varchar(255) NOT NULL,
`movie_width` int(11) NOT NULL,
`movie_height` int(11) NOT NULL,
`token` varchar(255) DEFAULT NULL,
`source_thumb_url` varchar(255) NOT NULL,
`related` varchar(1024) NOT NULL,
`force_related` varchar(255) NOT NULL,
`record_num` int(11) NOT NULL AUTO_INCREMENT,
`webvtt_src` text NOT NULL,
`category_thumb` int(11) NOT NULL,
`related_date` date NOT NULL,
`publish_ready` tinyint(1) NOT NULL,
PRIMARY KEY (`record_num`),
KEY `encoded_date` (`encoded_date`,`photos`,`enabled`),
KEY `filename` (`filename`),
KEY `scheduled_date` (`scheduled_date`),
KEY `enabled_2` (`enabled`,`length`,`photos`),
KEY `enabled` (`enabled`,`encoded_date`,`photos`),
KEY `rating` (`rating`,`enabled`,`photos`),
KEY `token` (`token`),
KEY `submitter` (`submitter`),
FULLTEXT KEY `keywords` (`keywords`,`title`),
FULLTEXT KEY `title` (`title`),
FULLTEXT KEY `description` (`description`),
FULLTEXT KEY `keywords_2` (`keywords`)
) ENGINE=MyISAM AUTO_INCREMENT=124207 DEFAULT CHARSET=latin1
CREATE TABLE `content_views` (
`views` int(11) NOT NULL,
`content` int(11) NOT NULL,
PRIMARY KEY (`content`),
KEY `views` (`views`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
For this query:
SELECT c.*, cv.views
FROM content c JOIN
content_views cv
ON cv.content = c.record_num
WHERE c.enabled = 1
ORDER BY cv.views;
The best indexes are probably content(enabled, record_num) and content_views(content, views). I am guessing that the performance even with these indexes will be similar to what you have now.

optimize mysql query across multiple tables

I am having some issues optimizing the following query that joins multiple tables.
SELECT count(*) as count, `snapshot_id`, `snapshot_guid`, `image`, `subject`, `name`, `brands`.`facebook`, `brands`.`brand_id`, `brand_guid`, `date_sent`
FROM (`snapshots`)
INNER JOIN `brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE `snapshots`.`status` = 1
AND `brands`.`status` = 1
AND `brands`.`archive` = 0
GROUP BY `snapshots`.`brand_id`, `snapshots`.`subject`
ORDER BY `date_sent` desc
LIMIT 20
Execution Time: 4.9 seconds
Using EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE brands ALL PRIMARY,brand_id,status,brand_status NULL NULL NULL 338 Using where; Using temporary; Using filesort
1 SIMPLE snapshots ref brand_id,status,snapshot_brand_status snapshot_brand_status 5 mockd_catalog.brands.brand_id,const 166
Describe Tables for Brands and Snapshots:
Describe brands
Field Type Null Key Default Extra
brand_id int(11) NO PRI NULL auto_increment
brand_guid char(12) NO MUL NULL
friendly varchar(128) YES NULL
name varchar(128) NO NULL
url varchar(2048) YES NULL
logo text YES NULL
cover text YES NULL
facebook varchar(2048) YES NULL
address_1 varchar(128) YES NULL
address_2 varchar(128) YES NULL
city varchar(50) YES NULL
state varchar(50) YES NULL
postal varchar(20) YES NULL
country_code varchar(128) YES NULL
date_created datetime YES NULL
date_modified datetime YES NULL
hp_snapshot tinyint(1) NO 0
status tinyint(1) YES MUL 0
archive tinyint(1) NO 0
Describe snapshots
Field Type Null Key Default Extra
snapshot_id int(11) NO PRI NULL auto_increment
snapshot_guid char(36) YES MUL NULL
brand_id int(11) NO MUL 0
email varchar(256) NO MUL NULL
seed_email varchar(256) NO NULL
date_sent datetime NO MUL NULL
date_created datetime NO NULL
date_modified datetime YES NULL
content_type varchar(10) YES NULL
subject varchar(256) NO NULL
source longtext YES NULL
html longtext NO NULL
html_error text YES NULL
thumbnail text YES NULL
image text YES NULL
status tinyint(1) NO MUL 0
archive tinyint(1) NO 0
tags text YES NULL
I've tried everything that I can think of, and can't seem to further optimize this query. Any help is appreciated.
Rick
edit 3/22/2015 # 10:27am ET:
CREATE TABLE `snapshots` (
`snapshot_id` int(11) NOT NULL AUTO_INCREMENT,
`snapshot_guid` char(36) DEFAULT NULL,
`brand_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(256) NOT NULL,
`seed_email` varchar(256) NOT NULL,
`date_sent` datetime NOT NULL,
`date_created` datetime NOT NULL,
`date_modified` datetime DEFAULT NULL,
`content_type` varchar(10) DEFAULT NULL,
`subject` varchar(256) NOT NULL,
`source` longtext,
`html` longtext NOT NULL,
`html_error` text,
`thumbnail` text,
`image` text,
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '-1= error, 0 = new, 1 = approved, 2 = review ',
`archive` tinyint(1) NOT NULL DEFAULT '0',
`tags` text,
PRIMARY KEY (`snapshot_id`),
KEY `snapshot_id` (`snapshot_id`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `email` (`email`(255)),
KEY `status` (`status`),
KEY `snapshot_guid` (`snapshot_guid`) USING BTREE,
KEY `subject` (`subject`(255)),
KEY `archive` (`archive`),
KEY `archive_status` (`archive`,`status`),
KEY `date_sent` (`date_sent`) USING BTREE,
KEY `recent_snapshots` (`snapshot_id`,`snapshot_guid`,`archive`,`status`,`brand_id`,`date_sent`,`subject`(255)) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=95002 DEFAULT CHARSET=utf8;
Brands Table:
CREATE TABLE `brands` (
`brand_id` int(11) NOT NULL AUTO_INCREMENT,
`brand_guid` char(12) NOT NULL,
`friendly` varchar(128) DEFAULT NULL,
`name` varchar(128) NOT NULL,
`url` varchar(2048) DEFAULT NULL,
`logo` text,
`cover` text,
`facebook` varchar(2048) DEFAULT NULL,
`address_1` varchar(128) DEFAULT NULL,
`address_2` varchar(128) DEFAULT NULL,
`city` varchar(50) DEFAULT NULL,
`state` varchar(50) DEFAULT NULL,
`postal` varchar(20) DEFAULT NULL,
`country_code` varchar(128) DEFAULT NULL,
`date_created` datetime DEFAULT NULL,
`date_modified` datetime DEFAULT NULL,
`hp_snapshot` tinyint(1) NOT NULL DEFAULT '0',
`status` tinyint(1) DEFAULT '0',
`archive` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`brand_id`),
KEY `brand_guid` (`brand_guid`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `status` (`status`) USING BTREE,
KEY `archive_status` (`archive`,`status`),
KEY `archive` (`archive`)
) ENGINE=InnoDB AUTO_INCREMENT=423 DEFAULT CHARSET=utf8;
The select statement:
SELECT
COUNT(*) AS count,
`snapshot_id`,
`snapshot_guid`,
`image`,
`subject`,
`name`,
`brands`.`facebook`,
`brands`.`brand_id`,
`brand_guid`,
`date_sent`
FROM
(`snapshots`)
INNER JOIN
`brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE
`snapshots`.`archive` = 0
AND `snapshots`.`status` = 1
AND `brands`.`archive` = 0
AND `brands`.`status` = 1
GROUP BY `snapshots`.`brand_id` , `snapshots`.`subject`
ORDER BY `date_sent` DESC
LIMIT 20;
Explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE snapshots ref brand_id,status,archive,archive_status status 1 const 48304 Using where; Using temporary; Using filesort
1 SIMPLE brands eq_ref PRIMARY,brand_id,status,archive_status,archive PRIMARY 4 mockd_catalog.snapshots.brand_id 1 Using where
Ther are several problems :
The key on date_send is defined using hash while you are using it in order by date_sent.
You have a where clause with brand.status and brand.archive. archive is only in a combined key brand_id, status, archive, but it cannot be used because the first column (brand_id) is not used in the where clause. Create an index for archive only, or a composite (status, archive).
subject needs an index on its own. Currently its index is buried deep down in a composite index.
As a general rule the order of the columns in a composite indexes important. The index is only useful if the used columns are the first ones. Also they are less effective the wider the first column is. This means that
KEY `snap_indx` (`snapshot_id`,`snapshot_guid`,`email`(255),`date_sent`,`subject`(255),`status`,`archive`)
is inefficient because subject with its 255 bytes comes before status and archive which have only 4 bytes each.

SQL multiple selecting from two tables and group

i have problem with select.
I managed to write a query that was doing something similar but I was able to pull data after only one I need to group as a team but with two names
I have a problem to write one question, I need to pick something about this style
team_id | player1 | player2 | position | category
1 | John Newman | Andy Roddick | 1 | 1
2 | Roger Federer | Rafael Nadal | 1 | 1
I have two tables/ one contains players and second contains attendance in team/
CREATE TABLE IF NOT EXISTS `atendance` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`player1` int(11) NOT NULL,
`player2` int(11) NOT NULL,
`position` int(11) NOT NULL,
`category` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=22 ;
CREATE TABLE IF NOT EXISTS `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(12) COLLATE utf8_czech_ci NOT NULL,
`surname` varchar(24) COLLATE utf8_czech_ci NOT NULL,
`datum_n` date NOT NULL,
`klub` varchar(60) COLLATE utf8_czech_ci NOT NULL,
`tel` varchar(12) COLLATE utf8_czech_ci NOT NULL,
`mail` varchar(32) COLLATE utf8_czech_ci NOT NULL,
`cas` text COLLATE utf8_czech_ci NOT NULL,
`foto` text COLLATE utf8_czech_ci NOT NULL,
`pass` text COLLATE utf8_czech_ci NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '0',
`valid_from` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`valid_until` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
`pohlavie` varchar(6) COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=25 ;
I think you need to use JOIN particularly LEFT JOIN, like:
SELECT t.id, p1.name, p2.name, t.position, t.category
FROM atendance t
LEFT JOIN players p1
ON t.player1 = p1.id
LEFT JOIN players p2
ON t.player2 = p2.id
As much as i understood, all the required data is in first table. You just need to fire,
SELECT ID, PLAYER1, PLAYER2, POSITION, CATEGORY FROM ATENDANCE
Please note, instead of CREATE commands provide the output result of that table, as you want SELECT statement to be built. It makes easier to understand.

mysql query with like and group by not using index

I have a problem with a query not using the right index or any index at all.
Here is my TABLE CREATE
CREATE TABLE `data` (
`data_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`other_id` int(10) unsigned NOT NULL,
`author_text` varchar(255) DEFAULT NULL,
`sometext` text NOT NULL,
`somehash` char(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`longurl` varchar(255) NOT NULL,
`published` int(11) NOT NULL,
`language` varchar(3) DEFAULT NULL,
`level` enum('-1','0','1','2') CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT '2',
`day` int(11) NOT NULL,
`processed` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`data_id`),
UNIQUE KEY `unique_url` (`other_id`,`somehash`),
KEY `processed` (`processed`),
KEY `language` (`day`,`language`)
) ENGINE=MyISAM AUTO_INCREMENT=2576573 DEFAULT CHARSET=utf8
The QUERY
SELECT COUNT( * ) AS `num` , `data`.`language` AS `language`
FROM `data`
WHERE `data`.`author_text` LIKE '%test%'
AND `data`.`day`
BETWEEN '1364342400'
AND '1372118399'
GROUP BY `data`.`language`
ORDER BY `data`.`language` ASC
An EXPLAIN
id|select_type|table|type|possible_keys|key |key_len|ref |rows |Extra
--|-----------|-----|----|-------------|----|-------|----|-------|--------------------------------------------
1 |SIMPLE |data |ALL |language |NULL|NULL |NULL|2563731|Using where; Using temporary; Using filesort