mysql query not working with where in condition - mysql

I am running following query in mysql.
SELECT jobtype_has_trade.jobtype_id,users_jobtype.user_id
FROM users
LEFT JOIN subcontractor ON users.subcontractor_id = subcontractor.id
AND subcontractor.quotations = "YES"
LEFT JOIN users_jobtype ON users.id = users_jobtype.user_id
AND users_jobtype.status = "A"
LEFT JOIN jobtype_has_trade ON users_jobtype.jobtype_trade_id = jobtype_has_trade.id
WHERE users.is_subcontractor = "YES"
AND users.has_android = "YES"
AND jobtype_has_trade.jobtype_id IN (1,3,4)
ORDER by users_jobtype.user_id ASC
and i am getting this output
In above records i need only that user_id that have jobtype_id 1,3 and 4. so, user_id 3 and 7 is acceptable and 9 is not because it has only 3 and 4 as jobtype_id... in sort how can i get only 3 and 7 as user_id in above query?

one option could be to use having clause with group by
SELECT users_jobtype.user_id
FROM users
LEFT JOIN subcontractor ON users.subcontractor_id = subcontractor.id
AND subcontractor.quotations = "YES"
LEFT JOIN users_jobtype ON users.id = users_jobtype.user_id
AND users_jobtype.status = "A"
LEFT JOIN jobtype_has_trade ON users_jobtype.jobtype_trade_id = jobtype_has_trade.id
WHERE users.is_subcontractor = "YES"
AND users.has_android = "YES"
AND jobtype_has_trade.jobtype_id IN (1,3,4)
group by users_jobtype.user_id
having count(distinct jobtype_has_trade.jobtype_id)=3

just check existing of jobtype_id in 1,3,4 for each user. I don't know about your schema
if you have jobtype_id in users_jobtype then you don't need to join to jobtype_has_trade in subquery in where (in my solution)
SELECT jobtype_has_trade.jobtype_id,
users_jobtype.user_id
FROM users
LEFT JOIN subcontractor ON users.subcontractor_id = subcontractor.id
AND subcontractor.quotations = "YES"
LEFT JOIN users_jobtype ON users.id = users_jobtype.user_id
AND users_jobtype.status = "A"
LEFT JOIN jobtype_has_trade ON users_jobtype.jobtype_trade_id = jobtype_has_trade.id
WHERE users.is_subcontractor = "YES"
AND users.has_android = "YES"
AND jobtype_has_trade.jobtype_id IN (1,
3,
4)
and exists( select jobtype_id from jobtype_has_trade inner join users_jobtype on users_jobtype.jobtype_trade_id=jobtype_has_trade.id where users_jobtype.user_id=users.id and
jobtype_has_trade.jobtype_id=1
)
and exists( select jobtype_id from jobtype_has_trade inner join users_jobtype on users_jobtype.jobtype_trade_id=jobtype_has_trade.id where users_jobtype.user_id=users.id and
jobtype_has_trade.jobtype_id=3
)
and exists( select jobtype_id from jobtype_has_trade inner join users_jobtype on users_jobtype.jobtype_trade_id=jobtype_has_trade.id where users_jobtype.user_id=users.id and
jobtype_has_trade.jobtype_id=4
)
ORDER BY users_jobtype.user_id ASC

Related

mysql query takes 3 hours to run and process

I have a query that is ran on a cron job late at night. This query is then processed through a generator as it has to populate another database and I make some additional processes and checks before it is sent to the other DB.
I am wondering is there anyway for me to speed up this query and hopefully keep it as a single query. Or will I be forced to create other queries and join the data within PHP? This queries the main mautic database.
SELECT c.id as "campaign_id",
c.created_by_user,
c.name,
c.date_added,
c.date_modified,
(SELECT DISTINCT COUNT(cl.lead_id)) as number_of_leads,
GROUP_CONCAT(lt.tag) as tags,
cat.title as category_name,
GROUP_CONCAT(ll.name) as segment_name,
GROUP_CONCAT(emails.name) as email_name,
CASE WHEN c.is_published = 1 THEN "Yes" ELSE "No" END AS "published",
CASE WHEN c.publish_down > now() THEN "Yes"
WHEN c.publish_down > now() AND c.is_published = 0 THEN "Yes"
ELSE "No" END AS "expired"
FROM campaigns c
LEFT JOIN campaign_leads cl ON cl.campaign_id = c.id
LEFT JOIN lead_tags_xref ltx on cl.lead_id = ltx.lead_id
LEFT JOIN lead_tags lt on ltx.tag_id = lt.id
LEFT JOIN categories cat on c.category_id = cat.id
LEFT JOIN lead_lists_leads llist on cl.lead_id = llist.lead_id
LEFT JOIN lead_lists ll on llist.leadlist_id = ll.id
LEFT JOIN email_list_xref el on ll.id = el.leadlist_id
LEFT JOIN emails on el.email_id = emails.id
GROUP BY c.id;
Here is a image of the explain
https://prnt.sc/qQtUaLK3FIpQ
Definitions
Campaign Table:
https://prnt.sc/6JXRGyMsWpcd
Campaign_leads table
https://prnt.sc/pOq0_SxW2spe
lead_tags_xref table
https://prnt.sc/oKYn92O82gHL
lead_tags table
https://prnt.sc/ImH81ECF6Ly1
categories table
https://prnt.sc/azQj_Xwq3dw9
lead_lists_lead table
https://prnt.sc/x5C5fiBFP2N7
lead_lists table
https://prnt.sc/bltkM0f3XeaH
email_list_xref table
https://prnt.sc/kXABVJSYWEUI
emails table
https://prnt.sc/7fZcBir1a6QT
I am only expected 871 rows to be completed, I have identified that the joins can be very large, in the tens of thousands.
Seems you have an useless select DISTINCT .. could you are looking for a count(distinct .. )
In this way you can avoid nested select for each rows in main select ..
SELECT c.id as "campaign_id",
c.created_by_user,
c.name,
c.date_added,
c.date_modified,
COUNT(DISTINCT cl.lead_id) as number_of_leads,
GROUP_CONCAT(lt.tag) as tags,
cat.title as category_name,
GROUP_CONCAT(ll.name) as segment_name,
GROUP_CONCAT(emails.name) as email_name,
CASE WHEN c.is_published = 1 THEN "Yes" ELSE "No" END AS "published",
CASE WHEN c.publish_down > now() THEN "Yes"
WHEN c.publish_down > now() AND c.is_published = 0 THEN "Yes"
ELSE "No" END AS "expired"
FROM campaigns c
LEFT JOIN campaign_leads cl ON cl.campaign_id = c.id
LEFT JOIN lead_tags_xref ltx on cl.lead_id = ltx.lead_id
LEFT JOIN lead_tags lt on ltx.tag_id = lt.id
LEFT JOIN categories cat on c.category_id = cat.id
LEFT JOIN lead_lists_leads llist on cl.lead_id = llist.lead_id
LEFT JOIN lead_lists ll on llist.leadlist_id = ll.id
LEFT JOIN email_list_xref el on ll.id = el.leadlist_id
LEFT JOIN emails on el.email_id = emails.id
GROUP BY c.id;
anyway be sure you have a proper composite index on
table campaign_leads columns campaign_id, lead_id
table lead_tags_xref columns lead_id, tag_id
table lead_lists_leads columns lead_id, leadlist_id
table email_list_xref columns leadlist_id, email_id

How do I left join only if previous left join didn't find anything

It shouldn't join on tbi2 if tbi1 finds a result, but it retrieving results from both tables.
The idea of this is to join on the item table but if there isn't an exact match, fallback to joining on '*'
(This is the same SQL statement as a previous question, but a different attempt... again.)
SELECT
COALESCE(tbi1.id,tbi2.id,tbi3.id,tbi4.id,tbi5.id,tbi6.id) AS id
,COALESCE(tbi1.cost,tbi2.cost,tbi3.cost,tbi4.cost,tbi5.cost,tbi6.cost) AS cost
,COALESCE(tbi1.price,tbi2.price,tbi3.price,tbi4.price,tbi5.price,tbi6.price) AS price
,COALESCE(tbi1.itemNumber,tbi2.itemNumber,tbi3.itemNumber,tbi4.itemNumber,tbi5.itemNumber,tbi6.itemNumber) AS itemNumber
,COUNT(COALESCE(tbi1.id,tbi2.id,tbi3.id,tbi4.id,tbi5.id,tbi6.id)) AS quantityToAdd
,tbl_equipment.id AS equipId
,tbl_equipment.partNum
,tbl_equipment.nolinks
,tbl_addedItemCustomerPrices.customerprice AS specialPrice
,tbl_testTypes.isInspt
,tbl_testTypes.isRecharge
,tbl_testTypes.isRecharagable
,tbl_tests.nextDate AS theNextDate
FROM
tbl_tests
INNER JOIN tbl_equipment ON tbl_equipment.id = tbl_tests.equipmentId
INNER JOIN tbl_model ON tbl_model.id = tbl_equipment.model
INNER JOIN tbl_testTypes ON tbl_testTypes.id = tbl_tests.testTypeId
LEFT JOIN tbl_items AS tbi1 ON tbi1.size = tbl_model.size AND tbi1.service = tbl_testTypes.name
LEFT JOIN tbl_items AS tbi2 ON tbi2.size = '*' AND tbi2.service = tbl_testTypes.name AND tbi1.id IS NULL
LEFT JOIN tbl_items AS tbi3 ON tbi3.type = tbl_model.type AND tbi3.service = tbl_testTypes.name AND tbi2.id IS NULL
LEFT JOIN tbl_items AS tbi4 ON tbi4.type = '*' AND tbi4.service = tbl_testTypes.name AND tbi3.id IS NULL
LEFT JOIN tbl_items AS tbi5 ON tbi5.category = tbl_model.category AND tbi5.service = tbl_testTypes.name AND tbi4.id IS NULL
LEFT JOIN tbl_items AS tbi6 ON tbi6.category = '*' AND tbi6.service = tbl_testTypes.name AND tbi5.id IS NULL
LEFT JOIN tbl_addedItemCustomerPrices ON COALESCE(tbi1.id,tbi2.id,tbi3.id,tbi4.id,tbi5.id,tbi6.id) = tbl_addedItemCustomerPrices.itemId AND tbl_addedItemCustomerPrices.customerid = $currentSite
WHERE
((nextDate >= '$startDate' AND nextDate <= '$endDate' ) OR tbl_testTypes.isRecharge = 'true')
{$monthEndQuery}
AND tbl_equipment.site = $currentSite
GROUP BY COALESCE(tbi1.id,tbi2.id,tbi3.id,tbi4.id,tbi5.id,tbi6.id)
ORDER BY tbl_equipment.id, isRecharagable DESC

Counting groups of 3? Possible?

I have the following query that is selecting groups of entries of 3 that match and don't exist in the second table.
I need to find a way to return via 'count_result' the count of the number of groups found, not individual entry ids as below.
How can I achieve this?
SELECT COUNT(sub.entry_id) as count_result
FROM exp_submissions AS sub
LEFT JOIN exp_judging_portfolios AS jud1 ON sub.entry_id = jud1.entry_id_1
LEFT JOIN exp_judging_portfolios AS jud2 ON sub.entry_id = jud1.entry_id_2
LEFT JOIN exp_judging_portfolios AS jud3 ON sub.entry_id = jud1.entry_id_3
WHERE jud1.entry_id_1 IS NULL
AND jud2.entry_id_2 IS NULL
AND jud3.entry_id_3 IS NULL
AND sub.member_group = 6
AND sub.type_id = 1
GROUP BY sub.member_id, sub.portfolio_number
HAVING count(sub.portfolio_number) = 3
No subquery necessary.
SELECT COUNT(DISTINCT(sub.member_id, sub.portfolio_number)) as count_result
FROM exp_submissions AS sub
LEFT JOIN exp_judging_portfolios AS jud1 ON sub.entry_id = jud1.entry_id_1
LEFT JOIN exp_judging_portfolios AS jud2 ON sub.entry_id = jud1.entry_id_2
LEFT JOIN exp_judging_portfolios AS jud3 ON sub.entry_id = jud1.entry_id_3
WHERE jud1.entry_id_1 IS NULL
AND jud2.entry_id_2 IS NULL
AND jud3.entry_id_3 IS NULL
AND sub.member_group = 6
AND sub.type_id = 1
GROUP BY sub.member_id, sub.portfolio_number
HAVING count(sub.portfolio_number) = 3
Use your query as the source for another query that just counts the results:
SELECT COUNT(*) FROM (
-- YOUR QUERY GOES HERE --
) AS t

How can I make more than one select queries in single select query.

I have to write a query where, I need to fetch records for last week, last month, and for all.
For this problem I wrote 3 diffrent queries (for last week, for last month and for all)
For Weekly Info :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz' AND DATE_FORMAT(o.created_date,'%Y-%m-%d') >= #weekstartdate AND DATE_FORMAT(o.created_date,'%Y-%m-%d') <= #weekenddate
GROUP BY bu.brand_name
For Monthly Info :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz' AND DATE_FORMAT(o.created_date,'%Y-%m-%d') >= #monthstartdate AND DATE_FORMAT(o.created_date,'%Y-%m-%d') <= #monthenddate
GROUP BY bu.brand_name
For All Records :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz'
GROUP BY bu.brand_name
and these are working fine (giving currect output).
But problem is that, I have to merge these three queries in single one.
Where output should be as
Brand name, item_sold(week), total_price(week),item_sold(month), total_price(month),item_sold(all), total_price(all)
How can I write this query?
Without looking deep into your code, the obvious solution would be
SELECT
all.brand_name
pw.items_sold items_sold_week
pw.total_price total_price_week
pm.items_sold items_sold_month
pm.total_price total_price_month
all.items_sold items_sold_all
all.total_price total_price_all
FROM
(your all-time select) all
JOIN (your per-month select) pm ON all.brand_name = pm.brand_name
JOIN (your per-week select) pw ON all.brand_name = pw.brand_name
Though you probably should rethink your entire approach and make sure whether you really want that kind of logic in a DB layer or it is better to be in your application.
You could use case to limit aggregates to a subset of rows:
select bu.brand_name
, count(case when date_format(o.created_date,'%Y-%m-%d') >= #weekstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #weekenddate
then 1 end) as '# Item Sold Week'
, sum(case when date_format(o.created_date,'%Y-%m-%d') >= #weekstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #weekenddate
then s.price end) as 'Total_Price Week'
, count(case when date_format(o.created_date,'%Y-%m-%d') >= #monthstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #monthstartdate
then 1 end) as '# Item Sold Month'
, ...
If all three selects uses the same fields in the results, you can UNION them:
SELECT *
FROM (SELECT 1) AS a
UNION (SELECT 2) AS b
UNION (SELECT 3) AS c
If you need to tell week/mon/all records from each other - just add constant field containing "week" or "mon"
You cam use the UNION.keyword between the queries to bundle them.together BUT tje column types and sequence must be the same in all queries. You could add an identifier to each set

MySQL join only selected rows

SELECT invoices.number_formatted, SUM(invoices_elements.netto)
FROM invoices
LEFT JOIN invoices_elements ON invoices_elements_code_invoices_id = invoices_id
WHERE invoices_enable = 1
AND invoices_elements_enable = 1
GROUP BY invoices_elements_code_invoices_id
If table "invoices_elements" doesn't have any rows with "invoices_elements_enable = 1" this query return NULL - but i want "number formatted". So i do this:
SELECT SUM(netto)
FROM (invoices)
LEFT JOIN (SELECT * FROM invoices_elements WHERE invoices_elements_enable = 1) ON invoices_elements_code_invoices_id = invoices_id
WHERE invoices_enable = 1
GROUP BY invoices_elements_code_invoices_id
... and this of coz works. But - is better way to do it?
You can do
SELECT SUM(netto)
FROM invoices
LEFT JOIN invoices_elements
ON invoices_elements_code_invoices_id = invoices_id
AND invoices_elements_enable = 1
WHERE invoices_enable = 1
GROUP BY invoices_elements_code_invoices_id
Note the restriction invoices_elements_enable = 1 is in the ON clause to avoid converting the query into an inner join.