Mysql Group BY with if statement - mysql

I'm not sure if this is something you can do in a single select statement without nesting selects.
I am grouping my results and I want to know IF a field inside the entire grouping contains a condition, display yes. With this it will just take the first row from the group and check the condition instead of all the rows in the group
if(field = 'condition','yes','no') as field_found

example table: id, score
SELECT t1.id, (
SELECT IF("10" IN (
SELECT score FROM table t2 WHERE t1.id = t2.id
),'yes','no'))
FROM table t1
GROUP BY t1.id
does that work?

Since you are already doing a group by, you should be able to add a MAX() as a column having the condition you are expecting and just add that to the group... such as
select
MAX( if( field LIKE '%condition%','1', '2' )) as ExtraOrderBy,
First_Name,
Last_Name,
... rest of query ...
group by
customers.Customer_ID
order by
1
In this case, the order by is the ordinal column in the SQL list instead of explicit retyping the MAX( IF() ) condition... So, if the condition is true, mark it with "1", otherwise "2" will float all those that qualify to the top of the list... Then, you could sub-order by other things like last name, first name, or other fields you have queried.

if(GROUP_CONCAT(field) LIKE '%condition%','yes','no')
SELECT first_name,last_name, CONCAT(physical_address," ",physical_address2," ",city, " ",zip) as address, MONTHNAME(install_date) as billing_month, IFNULL(status.quantity,0) as total_measures_instalclient2d,IFNULL(client1_measures,"") as client1_measures,IFNULL(client2_measures,"") as client2_measures,IFNULL(client1_quantity,0) as client1_quantity,IFNULL(client2_quantity,0) as client2_quantity,if(GROUP_CONCAT(measure_list.measure_type) LIKE '%Outreach/ Assessment%','yes','no') as outreach_invoiced,GROUP_CONCAT(IF(client='Client1',CONCAT(percent*100,"%-",measure_list.measure_type),NULL)) as client1_percent,GROUP_CONCAT(IF(client='Client2',CONCAT(percent*100,"%-",measure_list.measure_type),NULL)) as client2_percent,work_order.notes FROM customers
INNER JOIN measure on measure.customer_id = customers.customer_id
INNER JOIN measure_list on measure_list.measure_list_id = measure.measure_list_id
INNER JOIN work_order on work_order.work_order_id = measure.work_order_id
INNER JOIN billing on billing.workmanship = work_order.workmanship AND billing.measure_type = measure_list.measure_type
LEFT JOIN (
SELECT customers.customer_id,SUM(quantity) as quantity,GROUP_CONCAT(IF(client='Client1',measure_description,NULL)) as client1_measures,GROUP_CONCAT(IF(client='client2',measure_description,NULL)) as client2_measures,SUM(IF(client='client1',quantity,0)) as client1_quantity,SUM(IF(client='client2',quantity,0)) as client2_quantity FROM customers
INNER JOIN measure on measure.customer_id = customers.customer_id
INNER JOIN measure_list on measure_list.measure_list_id = measure.measure_list_id
INNER JOIN work_order on work_order.work_order_id = measure.work_order_id
INNER JOIN billing on billing.workmanship = work_order.workmanship AND billing.measure_type = measure_list.measure_type
WHERE measure_list.measure_type NOT IN ('measure1','measure2')
GROUP BY customers.customer_id
) as status on status.customer_id = customers.customer_id
WHERE measure_list.measure_type IN ('measure1','measure2')
GROUP BY customers.customer_id

Related

SQL Complex update query filter distinct values only

I have 3 tables with following columns.
Table: A with column: newColumnTyp1, typ2
Table: B with column: typ2, tableC_id_fk
Table: C with column: id, typ1
I wanted to update values in A.newColumnTyp1 from C.typ1 by following logic:
if A.typ2=B.typ2 and B.tableC_id_fk=C.id
the values must be distinct, if any of the conditions above gives multiple results then should be ignored. For example A.typ2=B.typ2 may give multiple result in that case it should be ignored.
edit:
the values must be distinct, if any of the conditions above gives multiple results then take only one value and ignore rest. For example A.typ2=B.typ2 may give multiple result in that case just take any one value and ignore rest because all the results from A.typ2=B.typ2 will have same B.tableC_id_fk.
I have tried:
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.typ2
it gives me a result of table with two columns typ1,typ2
My logic was, I will then filter this new table and compare the type2 value with A.typ2 and update A.newColumnTyp1
I thought of something like this but was a failure:
update A set newColumnTyp1= (
SELECT C.typ1 from
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.type2
where A.typ2=B.typ2);
I am thinking of an updateable CTE and window functions:
with cte as (
select a.newColumnTyp1, c.typ1, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set newColumnTyp1 = typ1
where cnt > 1
Update: if the columns have the same name, then alias one of them:
with cte as (
select a.typ1, c.typ1 typ1c, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set typ1 = typ1c
where cnt > 1
I think I would approach this as:
update a
set newColumnTyp1 = bc.min_typ1
from (select b.typ2, min(c.typ1) as min_typ1, max(c.typ1) as max_typ1
from b join
c
on b.tableC_id_fk = c.id
group by b.type2
) bc
where bc.typ2 = a.typ2 and
bc.min_typ1 = bc.max_typ1;
The subquery determines whether typ1 is always the same. If so, it is used for updating.
I should note that you might want the most common value assigned, instead of requiring unanimity. If that is what you want, then you can ask another question.

How to Join the table in phpmyadmin

This is two query that i need to combine:
first query:
SELECT emp.name, count(rej.employee_ic) as 'rejected leave'
FROM employee as emp
LEFT OUTER JOIN rejectedleave as rej
ON emp.ic_no = rej.employee_ic
GROUP BY emp.`ic_no`
Second query:
SELECT emp.name, count(app.employee_ic) as 'approved leave'
FROM employee as emp
LEFT OUTER JOIN approvedleave as app
ON emp.ic_no = app.employee_ic
GROUP BY emp.`ic_no`
The output:
first query:
first query output image
second query:
second query output image
i want to combine this two table into one table. please help me to solve this problem, appreciate your help.
Is this you need? It find the count of approved and rejected leaves for each available employee in separate subqueries and then joins it with the employee table to show the count of rejected and approved leaves for each employee in one row.
Use coalesce(col,0) to return 0 if the count if null.
Coalesce returns first non null value from the given list of arguments.
select
t1.*,
coalesce(t2.approvedleave,0) approvedleave
coalesce(t3.rejectedleave,0) rejectedleave
from employee t1
left join (
select employee_ic, count(*) approvedleave
from approvedleave
group by employee_ic
) t2 on t1.ic_no = t2.employee_ic
left join (
select employee_ic, count(*) rejectedleave
from rejectedleave
group by employee_ic
) t3 on t1.ic_no = t3.employee_ic;
Read about coalesce here:
http://www.w3resource.com/mysql/comparision-functions-and-operators/coalesce-function.php
I suspect you want this:
SELECT e.name, COALESCE(r.rejected_leave, 0) as rejected_leave,
COALESCE(a.approved_leave, 0) as approved_leave
FROM employee e LEFT OUTER JOIN
(SELECT rl.employee_ic, COUNT(*) as rejected_leave
FROM rejectedleave rl
GROUP BY rl.employee_ic
) r
ON e.ic_no = r.employee_ic LEFT OUTER JOIN
(SELECT al.employee_ic, COUNT(*) as approved_leave
FROM approvedleave al
GROUP BY al.employee_ic
) a
ON e.ic_no = a.employee_ic;
That is, do the aggregation before doing the joins. This assumes that e.ic_no and e.name are both unique in the employees table.

How to access parent column from a subquery within a join

I'm trying to left join the second table useri_ban based on the users' ids, with the extra condition: useri_ban.start_ban = max_start.
In order for me to calculate max_start, I have to run the following subquery:
(SELECT MAX(ub.start_ban) AS max_start, user_id FROM useri_ban ub WHERE ub.user_id = useri.id)
Furthermore, in order to add max_start to every row, I need to inner join this subquery's result into the main result. However, it seems that once I apply that join, the subquery is no longer able to access useri.id.
What am I doing wrong?
SELECT
useri.id as id,
useri.email as email,
useri_ban.warning_type_id as warning_type_id,
useri_ban.type as type,
useri.created_at AS created_at
FROM `useri`
inner join
(SELECT MAX(ub.start_ban) AS max_start, user_id FROM useri_ban ub WHERE ub.user_id = useri.id) `temp`
on `useri`.`id` = `temp`.`user_id`
left join `useri_ban` on `useri_ban`.`user_id` = `useri`.`id` and `useri_ban`.`start_ban` = `max_start`
Does this solve your problem? You need GROUP BY in the inner query instead of another join.
SELECT useri.id, useri.email, maxQuery.maxStartBan
FROM useri
INNER JOIN
(
SELECT useri_ban.user_id ubid, MAX(useri_ban.startban) maxStartBan
FROM useri_ban
GROUP BY useri_ban.user_id
) AS maxQuery
ON maxQuery.ubid = useri.id;

LEFT JOIN SUM with WHERE clause

The following query always outputs SUM for all rows instead of per userid. Not sure where else to look. Please help.
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
) assignments ON (userid = assignments.userid AND ticketid = ?)
WHERE ticketid = ?
ORDER BY assigned,scheduled
If you want to keep the SELECT *, you would have to add a group by clause in the subquery. Something like this
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
GROUP BY userid
) time_entriesSummed ON time_entriesSummed.userid = assignments.userid
WHERE ticketid = ?
ORDER BY assigned,scheduled
But a better way would be to change the SELECT * to instead select the fields you want a add a group by clause directly. Something like this
SELECT
assignments.id,
assignments.assigned,
assignments.scheduled,
SUM(time_entries.timeworked) AS totalTimeworked
FROM assignments
LEFT JOIN time_entries
ON time_entries.userid = assignments.userid
GROUP BY assignments.id, assignments.assigned, assignments.scheduled
Edit 1
Included table names in query 2 as mentioned in chameera's comment below

MySQL Inner Join with where clause sorting and limit, subquery?

Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.