SQL calculate new column based on other queries with inner join - mysql

I'm building up a large SQL query to calculate everything in one go based on a table. I need a column called total_revenue, which should be the sum of total_commission and total_profit together.
Right now, I'm constructing a new inner join to do this, but this inner join that calculates total_revenue is an overhead.
How can I, instead, run a inner join on the result of my upper query to then do a sum of total_commission + total_profit without doing another inner join on another table, here's my current SQL:
SELECT
# Affilaite
Conversion.aff_id,
# Revenue
JoinedRevenue.total_revenue,
# Commission
JoinedCommission.total_commission,
# Profit
JoinedProfit.total_profit
FROM
tlp_conversions AS Conversion
INNER JOIN
(
SELECT
Commission.aff_id,
Commission.created,
SUM(Commission.amount) AS total_commission
FROM
tlp_commissions AS Commission
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
GROUP BY
Commission.aff_id
) AS JoinedCommission
ON JoinedCommission.aff_id = Conversion.aff_id
INNER JOIN
(
SELECT
Application.tlp_aff_id,
ApplicationResponse.application_id,
ApplicationResponse.result,
Commission.seller_code,
Commission.application_response_id,
Commission.created,
SUM(Commission.amount) AS total_profit
FROM
tlp_commissions AS Commission
INNER JOIN tlp_application_responses AS ApplicationResponse
ON ApplicationResponse.id = Commission.application_response_id
INNER JOIN tlp_applications AS Application
ON Application.id = ApplicationResponse.application_id
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
AND
ApplicationResponse.result = 'Accepted'
AND
Commission.seller_code = 44
GROUP BY
Application.tlp_aff_id
) AS JoinedProfit
ON JoinedProfit.tlp_aff_id = Conversion.aff_id
INNER JOIN
(
SELECT
Application.tlp_aff_id,
ApplicationResponse.application_id,
Commission.application_response_id,
Commission.created,
SUM(Commission.amount) AS total_revenue
FROM
tlp_commissions AS Commission
INNER JOIN tlp_application_responses AS ApplicationResponse
ON ApplicationResponse.id = Commission.application_response_id
INNER JOIN tlp_applications AS Application
ON Application.id = ApplicationResponse.application_id
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
GROUP BY
Application.tlp_aff_id
) AS JoinedRevenue
ON JoinedRevenue.tlp_aff_id = Conversion.aff_id
WHERE
Conversion.conversion_time >= '2022-10-03 00:00:00'
AND
Conversion.conversion_time <= '2022-10-03 23:59:59'
AND
Conversion.aff_id IS NOT NULL
GROUP BY
Conversion.aff_id
I was hoping I could just do another SQL join that loops over each returned result in my query and appends total_revenue based on the row column of each?
INNER JOIN
(
SELECT SUM(JoinedProfit.total_profit + JoinedCommission.total_commission) AS total_revenue
) AS JoinedRevenue
But this isn't the right syntax here.

Related

MYSQL use GROUP BY to SUM timediff to get a total open time

I have multiple tables that I am having to join together in order to work out how long tickets have been open, I am using the following query (convoluted I know!):
SELECT DISTINCT u_db.environments.name AS Env_name, TIMEDIFF(u_db.tickets.close_date, u_db.tickets.created_date) AS Total_open_time
FROM u_db.tickets
INNER JOIN u_db.ticket_units
ON u_db.tickets.id = u_db.ticket_units.ticket_id
INNER JOIN u_db.units
ON u_db.ticket_units.unit_id = u_db.units.id
INNER JOIN u_db.locations
ON u_db.units.location_id = u_db.locations.id
INNER JOIN u_db.location_groups
ON u_db.locations.locations_group_id = u_db.location_groups.id
INNER JOIN u_db.environments
ON u_db.location_groups.environment = u_db.environments.id
WHERE u_db.tickets.created_date >= '2021-09-01 00:00:00'
AND u_db.tickets.created_date < '2021-10-01 00:00:00'
AND u_db.location_groups.id IN (50,17,46,45,48,49)
AND u_db.tickets.id IN (132357,132361,132372,132473);
Note: the close_date and created_date are stored as TIMESTAMP.
This generates the following output:
Env_name Total_open_time
GA 27:38:59
GA 01:43:51
GR 04:32:58
GR 49:39:19
However, I would like to group by Env_name and SUM the Total_open_times for each group, so my desired output is:
Env_name Total_open_time
GA 29:22:50
GR 54:12:17
I cannot seem to get the times to totals to sum when I group by Env_name, any suggestions on how to achieve this would be greatly appreciated!
Guess you can sum with difference in seconds, then convert seconds to time
SELECT u_db.environments.name AS Env_name, SEC_TO_TIME(SUM(TIMESTAMPDIFF(SECOND, u_db.tickets.created_date, u_db.tickets.close_date))) AS Total_open_time
FROM u_db.tickets
INNER JOIN u_db.ticket_units
ON u_db.tickets.id = u_db.ticket_units.ticket_id
INNER JOIN u_db.units
ON u_db.ticket_units.unit_id = u_db.units.id
INNER JOIN u_db.locations
ON u_db.units.location_id = u_db.locations.id
INNER JOIN u_db.location_groups
ON u_db.locations.locations_group_id = u_db.location_groups.id
INNER JOIN u_db.environments
ON u_db.location_groups.environment = u_db.environments.id
WHERE u_db.tickets.created_date >= '2021-09-01 00:00:00'
AND u_db.tickets.created_date < '2021-10-01 00:00:00'
AND u_db.location_groups.id IN (50,17,46,45,48,49)
AND u_db.tickets.id IN (132357,132361,132372,132473)
GROUP BY Env_name

Use results returned from select subquery in update query

I am having a query in which i need to update a table.
This is my select query:
SELECT so.fk_customer,
IF (((sum( lgg.fk_catalog_attribute_option_global_gender = 1 )/count( lgg.fk_catalog_attribute_option_global_gender )) *100 > 60) AND (c.gender='male'), 'Men',
IF (((sum( lgg.fk_catalog_attribute_option_global_gender = 2 )/count( lgg.fk_catalog_attribute_option_global_gender )) *100 > 60) AND (c.gender='female'), 'Women', '') ) as calculatedGender
FROM catalog_attribute_link_global_gender AS lgg
INNER JOIN catalog_simple AS cs ON cs.fk_catalog_config = lgg.fk_catalog_config
INNER JOIN sales_order_item AS soi ON soi.sku = cs.sku
INNER JOIN sales_order AS so ON soi.fk_sales_order = so.id_sales_order
INNER JOIN customer as c ON c.id_customer = so.fk_customer
WHERE lgg.fk_catalog_attribute_option_global_gender IN (1,2)
AND so.created_at BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
GROUP BY so.fk_customer
HAVING count( lgg.fk_catalog_attribute_option_global_gender ) > 2
I have another table in which there is a column fk_customer and column gender, I need to update that table with the results from above query. I need to do it in the same query. The above query is giving me perfect results.
You will have to merge the UPDATE statement with this query as:
UPDATE anotherTable A JOIN
(SELECT so.fk_customer,
IF (((sum( lgg.fk_catalog_attribute_option_global_gender = 1 ) / count(lgg.fk_catalog_attribute_option_global_gender )) *100 > 60)
AND (c.gender='male'), 'Men',
IF (((sum( lgg.fk_catalog_attribute_option_global_gender = 2 ) / count( lgg.fk_catalog_attribute_option_global_gender )) *100 > 60)
AND (c.gender='female'), 'Women', '') ) as calculatedGender,
'ID'
FROM catalog_attribute_link_global_gender AS lgg
INNER JOIN catalog_simple AS cs ON cs.fk_catalog_config = lgg.fk_catalog_config
INNER JOIN sales_order_item AS soi ON soi.sku = cs.sku
INNER JOIN sales_order AS so ON soi.fk_sales_order = so.id_sales_order
INNER JOIN customer as c ON c.id_customer = so.fk_customer
WHERE lgg.fk_catalog_attribute_option_global_gender IN (1,2)
AND so.created_at BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
GROUP BY so.fk_customer
HAVING count( lgg.fk_catalog_attribute_option_global_gender ) > 2) B
ON A.'ID' = B.'ID'
SET A.fk_customer = B.fk_customer,
A.gender = B.calculatedGender;
This shall give you the desired result provided you figure out the 'ID' column on which you will join both the tables A and B.

mysql query inner join on same table with different conditions

I have a table transactions and I trying to figure out our new customers in a given month. That means that if a customer didn't have a transaction in the time before the month he/she counts as a new customer.
I have figured out a way, but it is seriously inefficient and takes ages. I then came across this artikel which compares different methods. I have tried to adjust that approach to mine without success.
To visualise my problem:
|--------------------------- time period with all transactions -----------------------|
|----- period before month transactions = 0) ---|---- curr month transactions > 0 ----|
The table looks like this:
transactions
id, email, state, date_paid
My query:
SELECT
l.email
FROM
transactions as l
LEFT JOIN transactions as r ON r.email = l.email
WHERE
r.email IS NULL
AND l.state = 'paid'
AND r.state = 'paid'
AND l.date_paid <= '2013-12-31 23:59:59'
AND r.date_paid < '2013-12-01 00:00:00'
What am I doing wrong?
Try this:
SELECT l.email
FROM transactions AS l
LEFT JOIN transactions AS r ON r.email = l.email AND r.state = 'paid' AND r.date_paid < '2013-12-01 00:00:00'
WHERE r.email IS NULL AND l.state = 'paid' AND l.date_paid <= '2013-12-31 23:59:59'
try this:
SELECT l.email
FROM transactions l
WHERE NOT l.email IN (SELECT r.email
FROM transactions r
WHERE r.state = 'paid' AND r.date_paid < '2013-12-01 00:00:00')
AND l.state = 'paid' AND l.date_paid <= '2013-12-31 23:59:59'

Merging an SQL query from a third table to an existing query that counts from 2 different tables

I have a query that counts calls and failed calls from two different tables from Nov 11 to Nov 24, 2013 for each client.
SELECT d.id_client,
d.login,
Coalesce(c.total, 0) AS calls,
Coalesce(fc.total, 0) AS calls_failed
FROM api.clients d
LEFT OUTER JOIN (SELECT Count(*) AS total,
id_client
FROM voip.calls c
WHERE c.call_start >= '2013-11-11 00:00:00'
AND c.call_start < '2013-11-25 00:00:00'
GROUP BY id_client) c
ON d.id_client = c.id_client
LEFT OUTER JOIN (SELECT Count(*) AS total,
id_client
FROM voip.callsfailed c
WHERE c.call_start >= '2013-11-11 00:00:00'
AND c.call_start < '2013-11-25 00:00:00'
AND c.ie_error_number <> 0
GROUP BY id_client) fc
ON d.id_client = fc.id_client
WHERE d.id_client IN (SELECT e.idclient
FROM voip.invoiceclients e
WHERE e.clientnr = 'demo')
I have a separate query that provides client_balance, mobile_number, name for each client.
SELECT cr.id_client,
inv.taxid AS company,
inv.name,
inv.lastname,
inv.mobilephone,
cr.account_state
FROM clientsretail cr,
invoiceclients inv
WHERE cr.id_client = inv.idclient
AND inv.clientnr = 'demo'
ORDER BY inv.taxid,
inv.name;
How can I merge these queries to produce the following output:
id_client,Company,Name,Lastname,Mobilephone,Login,Calls,Failed,calls,Balance
I tried to take some baby steps with the following query, but failed:
SELECT d.id_client, d.login,
COALESCE(c.total, 0) AS calls, COALESCE(fc.total, 0) AS calls_failed
FROM api.clients d
LEFT OUTER JOIN
(
SELECT COUNT(*) AS total, id_client
FROM voip.calls c
WHERE c.call_start >= '2013-11-11 00:00:00'
AND c.call_start < '2013-11-25 00:00:00'
GROUP BY id_client
) c ON d.id_client = c.id_client
LEFT OUTER JOIN
(
SELECT COUNT(*) AS total, id_client
FROM voip.callsfailed c
WHERE c.call_start >= '2013-11-11 00:00:00'
AND c.call_start < '2013-11-25 00:00:00'
AND c.IE_error_number <> 0
GROUP BY id_client
) fc ON d.id_client = fc.id_client
LEFT OUTER JOIN
(
SELECT c.idclient,
c.taxid,
c.name,
c.lastname,
c.mobilephone
FROM voip.invoiceclients c
) v ON d.id_client=v.idclient
WHERE d.id_client IN
(
SELECT e.idclient
FROM voip.invoiceclients e
WHERE e.clientnr='demo'
)
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near
'FROM voip.invoiceclients c
) v ON d.id_client=v.idclient WHERE d.id_clie' at line 28
Your code is absolutely correct,There is a small syntax error only.Just remove the comma(',') after c.mobilephone in the select statement,your syntax error problem will be solved and query will run.

Help calculating average per day

The daily_average column is always returning zero. The default timestamp values are for the past week. Any thoughts on what I'm doing wrong here in getting the average order value per day?
SELECT
SUM(price+shipping_price) AS total_sales,
COUNT(id) AS total_orders,
AVG(price+shipping_price) AS order_total_average,
(SELECT
SUM(quantity)
FROM `order_product`
INNER JOIN `order` ON (
`order`.id = order_product.order_id AND
`order`.created >= '.$startTimestamp.' AND
`order`.created <= '.$endTimestamp.' AND
`order`.type_id = '.$type->getId().' AND
`order`.fraud = 0
)
) as total_units,
SUM(price+shipping_price)/DATEDIFF('.$endTimestamp.', '.$startTimestamp.') as daily_average
FROM `order`
WHERE created >= '.$startTimestamp.' AND
created <= '.$endTimestamp.' AND
fraud = 0 AND
type_id = '.$type->getId().'
You're using aggregate functions (SUM, COUNT, AVG) without an aggregate command (group by). I think your SQL is more complicated than it needs to be (no need for the inner select).
Here's a SQL command that should work (hard to test without test data ;))
SELECT
COUNT(id) total_orders,
SUM(finalprice) total_sales,
AVG(finalprice) order_average,
SUM(units) total_units,
SUM(finalprice)/DATEDIFF('.$endTimestamp.', '.$startTimestamp.') daily_average
FROM (
SELECT
o.id id,
o.price+o.shipping_price finalprice,
SUM(p.quantity) units
FROM order o INNER JOIN order_product p ON p.order_id=o.id
WHERE o.created>='.$startTimestamp.'
AND o.created<='.$endTimestamp.'
AND o.fraud=0
AND o.type_id='.$type->getId().'
GROUP BY p.order_id
) t;
Does casting one of the elements in the division work for you?
SELECT
SUM(price+shipping_price) AS total_sales,
COUNT(id) AS total_orders,
AVG(price+shipping_price) AS order_total_average,
(SELECT
SUM(quantity)
FROM `order_product`
INNER JOIN `order` ON (
`order`.id = order_product.order_id AND
`order`.created >= '.$startTimestamp.' AND
`order`.created <= '.$endTimestamp.' AND
`order`.type_id = '.$type->getId().' AND
`order`.fraud = 0
)
) as total_units,
CAST(SUM(price+shipping_price) AS float)/DATEDIFF('.$endTimestamp.', '.$startTimestamp.') as daily_average
FROM `order`
WHERE created >= '.$startTimestamp.' AND
created <= '.$endTimestamp.' AND
fraud = 0 AND
type_id = '.$type->getId().'