Moodle Join Query Slow - mysql

I've been at this thing for hours. Does anyone have any ideas on how to speed this query up?
SELECT SQL_CALC_FOUND_ROWS
mdl_course.fullname as course_name,
mdl_course.idnumber as course_length,
mdl_user.firstname as firstname,
mdl_user.lastname as lastname,
case when mdl_course_completions.timecompleted IS NOT NULL
then FROM_UNIXTIME(mdl_course_completions.timecompleted,'%m/%d/%Y') else 'N/A' end AS date_completed,
mdl_cohort.name AS group_name,
mdl_course_categories.name as category_name,
case when mdl_course_completions.timecompleted IS NOT NULL then 'Completed' else
'Incomplete' end AS completion_status
FROM mdl_enrol
JOIN mdl_user_enrolments ON mdl_user_enrolments.enrolid = mdl_enrol.id
JOIN mdl_course ON mdl_course.id = mdl_enrol.courseid
JOIN mdl_user ON mdl_user.id = mdl_user_enrolments.userid AND mdl_user.deleted = 0
JOIN mdl_cohort_members ON mdl_cohort_members.userid = mdl_user.id
JOIN mdl_cohort ON mdl_cohort.id = mdl_cohort_members.cohortid
JOIN mdl_course_categories ON mdl_course_categories.id = mdl_course.category
JOIN mdl_course_completions ON mdl_course_completions.course = mdl_course.id
AND mdl_course_completions.userid = mdl_user.id
AND mdl_course_completions.deleted IS NULL
GROUP BY mdl_course_completions.id
ORDER BY mdl_course.fullname ASC
mdl_course_completions has over 100,000 rows in it and is slowing everything down.

Related

mysql creating sort index making queries slow

We have a view table the query for the view is pasted below. The problem is that whenever we try to query items view our database start making sorting index which make the over all time of our response very slow.
CREATE VIEW items_view AS
SELECT item_detail.*, item.name, item.description, item.thumbnail_url, item.large_image_url, item.ios_url, item.android_url, item.vr_url, item.vr_updated_url, item.webgl_url, item.video_url, item.quest_url,
item.is_transferable, item.ip_id, item.brand_id, item.edition_id, item.currency_id, item.category_id, item.rarity_id, item.type_id, item.asset_id, item.artist_id, item.art_collection_id,
IFNULL(item.variation_id, 0) AS variation_id,
item.series_id, item.set_id, item.is_tradable, item.number_minted, ip.name AS ip_name, brand.name As brand_name, brand.value AS brand_image,
edition.name AS edition_name, type.name As type_name, artist.name AS artist_name, art_collection.name AS art_collection_name,
IFNULL(variation.name, '') As variation_name, variation.value AS variation_value, category.name As category_name, asset.name AS asset_name, asset.value AS asset_image,
rarity.name As rarity_name, currency.symbol, currency.code AS currency_code, currency.name AS currency_name, series.name AS series_name, sets.name AS set_name,
CONCAT(users.first_name, ' ' ,users.last_name) as owner_name, users.username AS owner_username, users.is_public_inventory, users.role, users.pub_key,
auctions.id as auction_id, auctions.expire_at as auction_expire_at, max(ab.bid_amount) AS highest_bid_amount, IF(COUNT(auctions.id) > 0 AND !auctions.is_completed AND !auctions.is_deleted
, true, false) AS is_auction, auctions.start_amount AS auction_start_amount, auctions.reserved_amount AS auction_reserved_amount, auctions.buy_now_amount AS auction_buy_now_amount,
auctions.created_at as auction_created_at, auctions.status AS auction_status, auctions.is_extended AS auction_extended, TIMESTAMPDIFF(SECOND, now(), auctions.expire_at) AS auction_expire_seconds FROM item
LEFT JOIN base_lookup AS ip ON item.ip_id = ip.id
LEFT JOIN base_lookup AS brand ON item.brand_id = brand.id
LEFT JOIN base_lookup AS edition ON item.edition_id = edition.id
LEFT JOIN base_lookup AS category ON item.category_id = category.id
LEFT JOIN base_lookup AS type ON item.type_id = type.id
LEFT JOIN base_lookup AS variation ON item.variation_id = variation.id
LEFT JOIN base_lookup AS rarity ON item.rarity_id = rarity.id
LEFT JOIN base_lookup AS series ON item.series_id = series.id
LEFT JOIN base_lookup AS sets ON item.set_id = sets.id
LEFT JOIN base_lookup AS asset ON item.asset_id = asset.id
LEFT JOIN base_lookup AS artist ON item.artist_id = artist.id
LEFT JOIN base_lookup AS art_collection ON item.art_collection_id = art_collection.id
INNER JOIN item_detail ON item.id = item_detail.parent_id
INNER JOIN users ON users.id = item_detail.owner_id
LEFT JOIN currency ON currency.id = item.currency_id
LEFT JOIN auctions on (item_detail.id = auctions.item_detail_id AND auctions.is_deleted = 0 AND auctions.is_completed = 0)
LEFT JOIN auction_bidding ab on auctions.id = ab.auction_id
WHERE item_detail.is_active = 1
group by item_detail.id
order by item_detail.id;
One of the query we are doing on this items_view that is stuck in making sorting index
SELECT
`id`, `code`, `name`, `large_image_url`,
`thumbnail_url`, `medium_thumbnail_url`, `symbol`, `ip_name`, `owner_id`,
`auction_id`, `is_auction`, `highest_bid_amount`, `auction_expire_at`,
`auction_start_amount`, `enable_sale`, `owner_name`, `role`, `amount`,
`auction_expire_seconds`, `rarity_name`, `ip_content`, `asset_content`
FROM `items_view` AS `items_view`
WHERE `items_view`.`transaction_hash` IS NOT NULL AND
`items_view`.`lobby_name` = 'live' AND
`items_view`.`is_tradable` = 1 AND
`items_view`.`is_transferring` = 0 AND
`items_view`.`is_locked` = 0 AND
`items_view`.`enable_sale` = true AND
`items_view`.`is_auction` = 0
ORDER BY `items_view`.`item_order` ASC LIMIT 24, 24;
can any give any suggest what are we doing wrong here!
Edited:
link to explain run against the above query
link to csv

invoice type fetch query from tables

ER diagram
ER diagram is attached
I need to get sales invoice invoices.type = 'sales' and invoices.type = 'purchase'
with company name as you can see in select statement
also sales and purchase columns
My query
SELECT jobs.id as jobID,
-- c.name AS Customer,jobs.job_no,
invoices.invoice_due_date,
jobs.id,
( (invoice_items.amount * invoice_items.quantity * invoice_items.exchange_rate )) as Ammount
FROM invoices
INNER JOIN `invoice_items` ON invoices.id = invoice_items.invoice_id
INNER JOIN `jobs` ON jobs.id = invoices.job_id
-- INNER JOIN `company` as c ON c.company_id = jobs.company_id -- jobs and company.
WHERE invoices.type = 'sales'
-- new conditions
AND MONTH(jobs.created_at) = '3'
AND YEAR(jobs.created_at) = '2017'
AND jobs.status > 0
AND jobs.complete_job > 0
AND jobs.completed_at IS NOT NULL
AND invoice_items.deleted_at IS NULL
GROUP BY jobs.job_no
ORDER BY invoices.invoice_due_date DESC
Try this updated...
SELECT jobs.id as jobID,
C.name AS Customer,jobs.job_no,
invoices.invoice_due_date,
jobs.id,
( (invoice_items.amount * invoice_items.quantity *invoice_items.exchange_rate )) as Ammount,
CASE invoices.type
WHEN 'sales' THEN 'S'
ELSE 'P'
END as trans_type
FROM invoices
INNER JOIN `invoice_items` ON invoices.id = invoice_items.invoice_id
INNER JOIN `jobs` ON jobs.id = invoices.job_id
LEFT OUTER JOIN `company` as C ON C.company_id = jobs.company_id
WHERE (invoices.type = 'sales' OR invoices.type = 'purchase') AND (MONTH(jobs.created_at) = '3') AND (YEAR(jobs.created_at) = '2017') AND (jobs.status > 0) AND (jobs.complete_job > 0) AND (jobs.completed_at IS NOT NULL)

Is there any alternate way to select the values in this case statement rather than subqueries

I know some degree of MySQL, but I'm not sure if there's a more correct way to do what I'm doing here.
#Select Ticket ID, Ticket Title, Assigned To
SELECT * FROM
(SELECT
ti.number, ti.ticket_id, foev.value AS "ticket_name",
CASE WHEN ti.staff_id = 0 AND ti.team_id = 0
then
"** Unassigned **"
ELSE
CASE when ti.staff_id = 0
THEN
(SELECT te.name FROM ost_team te WHERE te.team_id = ti.team_id)
ELSE
(SELECT CONCAT(st.firstname,LEFT(st.lastname,1)) FROM ost_staff st WHERE st.staff_id = ti.staff_id)
END
END AS "assigned_to"
FROM ost_ticket ti
LEFT JOIN ost_form_entry foe ON foe.object_id = ti.ticket_id AND foe.object_type = "T"
LEFT JOIN ost_form_entry_values foev ON foev.entry_id = foe.id
GROUP BY ti.ticket_id)
AS ticket_meta_list
ORDER BY ticket_id ASC;
Is there a more correct way to do those subqueries? I'm thinking there may be with joins that I'm less familiar with.
If your staff table has no staff_id with value 0, and your team table has no team_id with value 0, you can use left join in combination with coalesce:
SELECT ti.number,
ti.ticket_id,
foev.value AS ticket_name,
COALESCE(CONCAT(st.firstname,LEFT(st.lastname,1)),
te.name, '** Unassigned **') AS assigned_to
FROM ost_ticket ti
LEFT JOIN ost_form_entry foe
ON foe.object_id = ti.ticket_id
AND foe.object_type = 'T'
LEFT JOIN ost_form_entry_values foev
ON foev.entry_id = foe.id
LEFT JOIN ost_team te
ON te.team_id = ti.team_id
LEFT JOIN ost_staff st
ON st.staff_id = ti.staff_id
GROUP BY ti.ticket_id
ORDER BY ticket_id

MySQL - I want my table JOIN to return a single row based on a condition

in the below code there are multiple entries in 'leads' table with the same 'account_id'. I want it to return a single row - the one with the minimal value of another field 'date_entered'. I cannot use 'group by' on account_id as I intend to use 'group by' on BU and get summation accordingly. Please help.
select uc.business_unit_dp_c,
FORMAT(SUM(CASE
WHEN lc.source_leads_c not in ('Discovery','Discovery SuperEmail','Self Generated','Partner','Channel_Partner') and k.id<>'' THEN k.order_value
WHEN lc.source_leads_c not in ('Discovery','Discovery SuperEmail','Self Generated','Partner','Channel_Partner') and s.id<>'' THEN s.sivr_aiv_inr
ELSE 0
END),0)
as Online,
FORMAT(SUM(CASE
WHEN lc.source_leads_c in ('Discovery', 'Discovery SuperEmail') and k.id<>'' THEN k.order_value
WHEN lc.source_leads_c in ('Discovery', 'Discovery SuperEmail') and s.id<>'' THEN s.sivr_aiv_inr
ELSE 0
END),0)
as Discovery,
FORMAT(SUM(CASE
WHEN lc.source_leads_c in ('Partner','Channel_Partner') and k.id<>'' THEN k.order_value
WHEN lc.source_leads_c in ('Partner','Channel_Partner') and s.id<>'' THEN s.sivr_aiv_inr
ELSE 0
END),0)
as Self_Generated_CP
from opportunities as o
left join opportunities_cstm as oc on o.id=oc.id_c
left join opportunities_knw_caf_1_c as ok on o.id=ok.opportunities_knw_caf_1opportunities_ida
left join knw_caf as k on ok.opportunities_knw_caf_1knw_caf_idb=k.id
left join opportunities_knw_sivr_caf_1_c as os on os.opportunities_knw_sivr_caf_1opportunities_ida=o.id
left join knw_sivr_caf as s on s.id=os.opportunities_knw_sivr_caf_1knw_sivr_caf_idb
left join accounts_opportunities as ao on ao.opportunity_id=o.id
left join leads as l on l.account_id=ao.account_id and l.account_id <> ''
left join leads_cstm as lc on lc.id_c=l.id
left join users_cstm as uc on uc.id_c=o.assigned_user_id
where o.sales_stage='clw' and
(k.id<>'' or s.id<>'') and o.jira_raise_date <> '' and
(o.tranjection_type in ('Fresh Plan / New Customer','Number Activation','Revival','Balance Amount') or o.transaction_sivr in ('Paid Project','Number Allocation','New Feature')) and
o.jira_raise_date between '2016-06-01' and curdate()
group by uc.business_unit_dp_c
Write SQL just as you described
Select *
from from opportunities o
left join opportunities_cstm oc
on o.id = oc.id_c
left join opportunities_knw_caf_1_c ok
on o.id = ok.opportunities_knw_caf_1opportunities_ida
left join knw_caf k
on ok.opportunities_knw_caf_1knw_caf_idb = k.id
left join opportunities_knw_sivr_caf_1_c os
on os.opportunities_knw_sivr_caf_1opportunities_ida=o.id
left join knw_sivr_caf s
on s.id = os.opportunities_knw_sivr_caf_1knw_sivr_caf_idb
left join accounts_opportunities ao
on ao.opportunity_id=o.id
left join leads l
on l.account_id=ao.account_id
and l.account_id <> ''
left join leads_cstm lc
on lc.id_c = l.id
left join users_cstm uc
on uc.id_c = o.assigned_user_id
where o.sales_stage = 'clw' and
and (k.id <> '' or s.id <> '')
and o.jira_raise_date <> ''
and (o.tranjection_type in
('Fresh Plan / New Customer',
'Number Activation','Revival','Balance Amount') or
o.transaction_sivr in
('Paid Project','Number Allocation','New Feature'))
and o.jira_raise_date between '2016-06-01' and curdate()
-- next, add this additional predicate to Where clause...
use table w/DateEntered column
and date_entered =
(Select Min(date_entered)
From accounts_opportunities os
join tableWithDateEntered dr -- Table w/DateEntered
on ????? -- proper join criteria here
Where os.account_id = l.account_id)
--- or as constructed by op ( and simplified by me, since both account_id and date_entered are in table leads, that's the only table that needs to be referenced in the subquery).....
and l.date_entered =
(select min(date_entered)
from leads
where account_id = l.account_id)
select min(C.date),C.Customer_Code from (
select InvoiceNo,month(InvoiceDate) as date,Customer_Code,Createddate from tbl_Invoices A Inner Join tbl_customer B on A.customer_Code=B.CustomerCode
where YEAR(InvoiceDate)='2017'
and CustomerCode not in (select CustomerCode from tbl_customer where year(createddate) in (year(getdate())))
and CustomerCode not in (select customer_Code from tbl_Invoices where year(InvoiceDate) in (year(getdate())-1))
and CustomerCode in (select customer_Code from tbl_Invoices where year(InvoiceDate) not in (year(getdate())))
--group by Customer_Code,Createddate,InvoiceNo
)C group by C.Customer_Code

Optimising Multiple Joins to the same table MySQL

I have a query which joins in the same table (user_status) multiple times in order to pull back different status information. The overall goal of this query is to turn it in to a view. I believe that these joins are causing latency in the query run time, which is causing the query to hang. I am trying to optimise the query as much as possible, but don't know of another way of doing this except to have all of these joins. The joins are left joins as I want to see null values being returned aswell. The where clause filter is to return data for a timeline of up to 370 days ago. I will paste my query below; any suggestions would be really appreciated:
SELECT
c.id AS customer_id,
c.priority AS priority,
c.create_date AS create_date,
u.login AS login,
c.due_date AS due_date,
ts.user_status AS status,
cts.status AS current_status,
cts.id AS status_id,
w.name AS workflow_name,
c.summary AS summary,
cfi.added_by AS acknowledged_by,
CONCAT(dispatch_user.first_name,' ',dispatch_user.last_name) AS dispatch_user,
CONCAT(dispatched_user.first_name,' ',dispatched_user.last_name) AS acknowledged,
CONCAT(last_update_by.first_name,' ',last_update_by.last_name) AS manager_last_updated_by,
c.updated_date AS updated_date,
CONCAT(u.first_name,' ',u.last_name) AS last_updated_by,
ts_new.status_date AS new_dt,
ts_initial.status_date AS initial_dt,
ts_assigned.status_date AS assigned_dt,
ts_investigating.status_date AS investigating_dt,
ts_resolved.status_date AS resolved_dt,
ts_pending_closure.status_date AS pending_closure_dt,
ts_closed.status_date AS closed_dt,
IF(user_visit.status IS NOT NULL, 'Yes', 'No') AS user_visit,
fftr.reason AS reason,
fftr.notes AS resolution,
FROM customer c
JOIN form1 AS tcv ON (c.id = tcv.customer_id)
JOIN user_status AS cts ON (c.current_status_id = cts.id)
JOIN user AS u ON (u.id = cts.status_by_id)
LEFT JOIN user_status AS ts ON (ts.workflow_state = cts.status)
LEFT JOIN change_info AS cfi ON cfi.customer_id = c.id
LEFT JOIN user_status AS dispatch_status ON dispatch_status.customer_id = c.id AND dispatch_status.status = 'FTD'
LEFT JOIN user AS dispatch_user ON dispatch_user.id = dispatch_status.status_by_id
LEFT JOIN user_status AS assigned_ft_dis ON assigned_ft_dis.customer_id = c.id AND assigned_ft_dis.status = 'ASSIGNED - FTD'
LEFT JOIN user_status AS assigned_ft_dis_rx ON assigned_ft_dis_rx.customer_id = c.id AND assigned_ft_dis_rx.status = 'ASSIGNED - FT'
LEFT JOIN user AS dispatched_user ON dispatched_user.id = assigned_ft_dis_rx.status_by_id
LEFT JOIN user_status AS last_update_status ON last_update_status.id = c.current_status_id
LEFT JOIN user last_update_by ON last_update_by.id = last_update_status.status_by_id
LEFT JOIN user_status AS ts_new ON c.id = ts_new.customer_id AND ts_new.status LIKE 'NEW%'
LEFT JOIN user_status AS ts_initial ON c.id = ts_initial_diagnosis.customer_id AND ts_initial_diagnosis.status LIKE 'INITIAL SOLUTION%'
LEFT JOIN user_status AS ts_assigned ON c.id = ts_assigned.customer_id AND ts_assigned.status LIKE 'ASSIGNED%'
LEFT JOIN user_status AS ts_investigating ON c.id = ts_investigating.customer_id AND ts_investigating.status LIKE 'INVESTIGATING%'
LEFT JOIN user_status AS ts_resolved ON c.id = ts_resolved.customer_id AND ts_resolved.status LIKE 'RESOLVED%'
LEFT JOIN user_status AS ts_pending_closure ON c.id = ts_pending_closure.customer_id AND ts_pending_closure.status LIKE 'PENDING CLOSURE%'
LEFT JOIN user_status AS ts_closed ON c.id = ts_closed.customer_id AND ts_closed.status LIKE 'CLOSED%'
LEFT JOIN user_status AS user_visit ON c.id = user_visit.customer_id AND user_visit.status = 'RESOLVED – FT'
LEFT JOIN change AS tc_fftr ON c.id = tc_fftr.customer_id AND tc_fftr.contents LIKE 'Resolution%'
LEFT JOIN form4 AS fftr ON fftr.change_id = tc_fftr.id
WHERE
c.create_date >= DATE_SUB(NOW(),INTERVAL 370 DAY)
First, the query as listed would not have run as you don't have a "w" alias, and have a trailing comma after the last field. I have rewritten (slightly) for readability and relationships to each other and changed a few "aliases".
SELECT
c.id as customer_id,
c.priority as priority,
c.create_date as create_date,
u.login as login,
c.due_date as due_date,
ts.user_status as status,
cts.status as current_status,
cts.id as status_id,
w.name as workflow_name,
c.summary as summary,
cfi.added_by as acknowledged_by,
concat(du.first_name,' ',du.last_name) as dispatch_user,
concat(disU.first_name,' ',disU.last_name) as acknowledged,
concat(updBy.first_name,' ',updBy.last_name) as manager_last_updated_by,
c.updated_date as updated_date,
concat(u.first_name,' ',u.last_name) as last_updated_by,
ts_new.status_date as new_dt,
tsi.status_date as initial_dt,
tsa.status_date as assigned_dt,
tsinv.status_date as investigating_dt,
tsres.status_date as resolved_dt,
tspc.status_date as pending_closure_dt,
ts_closed.status_date as closed_dt,
IF(uv.status IS NOT NULL, 'Yes', 'No') as user_visit,
fftr.reason as reason,
fftr.notes as resolution
FROM
customer c
join form1 tcv
on (c.id = tcv.customer_id)
join user_status cts
on c.current_status_id = cts.id
join user u
on cts.status_by_id = u.id
left join user_status ts
on cts.status = ts.workflow_state
left join change_info cfi
on c.id = cfi.customer_id
left join user_status ds
on c.id = ds.customer_id
and ds.status = 'FTD'
left join user du
on ds.status_by_id = du.id
left join user_status ftDis
on c.id = ftDis.customer_id
and ftDis.status = 'ASSIGNED - FTD'
left join user_status rx
on c.id = rx.customer_id
and rx.status = 'ASSIGNED - FT'
left join user disU
on rx.status_by_id = disU.id
left join user_status lus
ON c.current_status_id = lus.id
left join user updBy
on lus.status_by_id = updBy.id
left join user_status ts_new
on c.id = ts_new.customer_id
and ts_new.status like 'NEW%'
left join user_status tsi
on c.id = tsi.customer_id
and tsi.status like 'INITIAL SOLUTION%'
left join user_status tsa
on c.id = tsa.customer_id
and tsa.status like 'ASSIGNED%'
left join user_status tsinv
on c.id = tsinv.customer_id
and tsinv.status like 'INVESTIGATING%'
left join user_status tsres
on c.id = tsres.customer_id
and tsres.status like 'RESOLVED%'
left join user_status tspc
on c.id = tspc.customer_id
and tspc.status like 'PENDING CLOSURE%'
left join user_status ts_closed
on c.id = ts_closed.customer_id
and ts_closed.status like 'CLOSED%'
left join user_status uv
on c.id = uv.customer_id
and uv.status = 'RESOLVED – FT'
left join change tc_fftr
ON c.id = tc_fftr.customer_id
and tc_fftr.contents LIKE 'Resolution%'
left join form4 fftr
ON tc_fftr.id = fftr.change_id
WHERE
c.create_date >= DATE_SUB(NOW(),INTERVAL 370 DAY)
GROUP BY
customer_id
I would ensure the following compound indexes on your tables...
table index
customer (create_date, id)
form1 (customer_id)
change_info (customer_id, added_by)
user_status (id, status_by_id, status )
user (id, first_name, last_name)
user_status (customer_id, status, status_date, status_by_id )
change (customer_id, contents, id )
form4 (change_id)
Also, it appears you were not even using the assigned_ft_dis (changed alias to ftDis)
then, in your join after the "form1" of...
join user_status cts
on c.current_status_id = cts.id
join user u
on cts.status_by_id = u.id
left join user_status ts
on cts.status = ts.workflow_state
why are you re-joining based on the cts.status to the user_status table again by alias "ts". IF you have 10k records with status "NEW", you are left-joining to all of those records for the workflow_state. You have nothing associated to the customer ID. But since you are prior in the chain of joins, joined by the current_status_id (which I can only GUESS is the auto-increment ID of the user_status table). Is this a mistake? Are you trying to get some description for the "workflow_state". I believe this one component is a big hit on your performance. Is there a different TABLE (alias "w" for the workflow_state) NOT accurately reflected here?
Now, a possible rewrite. Do a single join to the user status and user table to determine the respective status elements and by whom and doing case/when for the criteria... something like... I am using "US" for User_Status alias and "U2" for generic "User" reference for each corresponding "user status" entry. Now, notice all the case/when instances. Since I am only joining to the status and then user table without specific criteria, I can apply the criteria at the FIELD condition level and either be null, or applicable and grab both the date and who of the action.
SELECT
c.id as customer_id,
c.priority as priority,
c.create_date as create_date,
u.login as login,
c.due_date as due_date,
ts.user_status as status,
cts.status as current_status,
cts.id as status_id,
w.name as workflow_name,
c.summary as summary,
MAX( case when US.status = 'FTD' then US.Status_Date END ) as dispatch_date,
MAX( case when US.status = 'FTD' then concat(U2.first_name,' ',U2.last_name) END ) as dispatch_user,
MAX( case when US.status = 'ASSIGNED - FTD' then US.Status_Date END ) as assigned_date,
MAX( case when US.status = 'ASSIGNED - FTD' then concat(U2.first_name,' ',U2.last_name) END ) as assigned_user,
MAX( case when US.status = 'ASSIGNED - FT' then US.Status_Date END ) as assigned2_date,
MAX( case when US.status = 'ASSIGNED - FT' then concat(U2.first_name,' ',U2.last_name) END ) as assigned2_user,
MAX( case when US.status like 'NEW%' then US.Status_Date END ) as new_date,
MAX( case when US.status like 'NEW%' then concat(U2.first_name,' ',U2.last_name) END ) as new_user,
MAX( case when US.status like 'INITIAL SOLUTION%' then US.Status_Date END ) as solution_date,
MAX( case when US.status like 'INITIAL SOLUTION%' then concat(U2.first_name,' ',U2.last_name) END ) as solution_user,
MAX( case when US.status like 'ASSIGNED%' then US.Status_Date END ) as assigned_date,
MAX( case when US.status like 'ASSIGNED%' then concat(U2.first_name,' ',U2.last_name) END ) as assigned_user,
MAX( case when US.status like 'INVESTIGATING%' then US.Status_Date END ) as investigate_date,
MAX( case when US.status like 'INVESTIGATING%' then concat(U2.first_name,' ',U2.last_name) END ) as investigate_user,
MAX( case when US.status like 'RESOLVED%' then US.Status_Date END ) as resolved_date,
MAX( case when US.status like 'RESOLVED%' then concat(U2.first_name,' ',U2.last_name) END ) as resolved_user,
MAX( case when US.status like 'PENDING CLOSURE%' then US.Status_Date END ) as pendClose_date,
MAX( case when US.status like 'PENDING CLOSURE%' then concat(U2.first_name,' ',U2.last_name) END ) as pendClose_user,
MAX( case when US.status like 'CLOSED%' then US.Status_Date END ) as closed_date,
MAX( case when US.status like 'CLOSED%' then concat(U2.first_name,' ',U2.last_name) END ) as closed_user,
MAX( case when US.status = 'RESOLVED - FT' then US.Status_Date END ) as resolved_date,
MAX( case when US.status = 'RESOLVED - FT' then concat(U2.first_name,' ',U2.last_name) END ) as resolved_user,
c.updated_date as updated_date,
concat(u.first_name,' ',u.last_name) as last_updated_by,
IF(uv.status IS NOT NULL, 'Yes', 'No') as user_visit,
fftr.reason as reason,
fftr.notes as resolution
FROM
customer c
left join user_status US
on c.id = US.customer_id
left join user U2
on US.status_by_id = U2.id
join form1 tcv
on (c.id = tcv.customer_id)
join user_status cts
on c.current_status_id = cts.id
join user u
on cts.status_by_id = u.id
left join user_status ts
on cts.status = ts.workflow_state
left join change_info cfi
on c.id = cfi.customer_id
left join user_status lus
ON c.current_status_id = lus.id
left join user updBy
on lus.status_by_id = updBy.id
left join change tc_fftr
ON c.id = tc_fftr.customer_id
and tc_fftr.contents LIKE 'Resolution%'
left join form4 fftr
ON tc_fftr.id = fftr.change_id
WHERE
c.create_date >= DATE_SUB(NOW(),INTERVAL 370 DAY)
GROUP BY
customer_id
This still may not work completely, but I am sure MOST of it WILL work and accommodate almost all of what you are trying to get even though I am getting a few more details (pair of both dates and who of the corresponding status actions).