SELECT query take too much time to process - mysql

The following MySQL query take too much time. its take 24sec . and total records not more the 15000 each table please guide me for faster
Thanks
select c1.code,
( SELECT COALESCE(sum(i.total_amount),0)
FROM invoice as i
WHERE i.customer_code= c1.code
)-
( SELECT COALESCE(sum(p.amount),0)
FROM collection as p
where p.customer_code = c1.code
)-
( SELECT COALESCE(sum(CN.amount),0)
FROM cr_note as CN
where CN.customer_code= c1.code
) as rem_Balance
from customer as c1

you make it fast by replacing sub queries to queries with left joins like this:
WITH allInvoice AS (SELECT customer_code AS code, SUM(total_amount) AS amount FROM invoice GROUP BY customer_code),
allCollection AS (SELECT customer_code AS code, SUM(amount) AS amount FROM collection GROUP BY customer_code),
allNote AS (SELECT customer_code AS code, SUM(amount) AS amount FROM cr_note GROUP BY customer_code)
SELECT customer.code,
(COALESCE(allInvoice.amount) - COALESCE(allCollection.amount) - COALESCE(allNote.amount)) AS rem_Balance
FROM customer
LEFT JOIN allInvoice ON allInvoice.code = customer.code
LEFT JOIN allCollection ON allCollection.code = customer.code
LEFT JOIN allNote ON allNote.code = customer.code

Related

Select most recent record grouped by 3 columns

I am trying to return the price of the most recent record grouped by ItemNum and FeeSched, Customer can be eliminated. I am having trouble understanding how I can do that reasonably.
The issue is that I am joining about 5 tables containing hundreds of thousands of rows to end up with this result set. The initial query takes about a minute to run, and there has been some trouble with timeout errors in the past. Since this will run on a client's workstation, it may run even slower, and I have no access to modify server settings to increase memory / timeouts.
Here is my data:
Customer Price ItemNum FeeSched Date
5 70.75 01202 12 12-06-2017
5 70.80 01202 12 06-07-2016
5 70.80 01202 12 07-21-2017
5 70.80 01202 12 10-26-2016
5 82.63 02144 61 12-06-2017
5 84.46 02144 61 06-07-2016
5 84.46 02144 61 07-21-2017
5 84.46 02144 61 10-26-2016
I don't have access to create temporary tables, or views and there is no such thing as a #variable in C-tree, but in most ways it acts like MySql. I wanted to use something like GROUP BY ItemNum, FeeSched and select MAX(Date). The issue is that unless I put Price into the GROUP BY I get an error.
I could run the query again only selecting ItemNum, FeeSched, Date and then doing an INNER JOIN, but with the query taking a minute to run each time, it seems there is a better way that maybe I don't know.
Here is my query I am running, it isn't really that complicated of a query other than the amount of data it is processing. Final results are about 50,000 rows. I can't share much about the database structure as it is covered under an NDA.
SELECT DISTINCT
CustomerNum,
paid as Price,
ItemNum,
n.pdate as newest
from admin.fullproclog as f
INNER JOIN (
SELECT
id,
itemId,
MAX(TO_CHAR(pdate, 'MM-DD-YYYY')) as pdate
from admin.fullproclog
WHERE pdate > timestampadd(sql_tsi_year, -3, NOW())
group by id, itemId
) as n ON n.id = f.id AND n.itemId = f.itemId AND n.pdate = f.pdate
LEFT join (SELECT itemId AS linkid, ItemNum FROM admin.itemlist) AS codes ON codes.linkid = f.itemId AND ItemNum >0
INNER join (SELECT DISTINCT parent_id,
MAX(ins1.feesched) as CustomerNum
FROM admin.customers AS p
left join admin.feeschedule AS ins1
ON ins1.feescheduleid = p.primfeescheduleid
left join admin.group AS c1
ON c1.insid = ins1.feesched
WHERE status =1
GROUP BY parent_id)
AS ip ON ip.parent_id = f.parent_id
WHERE CustomerNum >0 AND ItemNum >0
UNION ALL
SELECT DISTINCT
CustomerNum,
secpaid as Price,
ItemNum,
n.pdate as newest
from admin.fullproclog as f
INNER JOIN (
SELECT
id,
itemId,
MAX(TO_CHAR(pdate, 'MM-DD-YYYY')) as pdate
from admin.fullproclog
WHERE pdate > timestampadd(sql_tsi_year, -3, NOW())
group by id, itemId
) as n ON n.id = f.id AND n.itemId = f.itemId AND n.pdate = f.pdate
LEFT join (SELECT itemId AS linkid, ItemNum FROM admin.itemlist) AS codes ON codes.linkid = f.itemId AND ItemNum >0
INNER join (SELECT DISTINCT parent_id,
MAX(ins1.feesched) as CustomerNum
FROM admin.customers AS p
left join admin.feeschedule AS ins1
ON ins1.feescheduleid = p.secfeescheduleid
left join admin.group AS c1
ON c1.insid = ins1.feesched
WHERE status =1
GROUP BY parent_id)
AS ip ON ip.parent_id = f.parent_id
WHERE CustomerNum >0 AND ItemNum >0
I feel it quite simple when I'd read the first three paragraphs, but I get a little confused when I've read the whole question.
Whatever you have done to get the data posted above, once you've got the data like that it's easy to retrive "the most recent record grouped by ItemNum and FeeSched".
How to:
Firstly, sort the whole result set by Date DESC.
Secondly, select fields you need from the sorted result set and group by ItemNum, FeeSched without any aggregation methods.
So, the query might be something like this:
SELECT t.Price, t.ItemNum, t.FeeSched, t.Date
FROM (SELECT * FROM table ORDER BY Date DESC) AS t
GROUP BY t.ItemNum, t.FeeSched;
How it works:
When your data is grouped and you select rows without aggregation methods, it will only return you the first row of each group. As you have sorted all rows before grouping, so the first row would exactly be "the most recent record".
Contact me if you got any problems or errors with this approach.
You can also try like this:
Select Price, ItemNum, FeeSched, Date from table where Date IN (Select MAX(Date) from table group by ItemNum, FeeSched,Customer);
Internal sql query return maximum date group by ItemNum and FeeSched and IN statement fetch only the records with maximum date.

Use aggregate values for farther calculation

I have the followin query:
SELECT contracts.id,
(SELECT sum(pos.sum_to_pay) FROM pos
where pos.contract_id=contracts.id and pos.is_draft=0) as paid,
(SELECT sum(acts.amount) FROM acts
where acts.contract_id=contracts.id) as acts_sum
from contracts
it works but i want to add another result field to_pay that should be calculated like acts_sum - paid = to_pay.
I'm trying to do it like this:
SELECT contracts.id,
(SELECT sum(pos.sum_to_pay) FROM pos
where pos.contract_id=contracts.id and pos.is_draft=0) as paid,
(SELECT sum(acts.amount) FROM acts
where acts.contract_id=contracts.id) as acts_sum,
(acts_sum - paid) as to_pay
from contracts
but I got the error Unknown column 'acts_sum'. How can i find to_pay value based on acts_sum and paid?
Do it with a subquery like this
SELECT acts_sum, paid, (acts_sum - paid) as to_pay FROM
(SELECT contracts.id,
(SELECT sum(pos.sum_to_pay) FROM pos
where pos.contract_id=contracts.id and pos.is_draft=0) as paid,
(SELECT sum(acts.amount) FROM acts
where acts.contract_id=contracts.id) as acts_sum,
from contracts ) subq
You could rewrite your query using joins, correlated sub queries sometimes considered as a costly solution
select c.id,
COALESCE(a.acts_sum,0),
COALESCE(p.paid,0),
COALESCE(a.acts_sum,0) - COALESCE(p.paid,0) as to_pay
from contracts c
left join (
SELECT contract_id,sum(sum_to_pay) paid
FROM pos
where is_draft=0
group by contract_id
) p on c.id = p.contract_id
left join (
SELECT contract_id,sum(amount) acts_sum
FROM acts
group by contract_id
) a on c.id = a.contract_id

MYSQL SUM of columns in joined table

I'm trying to do the following query:
SELECT
(
SELECT
COUNT(*)
FROM
pl_invoices
JOIN
pl_invoice_articles
ON
pl_invoices.invoice_id = pl_invoice_articles.invoice_article_invoice_id
WHERE
pl_invoices.invoice_amount_paid = SUM(pl_invoice_articles.invoice_article_price)
) as 'Aantal Betaald'
So: I need to know how many invoices are paid, but the only way to know that is to make the sum of all the invoice articles and compare that to the total amount paid.
This query does not work and I don't know what i'm doing wrong. Is there anyone who can help me?
Many thanks in advance!
Use having clause instead of where
SELECT cnt as 'Aantal Betaald' from (
SELECT COUNT(*) AS CNT, invoice_amount_paid FROM pl_invoices pl JOIN pl_invoice_articles pla
ON pl.invoice_id = pla.invoice_article_invoice_id
having pl.invoice_amount_paid = SUM(pla.invoice_article_price)
) tt ;
One solution is to this query to calculate the sum:
select
invoice_article_invoice_id,
SUM(invoice_article_price) as tot
from
pl_invoice_articles
group by
invoice_article_invoice_id
and join it as a subquery to the pl_invoices table:
select count(*)
from
pl_invoices inner join (
select
invoice_article_invoice_id,
SUM(invoice_article_price) as tot
from
pl_invoice_articles
group by
invoice_article_invoice_id
) s on pl_invoices.invoice_id = s.invoice_article_invoice_id
where
pl_invoices.invoice_amount_paid = s.tot

Join two MySQL SELECT statements containing one WHERE clause

How can I join the two select statements below to work as one statement? I would like the result to appear in one table. Thanks for your help.
First statement -
SELECT Account_ID, SUM(Profit_Loss) AS Starting_Balance
FROM client_ledger_history
WHERE Summary = 'Cash In'
GROUP BY Account_ID WITH ROLLUP
Second statement -
SELECT
client_ig_client_list.Account_ID,
client_ig_client_list.`Name`,
Share_Status,
Forex_Status,
Index_Status,
Share_Weighting,
Forex_Weighting,
Index_Weighting,
SUM(
client_ledger_history.Profit_Loss
) AS Current_Balance
FROM
client_ledger_history
LEFT JOIN client_ig_client_list ON client_ig_client_list.Account_ID = client_ledger_history.Account_ID
GROUP BY
Account_ID WITH ROLLUP
You should make a join into a nested table
SELECT
client_ig_client_list.Account_ID,
Starting_Balance,
client_ig_client_list.`Name`,
Share_Status,
Forex_Status,
Index_Status,
Share_Weighting,
Forex_Weighting,
Index_Weighting,
SUM(client_ledger_history.Profit_Loss) AS Current_Balance
FROM
client_ledger_history LEFT JOIN client_ig_client_list ON client_ig_client_list.Account_ID = client_ledger_history.Account_ID
LEFT JOIN
(SELECT Account_ID, SUM(Profit_Loss) AS Starting_Balance
FROM client_ledger_history WHERE Summary = 'Cash In' GROUP BY Account_ID WITH ROLLUP) as client_ledger_aggreagated_history
ON client_ledger_aggreagated_history.Account_ID = client_ledger_history.Account_ID
GROUP BY Account_ID WITH ROLLUP

SELECT a field from similar fields by MAX from another field

I have a table with three columns: Item, Quantity, and Date.
The values in the Item column may be duplicates, but the Quantity and Dates will be unique.
For example:
Item - Quantity - Date
Hammer - 3 - 1/12/15
Hammer - 7 - 5/18/15
Hammer - 6 - 8/1/15
Wrench - 8 - 2/24/15
Wrench - 3 - 6/10/15
I am trying to write a query that will only return:
Item - Quantity - Date
Hammer - 6 - 8/1/15
Wrench - 3 - 6/10/15
This is my code:
SELECT DISTINCT stock.stc_st AS Store, stock.art_st AS UPC, articles.descr AS Description, stock.quan_st AS Quantity, articles.rp AS Cost
FROM stock LEFT JOIN articles ON stock.art_st = articles.article
WHERE stock.ym_st =
(SELECT Max(stock.ym_st)
FROM stock t1
WHERE stock.art_st=t1.art_st
GROUP BY t1.art_st)
GROUP BY stock.stc_st, stock.art_st, articles.descr, stock.quan_st, articles.rp, articles.act, articles.stat
HAVING (((stock.stc_st)=[Which Store?]) AND ((articles.act)="Y") AND ((articles.stat)="Y"));
However, my code is returning all items when I only want it to return the items with the max date. If anyone could take a look at this and tell me what I am doing wrong, I would really appreciate it.
========================
Now I'm trying to use this code from the answers below and it's giving me a Syntax Error on JOIN on the Inner Join at tmaxdate.art_st. I'm sure this is something stupid like a parenthesis out of place. Could anyone more familiar with Access's SQL syntax tell me what I'm doing wrong? Thanks!
SELECT DISTINCT stock.stc_st AS Store, stock.art_st AS UPC, articles.descr AS Description, stock.quan_st AS Quantity, articles.rp AS Cost
FROM stock AS t1
INNER JOIN
(
SELECT tmaxdate.art_st, Max(tmaxdate.ym_st) AS MaxOfDate
FROM stock AS tmaxdate
GROUP BY tmaxdate.art_sc
) AS sub
ON (t1.ym_st = sub.MaxOfDate) AND (tmaxdate.art_st = sub.art_st)
LEFT JOIN articles ON stock.art_st = articles.article
GROUP BY stock.stc_st, stock.art_st, articles.descr, stock.quan_st, articles.rp, articles.act, articles.stat
HAVING (((stock.stc_st)=[Which Store?]) AND ((articles.act)="Y") AND ((articles.stat)="Y"));
I couldn't figure out how that sample data is distributed among your tables. So I stored those data in a table named YourTable.
First create a GROUP BY query to show you the most recent Date for each Item:
SELECT t1.Item, Max(t1.Date) AS MaxOfDate
FROM YourTable AS t1
GROUP BY t1.Item
Then you can use that as a subquery which you join back to the main table in order to select only its rows with the matching Item/Date pairs:
SELECT t2.Item, t2.Quantity, t2.Date
FROM
YourTable AS t2
INNER JOIN
(
SELECT t1.Item, Max(t1.Date) AS MaxOfDate
FROM YourTable AS t1
GROUP BY t1.Item
) AS sub
ON (t2.Date = sub.MaxOfDate) AND (t2.Item = sub.Item);
With your sample data in Access 2010, that query returns your requested output.
Since you don't actually have a single YourTable, you will need to adapt that approach for your actual tables, but this strategy should work there, too.
This has not been tested, but it is more along the lines of what you need.
It uses a subquery to find the maximum date for each primary key of stock table (I have assumed this is art_st )
SELECT stock.stc_st AS Store
, stock.art_st AS UPC
, articles.descr AS Description
, stock.quan_st AS Quantity
, articles.rp AS Cost
FROM ( stock
LEFT JOIN articles
ON stock.art_st = articles.article
)
INNER JOIN (SELECT t1.art_st, Max(stock.ym_st) AS t1MaxDate
FROM stock t1
GROUP BY t1.art_st
) AS TabMax
ON ( TabMax.art_st = stock.art_st
AND TabMax.t1MaxDate = stock.ym_st )
GROUP BY stock.stc_st
, stock.art_st
, articles.descr
, stock.quan_st
, articles.rp
, articles.act
, articles.stat
HAVING (((stock.stc_st)=[Which Store?]) AND ((articles.act)="Y") AND ((articles.stat)="Y"));