Using From (subquery) table in another subquery - mysql

So I have the following :
SELECT bt.CompanyName,
AcceptedTable.Count AS AcceptedCount,
CompletedTable.Count AS CompletedCount,
SkippedTable.Count AS SkippedCount,
TotalTable.Count AS TotalCount
FROM (
SELECT uic.*, uic.Id AS UCId, ic.*, ic.Id AS CompanyId, ic.Name AS CompanyName
FROM UserChallenges iuc
JOIN Users iu ON iuc.UserId = iu.Id
JOIN Companies ic ON ic.Id = iu.CompanyId) bt
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
GROUP BY CompanyId) TotalTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE AcceptedAt IS NOT NULL
GROUP BY CompanyId) AcceptedTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE DoneStatus IN (1,6)
GROUP BY CompanyId) CompletedTable ON CompletedTable.CompanyId = bt.CompanyId
JOIN (
SELECT CompanyId AS CompanyId, COUNT(UCId) AS Count
FROM bt
WHERE DoneStatus IN (4)
GROUP BY CompanyId) SkippedTable ON SkippedTable.CompanyId = bt.CompanyId
GROUP BY bt.CompanyName
The goal is to compute Counts on the same set of data with different WHERE clauses.
I tried the above approach as it simplifies a bit the query instead of having to do JOINs again in each sub queries.
But it seems like it's not possible, it's Common table expression and is not available in mysql 5.7.
What would be a better way to implement this ?
There must be a cleaner way than this super long query.
I could use a temporary table too, although I'm not sure it's a good idea in a View.

You cannot refer another Derived Table alias within a different Derived Table's FROM clause, because it is not accessible at that level.
Also, I feel that you can rather solve this problem using Conditional Aggregation:
SELECT bt.CompanyName,
COUNT(CASE WHEN bt.AcceptedAt IS NOT NULL THEN bt.UCId END) AS AcceptedCount,
COUNT(CASE WHEN bt.DoneStatus IN (1,6) THEN bt.UCId END) AS CompletedCount,
COUNT(CASE WHEN bt.DoneStatus IN (4) THEN bt.UCId END) AS SkippedCount,
COUNT(bt.UCId) AS TotalCount
FROM (
SELECT uic.*,
uic.Id AS UCId,
ic.*,
ic.Id AS CompanyId,
ic.Name AS CompanyName
FROM UserChallenges iuc
JOIN Users iu ON iuc.UserId = iu.Id
JOIN Companies ic ON ic.Id = iu.CompanyId) bt
GROUP BY bt.CompanyId, bt.CompanyName

Related

Filtering out a row with a null value

How would I filter out a row with a null value?
SELECT country_table.country as country,
(SELECT TRUNCATE(AVG(news_guard_score), 3) FROM news WHERE news.country_id = country_table.country_id) as 'avg_news_score'
FROM country_table
-- WHERE country_table.country_id IS NOT NULL
GROUP BY country;
It does not work with the commented code
How it looks like
I need it without the final row
Have you tried:
SELECT country_table.country as country,
(SELECT TRUNCATE(AVG(news_guard_score), 3) FROM news WHERE (news.country_id = country_table.country_id AND country_table.country_id IS NOT NULL)) as 'avg_news_score'
FROM country_table
GROUP BY country;
It sounds like you need a HAVING clause:
SELECT c.country, TRUNCATE(AVG(n.news_guard_score), 3)
FROM country_table c
INNER JOIN news n ON n.country_id = c.country_id
GROUP BY c.country
HAVING AVG(n.news_guard_score) IS NOT NULL;
Note: I have rewritten your query to use a direct inner join, rather than the correlated subquery which you were using (and which would probably not perform too well).

Is it possible to add JOIN query with SUM and GROUP BY in SQL for the following query?

I can read "cust_id" and their dues with the following query in one table called 'incomes' but I have names and address in other table called customer - what I need to add with the query - is it possible to add JOIN query with the following? how?
SELECT cust_id, SUM(inc_text)- (SUM(inc_amount)-
SUM(
case
when inctype_id =11 then 0
else inc_amount
end
)) Total_due_left FROM incomes
GROUP BY cust_id;
This should work:
SELECT c.*,
( SUM(i.inc_text) - (SUM(i.inc_amount) - SUM(CASE WHEN i.inctype_id = 11 then 0 ELSE inc_amount END))
) as Total_due_left
FROM incomes i JOIN
customers c
ON c.cust_id = i.cust_id
GROUP BY c.cust_id;
This works assuming that customers.cust_id is the primary key (or at least declared unique) in customers. Otherwise you need to list the columns explicitly in the SELECT and GROUP BY.
It also looks like this can be simplified to:
SELECT c.*,
( SUM(i.inc_text) - SUM(CASE WHEN i.inctype_id = 11 THEN inc_amount ELSE 0 END)
) as Total_due_left
FROM incomes i JOIN
customers c
ON c.cust_id = i.cust_id
GROUP BY c.cust_id;

Optimize Query Mysql to count data in each district

i have this query for calculate success total in each district. this query works but its take until 2min to output data, i have 15k rows in orders.
SELECT
nsf.id,
nsf.province,
nsf.city,
nsf.district,
nsf.shipping_fee,
IFNULL((SELECT COUNT(orders.id) FROM orders
JOIN users ON orders.customer_id = users.id
JOIN addresses ON addresses.user_id = users.id
JOIN subdistricts ON subdistricts.id = addresses.subdistrict_id
WHERE orders.status_tracking IN ("Completed","Successful Delivery")
AND subdistricts.ninja_fee_id = nsf.id
AND orders.transfer_to = "cod"),0) as success_total
from ninja_shipping_fees nsf
GROUP BY nsf.id
ORDER BY nsf.province;
the output should be like this
can you help me to improve the peformance? Thanks
Try performing the grouping/calculation in a joined "derived table" instead of a "correlated subquery"
SELECT
nsf.id
, nsf.province
, nsf.city
, nsf.district
, nsf.shipping_fee
, COALESCE( g.order_count, 0 ) AS success_total
FROM ninja_shipping_fees nsf
LEFT JOIN (
SELECT
subdistricts.ninja_fee_id
, COUNT( orders.id ) AS order_count
FROM orders
JOIN users ON orders.customer_id = users.id
JOIN addresses ON addresses.user_id = users.id
JOIN subdistricts ON subdistricts.id = addresses.subdistrict_id
WHERE orders.status_tracking IN ('Completed', 'Successful Delivery')
AND orders.transfer_to = 'cod'
GROUP BY subdistricts.ninja_fee_id
) AS g ON g.ninja_fee_id = nsf.id
ORDER BY nsf.province;
"Correlated subqueries" are often a source of poor performance.
Other notes, I prefer to use COALESCE() because it is ANSI standard and available in most SQL implementations now. Single quotes are more typically used to denote strings literals.

How to avoid conditional multiple SELECT in a query

I have this simple SQL
SELECT
(CASE
WHEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer) IS NOT NULL
THEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer)
ELSE Customer
END) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;
My question is how can I avoid make two same SELECT statements: one as condition and other as a value selection. I know that I can create a temporary variable, but this forces me to use stored procedures or temporary tables. Is there a simple way to avoid this?
You could use IFNULL like this:
IFNULL((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Or COALESCE like this:
COALESCE((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Reference:
COALESCE(value,...)
IFNULL(expr1,expr2)
Try this
SELECT
IsNull(Name,Customer)
AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;
You can rewrite your query by using join
SELECT
COALESCE(c.Name,Customer) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Programs.`Name` AS ProgramName
FROM ProgramsRun
LEFT JOIN Programs ON ProgramsRun.Programs_id = Programs.Programs_id
LEFT JOIN Customers c ON c.Customers_id = Customer;

Mysql Group BY with if statement

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