How to avoid repetitions of columns in MySQL subquery? - mysql

i am retrieving the result from above 4 table using following query
SELECT
(SELECT SUM(CASE when c.Training_Id=1 then 1 else 0 end)
FROM courses c
INNER JOIN enrolled_students es
ON c.Course_Id = es.Course_Id
) STEM,
(SELECT SUM(CASE when c.Training_Id=2 then 1 else 0 end)
FROM courses c
INNER JOIN enrolled_students es ON c.Course_Id = es.Course_Id
) MA,
c.* FROM campus c;
The problem with this query is, two(2) students are in STEM and one(1) Student in MA against Campus_Id 3, but its repeating records against all campuses. i want if campus has no students than there should be '0' Zero.

You need to filter your subselects by Campus_Id. But first you have to use distinct table aliases. Change your last line to ca.* FROM campus ca. Then you can use a where clause in your subselects (WHERE c.Campus_Id = ca.Campus_Id).
SELECT
(SELECT SUM(CASE when c.Training_Id=1 then 1 else 0 end)
FROM courses c
INNER JOIN enrolled_students es
ON c.Course_Id = es.Course_Id
WHERE c.Campus_Id = ca.Campus_Id -- line added
) STEM,
(SELECT SUM(CASE when c.Training_Id=2 then 1 else 0 end)
FROM courses c
INNER JOIN enrolled_students es ON c.Course_Id = es.Course_Id
WHERE c.Campus_Id = ca.Campus_Id -- line added
) MA,
ca.* FROM campus ca; -- line changed
This should solve your problem.
To improve the performance you can also filter your subselects by Training_Id. In the first subselect you only need the rows with Training_Id=1. So you can change your where clause to:
WHERE c.Campus_Id = ca.Campus_Id
AND c.Training_Id = 1
Doing that you can also use COUNT instead of SUM. So your subselect would look like:
SELECT COUNT(1)
FROM courses c
INNER JOIN enrolled_students es ON c.Course_Id = es.Course_Id
WHERE c.Campus_Id = ca.Campus_Id AND c.Training_Id = 1
To prevent code duplication (your subselects are almost equal) you can join all needed tables and group by Campus_Id:
select
COUNT(co.Training_Id=1 OR NULL) STEM,
COUNT(co.Training_Id=2 OR NULL) MA,
ca.Campus_Id
from campus ca
left join courses co on co.Campus_Id = ca.Campus_Id
left join enrolled_students es on es.Course_Id = co.Course_Id
where co.Training_Id in (1, 2)
group by ca.Campus_Id

Related

MySQL JOIN with count / distinct

I've got multiple tables in my database.
Below is my SQL. It all works well for the majority.
How my system works is I may have a game which may sit in multiple competitions therefore will get called into this query more than once through the various JOINS.
It's like Liverpool playing a game but that game earns points for two competitions. It's not two games even though it'll appear twice. It's also not two rows in my database table 'game'.
What I have works for everything but the games that are in two competitions and where I want to do a few counts.
So my query code at the bottom works, but if I try add in
SUM(CASE WHEN g.isRanfurly = 1 THEN 1 ELSE 0 END) as TTLTests,
then this fails as it counts those games twice.
Any ideas?
SELECT DISTINCT(g.gameId), gd.playerId, t.teamName,
COUNT(distinct gd.gameId) as Appearances,
SUM(gd.tries) as TTLTries,
COUNT(CASE WHEN g.isTestMatch = 1 THEN 1 ELSE 0 END) as TTLTests,
IF(gd.homeaway = 1, g.team1Id, g.team2Id) as myteamId
FROM `gamedata` gd
JOIN `games` g ON gd.gameId = g.gameId
JOIN `teams` t ON t.teamId = IF(gd.homeaway = 1, g.team1Id, g.team2Id)
JOIN `roundgames` rg ON rg.gameId = gd.gameId
JOIN `rounds` r ON r.roundId = rg.roundId
JOIN `competitions` c ON r.competitionId = c.competitionId
WHERE `playerId` = 1 AND `didntPlay` = 0 AND t.teamType = 2
group by t.teamName
You can try the below - remove the else 0 from the conditional count
SELECT gd.playerId, t.teamName,
COUNT(distinct gd.gameId) as Appearances,
SUM(gd.tries) as TTLTries,
COUNT(CASE WHEN g.isTestMatch = 1 THEN 1 END) as TTLTests,
IF(gd.homeaway = 1, g.team1Id, g.team2Id) as myteamId
FROM `gamedata` gd
JOIN `games` g ON gd.gameId = g.gameId
JOIN `teams` t ON t.teamId = IF(gd.homeaway = 1, g.team1Id, g.team2Id)
JOIN `roundgames` rg ON rg.gameId = gd.gameId
JOIN `rounds` r ON r.roundId = rg.roundId
JOIN `competitions` c ON r.competitionId = c.competitionId
WHERE `playerId` = 1 AND `didntPlay` = 0 AND t.teamType = 2
group by gd.playerId, t.teamName
The solution was to use DISTINCT within the SUM
SUM(CASE WHEN g.isTestMatch = 1 THEN gd.tries ELSE 0 END) as TTLTestTries,

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

mysql: Group Concat with joined tables and values that aren't unique

I have an issue where I need to aggregate and concatenate multiple row data into single row output. I understand the tables are a problem in that their is no unique index, but I need to do this at the query level instead of the scripting level and I can't touch the database structure.
Here we go:
table: characteristics
id code_a code_b
-------------------------------
2201 CHAU AIRS
2201 CHAU PELC
2201 PROX AUTO
2201 PROX HOP`
table: characteristics_types
code description
-------------------------------
CHAU Heating System
PROX Nearby
table: characteristics_sub_types
code_a code description
-------------------------------
CHAU AIRS Forced Air
CHAU PELC Baseboard
PROX AUTO Highway
PROX HOP Hospital
Result required:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard Highway, Hospital
Not working:
SELECT id,
(case when C.code_a='CHAU' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Heating System,
(case when C.code_a='PROX' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join characteristics_sub_types STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id,C.code_a
I am getting the following results:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard NULL
2201 NULL Highway, Hospital
Any direction would be greatly appreciated!
GROUP_CONCAT accepts a DISTINCT keyword which is useful in fan-out-queries. You can also use ORDER BY within a GROUP_CONCAT if you need your results to be ordred. See Documentation. Using this, we can write your intended query like below:
SELECT
c.id,
GROUP_CONCAT(DISTINCT ct.description) as 'Heating System',
GROUP_CONCAT(DISTINCT cst.description) as 'Nearby'
FROM characteristics c
LEFT JOIN characteristics_types ct ON ct.id = c.id
LEFT JOIN characteristics_sub_types cst
ON cst ON cst.code_a = c.code_a AND cst.code = c.code_b
GROUP BY 1
I would write separate subqueries, each one preforming the group_concat that you need, and join them together. Individually, I wrote them like this:
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id;
And the join like this:
SELECT c.id, t1.`Heating System`, t2.`Nearby`
FROM(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id) t1
JOIN(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Nearby'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'PROX'
GROUP BY c.id) t2 ON t1.id = t2.id;
Here is a working SQL Fiddle.
You can try this way:
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as descriptio_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as descriptio_nearby,
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as description_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as description_nearby
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id

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