I am running a query against a table and doing a left join to try and get the record from the left table with the most recent date but it's not picking up the other values relevant to the datetime column (user and notes)
SELECT
i.customer_sequence,
i.due_date,
MAX(cn.datetime) as notes_datetime,
cn.user as notes_user,
cn.notes as notes_notes
FROM
billing_invoices i
LEFT JOIN customer_notes cn
ON i.customer_sequence = cn.customer_seq
WHERE
cn.type = 'Accounts' AND
i.customer_sequence <> '0' AND
i.status = 'Unpaid' AND
i.directdebit <> 'Y'
GROUP BY
i.customer_sequence
ORDER BY
i.due_date DESC
Aggregation is not the solution here. You want the entire row from the joined table, so this suggest filtering instead. If you are running MySQL 8.0, I would recommend window functions:
SELECT *
FROM (
SELECT
i.customer_sequence,
i.due_date,
ROW_NUMBER() OVER(PARTITION BY i.customer_sequence ORDER BY cn.datetime DESC) rn,
cn.datetime as notes_datetime,
cn.user as notes_user,
cn.notes as notes_notes
FROM billing_invoices i
LEFT JOIN customer_notes cn
ON i.customer_sequence = cn.customer_seq
AND cn.type = 'Accounts'
WHERE
i.customer_sequence <> '0' AND
i.status = 'Unpaid' AND
i.directdebit <> 'Y'
) t
ORDER BY i.due_date DESC
Note that I moved the condition on the left joined table from the WHERE clause to the ON clause of the join (otherwise, this acts like an inner join).
In earlier versions, one option is a correlated subquery:
SELECT
i.customer_sequence,
i.due_date,
cn.datetime as notes_datetime,
cn.user as notes_user,
cn.notes as notes_notes
FROM billing_invoices i
LEFT JOIN customer_notes cn
ON i.customer_sequence = cn.customer_seq
AND cn.type = 'Accounts'
AND cn.datetime = (
SELECT MAX(cn1.datetime)
FROM customer_notes cn1
WHERE i.customer_sequence = cn1.customer_seq AND cn1.type = 'Accounts'
)
WHERE
i.customer_sequence <> '0' AND
i.status = 'Unpaid' AND
i.directdebit <> 'Y'
Related
I have a functional LEFT JOIN MySQL query structured like this:
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() || HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND RecordType = '1'
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
GROUP BY HTG_TechProps.EmpNumber ORDER BY HTG_TechProps.EmpNumber ASC
I need to add some criteria to the initial SELECT table like this:
HTG_TechProps.EmpStatus='A'
I get a Syntax error when I add a WHERE statement prior to the LEFT JOIN and when I add an AND like this after the LEFT JOIN it is ignored an still returning records that are not equal to A.
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND HTG_TechProps.EmpStatus='A'
Conditions on the first table in a LEFT JOIN should go in a WHERE clause:
SELECT COUNT(sr.ID) AS current_job,
tp.EmpNumber, tp.EmpFirstName, tp.EmpLastName, tp.Veh_Number
FROM HTG_TechProps tp LEFT JOIN
HTG_ScheduleRequest sr
ON tp.EmpNumber = sr.SSR AND
(sr.ScheduleDateCurrent = CURDATE() OR sr.ScheduleDateExact = CURDATE()
) AND
sr.RecordType = '1' AND -- assume this comes from SR
sr.JobStatus IN (2, 5, 8, 3, 4, 7)
WHERE tp.EmpStatus='A'
GROUP BY tp.EmpNumber -- this is okay assuming that it is unique or (equivalently) a primary key
ORDER BY tp.EmpNumber ASC;
Note that this introduces table aliases so the query is easier to write and to read.
You should Use OR
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() OR HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND RecordType = '1'
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
GROUP BY HTG_TechProps.EmpNumber
ORDER BY HTG_TechProps.EmpNumber ASC
and if you want apply where condtion for main table's columns you could use where eg:
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
WHERE (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() OR HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND HTG_TechProps.RecordType = '1'
GROUP BY HTG_TechProps.EmpNumber
ORDER BY HTG_TechProps.EmpNumber ASC
query taking 1 minute to fetch results
SELECT
`jp`.`id`,
`jp`.`title` AS game_title,
`jp`.`game_type`,
`jp`.`state_abb` AS game_state,
`jp`.`location` AS game_city,
`jp`.`zipcode` AS game_zipcode,
`jp`.`modified_on`,
`jp`.`posted_on`,
`jp`.`game_referal_amount`,
`jp`.`games_referal_amount_type`,
`jp`.`status`,
`jp`.`is_flaged`,
`u`.`id` AS employer_id,
`u`.`email` AS employer_email,
`u`.`name` AS employer_name,
`jf`.`name` AS game_function,
`jp`.`game_freeze_status`,
`jp`.`game_statistics`,
`jp`.`ats_value`,
`jp`.`integration_id`,
`u`.`account_manager_id`,
`jp`.`model_game`,
`jp`.`group_id`,
(CASE
WHEN jp.group_id != '0' THEN gm.group_name
ELSE 'NA'
END) AS group_name,
`jp`.`priority_game`,
(CASE
WHEN jp.country != 'US' THEN jp.country_name
ELSE ''
END) AS game_country,
IFNULL((CASE
WHEN
`jp`.`account_manager_id` IS NULL
OR `jp`.`account_manager_id` = 0
THEN
(SELECT
(CASE
WHEN
account_manager_id IS NULL
OR account_manager_id = 0
THEN
`u`.`account_manager_id`
ELSE account_manager_id
END) AS account_manager_id
FROM
user_user
WHERE
id = (SELECT
user_id
FROM
game_user_assigned
WHERE
game_id = `jp`.`id`
LIMIT 1))
ELSE `jp`.`account_manager_id`
END),
`u`.`account_manager_id`) AS acc,
(SELECT
COUNT(recach_limit_id)
FROM
recach_limit
WHERE
recach_limit = '1'
AND recach_limit_game_id = rpr.recach_limit_game_id) AS somewhatgame,
(SELECT
COUNT(recach_limit_id)
FROM
recach_limit
WHERE
recach_limit = '2'
AND recach_limit_game_id = rpr.recach_limit_game_id) AS verygamecommitted,
(SELECT
COUNT(recach_limit_id)
FROM
recach_limit
WHERE
recach_limit = '3'
AND recach_limit_game_id = rpr.recach_limit_game_id) AS notgame,
(SELECT
COUNT(joa.id) AS applicationcount
FROM
game_refer_to_member jrmm
INNER JOIN
game_refer jrr ON jrr.id = jrmm.rid
INNER JOIN
game_applied joa ON jrmm.id = joa.referred_by
WHERE
jrmm.STATUS = '1'
AND jrr.referby_user_id IN (SELECT
ab_testing_user_id
FROM
ab_testing)
AND joa.game_post_id = rpr.recach_limit_game_id
AND (rpr.recach_limit = 1
OR rpr.recach_limit = 2)) AS gamecount
FROM
(`game_post` AS jp)
JOIN
`user_info` AS u ON `jp`.`user_user_id` = `u`.`id`
JOIN
`game_functional` jf ON `jp`.`game_functional_id` = `jf`.`id`
LEFT JOIN
`group_musesm` gm ON `gm`.`group_id` = `jp`.`group_id`
LEFT JOIN
`recach_limit` rpr ON `jp`.`id` = `rpr`.`recach_limit_game_id`
WHERE
`jp`.`status` != '3'
GROUP BY `jp`.`id`
ORDER BY `posted_on` DESC
LIMIT 10
I would first suggest not nesting select statements because this will cause an n^x performance hit on every xth level and I see at least 3 levels of selects inside this query.
Add index
INDEX(status, posted_on)
Move LIMIT inside
Then, instead of saying
FROM (`game_post` AS jp)
say
FROM ( SELECT id FROM game_post
WHERE status != 3
ORDER BY posted_on DESC
LIMIT 10 ) AS ids
JOIN game_post AS jp USING(id)
(I am assuming that the PK of jp is (id)?)
That should efficiently use the new index to get the 10 ids needed. Then it will reach back into game_post to get the other columns.
LEFT
Also, don't say LEFT unless you need it. It costs something to generate NULLs that you may not be needing.
Is GROUP BY necessary?
If you remove the GROUP BY, does it show dup ids? The above changes may have eliminated the need.
IN(SELECT) may optimize poorly
Change
AND jrr.referby_user_id IN ( SELECT ab_testing_user_id
FROM ab_testing )
to
AND EXISTS ( SELECT * FROM ab_testing
WHERE ab_testing_user_id = jrr.referby_user_id )
(This change may or may not help, depending on the version you are running.)
More
Please provide EXPLAIN SELECT if you need further assistance.
I've tried this query successfully with a limit. The following query runs endless without limit:
SELECT o.product_sku
FROM order_table o
WHERE EXISTS (SELECT 1
FROM (SELECT DISTINCT u.type_id,
u.charge_type,
u.billed_weight
FROM ups_table u
WHERE charge_type = 'order_shipping_table'
AND NOT billed_weight = '0'
) dtm
WHERE o.order_id = dtm.type_id)
GROUP BY product_sku
HAVING Count(product_sku) > 1
First of all you don't need these subqueries with DISTINCT and so on:
SELECT o.product_sku
FROM order_table o
WHERE EXISTS (SELECT 1
FROM ups_table u
WHERE charge_type = 'order_shipping_table'
AND NOT billed_weight = '0'
AND type_id=o.order_id
)
GROUP BY product_sku
HAVING Count(product_sku) > 1
I don't know your table structure (what do you want to count in the HAVING?) but you can try to change EXISTS to JOIN:
SELECT o.product_sku
FROM order_table o
JOIN ups_table u on (u.type_id=o.order_id)
AND (u.charge_type = 'order_shipping_table')
AND NOT (billed_weight = '0')
GROUP BY product_sku
HAVING Count(DISTINCT o.order_id) > 1
And you need indexes on o.order_id, o.product_sku, u.type_id,u.charge_type, u.billed_weight
really do not know if you can, but i I need the DATE VENC to be equal to '2013-02-02'.
the values of the column date_pay:
1-2013-01-01
2-2013-02-02
3-0000-00-00
4-0000-00-00
this is my query:
SELECT s.id,
s.name,
s.nro_s,
ts.cat,
SUM( ts.pryce ) AS deuda,
SUM( ts.pryce ) DIV ts.pryce AS c_p,
date_venc = (select max(date_pay) from c ) // the date in question
FROM s
INNER JOIN c
INNER JOIN ts
WHERE s.id = '123'
AND c.id = '123'
AND c.date_pay = '0000-00-00'
AND s.ts = ts.id_ts
Sorry for my english, is very basic.
Greetings.
Assuming date_venc is DATE a possible solution
select *
from s
where s.date_venc=
(select max(cast(SUBSTRING_INDEX(date_pay,'-',-3)as DATE))from c);
also check out sqlfiddle
http://sqlfiddle.com/#!2/64197/1
and your query should probably be modified to,
SELECT s.id,
s.name,
s.nro_s,
ts.cat,
SUM( ts.pryce ) AS deuda,
SUM( ts.pryce ) DIV ts.pryce AS c_p,
date_venc
FROM s
INNER JOIN c
INNER JOIN ts
WHERE s.id = '123'
AND c.id = '123'
AND c.date_pay = '0000-00-00'
AND s.ts = ts.id_ts
AND date_venc = (select max(cast(SUBSTRING_INDEX(date_pay,'-',-3)as DATE)) from c ) // the date in question
I am trying to bring back a string based on an IF statement but it is extremely slow.
It has something to do with the first subquery but I am unsure of how to rearrange this as to bring back the same results but faster.
Here is my SQL:
SELECT IF
(
(
SELECT COUNT(*)
FROM
(
SELECT DISTINCT enquiryId, type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id
) AS parts
WHERE parts.enquiryId = enquiries.id
) > 1, 'Mixed',
(
SELECT DISTINCT type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id AND enquiryId = enquiries.id
)
) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
How can I make it faster?
I have modified my original query below, but I am getting the error that subquery returns more than one row:
SELECT
(SELECT
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
Please have a look if this query yields the same results:
SELECT
enquiryId,
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId
But N.B.'s comment is still valid. To see if and index is used and other information we need to see the EXPLAIN and the table definitions.
This should get you what you want.
I would first pre-query your parts enquiries and parts service types looking for both the count and MINIMUM of the part 'type', grouped by the enquiry ID.
then, run your IF() against that result. If the distinct count is > 0, then 'Mixed'. If only one, since I did the MIN(), it would only have the description of that one value that you desire anyhow.
SELECT
E.ID
IF ( PreQuery.DistTypes > 1, 'Mixed', PreQuery.FirstType ) as PartType
from
Enquiries E
JOIN ( SELECT
PE.EnquiryID,
COUNT( DISTINCT PE.ServiceTypeID ) as DistTypes,
MIN( PST.Type ) as FirstType
from
Parts_Enquiries PE
JOIN Parts_Service_Types PST
ON PE.ServiceTypeID = PST.ID
group by
PE.EnquiryID ) as PreQuery
ON E.ID = PreQuery.EnquiryID