Firebird SQL: query slow due to coalesce or can it be rewritten - sql

i'm having some performance problems with a frequently used query.
SELECT
v.id,
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref in (1,3,4)), 0) "fv",
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref=2), 0) "ivo",
coalesce((SELECT sum(amount) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1 AND atype_ref=5), 0) "iio",
coalesce((SELECT sum(amount * mvalue) FROM artjournal WHERE variant_ref=v.id AND storage_ref=1), 0) "vw"
FROM productvariant v
since artjournal is a big table and gets thousands of new records each day the performance is getting terrible.
I have indices on all ID fields.
Is there a way to rewrite this statement to speed things up? Or can i use a different way to retrieve the data from the artjournal table and return 0 if result is null?
Thanks for your thoughts,
Christiaan

Looks like you want a filtered aggregate:
SELECT v.id,
sum(case when a.atype_ref in (1,3,4) then a.amount else 0 end) as "fv",
sum(case when a.atype_ref = 2 then a.amount else 0 end) as "ivo",
sum(case when a.atype_ref = 5 then a.amount else 0 end) as "iio",
sum(a.amount * a.mvalue) as "vw"
FROM productvariant v
LEFT JOIN artjournal a ON a.variant_ref = v.id
WHERE storage_ref = 1
GROUP BY v.id;

Related

query tuning for performance

I need to rewrite the below query for performance optimization.It is currently consuming > 50000 CPU seconds. I see the problem with group by cube. Can anyone suggest how to rewrite it
select
"platform", "subscriptions","rptg_dt","store_front_id","engaged_subscriptions",
"state_type","subscribers","qualified_subscribers","hardware_detail",
"engaged_subscribers","adam_id"
from (
select
'2020-03-16' as rptg_dt,
coalesce(abc.adam_id, 'ALL_ITEMS') as adam_id,
trim(coalesce(abc.store_front_id, 'ALL_ITEMS')) as store_front_id,
coalesce(abc.state_type, 'ALL_ITEMS') as state_type,
trim(coalesce(abc.hardware_detail, 'ALL_ITEMS')) as hardware_detail,
coalesce(abc.platform, 'ALL_ITEMS') as platform,
abc.subscriptions as subscriptions,
abc.subscribers as subscribers,
abc.qualified_subscribers as qualified_subscribers,
abc.engaged_subscribers as engaged_subscribers,
abc.engaged_subscriptions as engaged_subscriptions
from (
select store_front_id as store_front_id,
cast(cast(a.adam_id as integer) as varchar(12)) as adam_id,
case when subscn_type = 'Harmony' then 'Harmony Trial' else trim(state_type) end as state_type,
coalesce(hardware_detail, 'Unknown') as hardware_detail, --state_type,hardware_detail,platform
trim(platform_name) as platform,
count(distinct subscn_id) as subscriptions,
count(distinct acct_id) as subscribers,
count(distinct case when qualified_ind = 1 or subscn_owner_ind = 1 then acct_id end) as qualified_subscribers,
count(distinct case when engaged_ind = 1 then acct_id end) as engaged_subscribers,
count(distinct case when engaged_ind = 1 then subscn_id end) as engaged_subscriptions
from itsp_amr.atv_state_daily a
where calendar_type = 'F'
and adam_id in (1472441559,1478184786)
and a.rptg_dt = '2020-03-16'
and state_type <>'CHURNED'
group by cube /*(store_front_id,adam_id,state_type,hardware_detail,platform)*/ (1,2,3,4,5)
) abc
) mandela_temp

BigQuery: group counters by month after self-join

I have table that looks like this:
I'm trying to build a query, that will show specific partnerId counters groupped by keywordName and month.
To solve first part(without grouping by month), I've built this query:
SELECT keywordName, COUNT(keywordName) as total, IFNULL(b.ebay_count, 0) as ebay, IFNULL(c.amazon_count, 0) as amazon,
FROM LogFilesv2_Dataset.FR_Clickstats_v2 a
LEFT JOIN
(SELECT keywordName as kw , SUM(CASE WHEN partnerId='eBay' THEN 1 ELSE 0 END) as ebay_count
FROM LogFilesv2_Dataset.FR_Clickstats_v2
WHERE partnerId = 'eBay' GROUP BY kw) b
ON keywordName = b.kw
LEFT JOIN
(SELECT keywordName as kw , SUM(CASE WHEN partnerId='AmazonApi' THEN 1 ELSE 0 END) as amazon_count
FROM LogFilesv2_Dataset.FR_Clickstats_v2
WHERE partnerId = 'AmazonApi' GROUP BY kw) c
ON keywordName = c.kw
WHERE keywordName = 'flipper' -- just to filter out single kw.
GROUP BY keywordName, ebay, amazon
It works quite well and returns following output:
Now I'm trying to make additional group by month, but all my attempts returned incorrect results.
Final output supposed to be similar to this:
You can do this with conditional aggregation:
select
date_trunc(dt, month) dt,
keywordName,
count(*) total,
sum(case when partnerId = 'eBay' then 1 else 0 end) ebay,
sum(case when partnerId = 'AmazonApi' then 1 else 0 end) amazon
from LogFilesv2_Dataset.FR_Clickstats_v2
group by date_trun(dt, month), keywordName

Using result of a SQL case to compare and return another case

I'm trying to compare the result of a sum which returns an amount, by using a case that would return the result of this comparison:
SELECT u.name, u.account,
sum (case u.account
when t.sent_to then t.quantity * -1
else t.quantity end) as quantity_sent,
case when quantity_sent > u.available then 'YES'
else 'NO' end as analysis_result
from users as u inner join trans as t
on u.account in (t.sent_to, t.received_by)
group by u.name, u.account;
Whenever I call quantity_sent back, I get an "Invalid column name quantity_sent". Is there any other way I can use the result on this newly created column to compare inside a case?
Try this-
SELECT
u.name, u.account,
SUM (
CASE u.account
WHEN t.sent_to THEN t.quantity * -1
ELSE t.quantity
END
) AS quantity_sent,
CASE
WHEN SUM (
CASE u.account
WHEN t.sent_to then t.quantity * -1
ELSE t.quantity
END ) > u.available THEN 'YES'
ELSE 'NO'
END as analysis_result
FROM users as u
INNER JOIN trans as t
ON u.account in (t.sent_to, t.received_by)
GROUP BY u.name, u.account;
You need to repeat the expression or use a subquery:
select name, account, quantity_sent,
(case when quantity_sent > u.available then 'YES'
else 'NO'
end) as analysis_result
from (select u.name, u.account,
sum(case u.account
when t.sent_to then t.quantity * -1
else t.quantity
end) as quantity_sent
from users u inner join
trans t
on u.account in (t.sent_to, t.received_by)
group by u.name, u.account
) u;
This is part of the general rule in SQL that you cannot re-use a column alias in the SELECT where it is defined. There is a simple reason for this: SQL does not guarantee the order of evaluation of the expressions in the SELECT.

Return 0 when there is no row returned

I have this query which returns Null, to show 0 i can use ISNULL on outer query around CAST, i don't know if it is better to use ISNULL in the inner query.
I have tried using ISNULL with inner query but it returns no rows instead of showing 0. I have tried removing group by clause but still same results.
SELECT CAST((SUM(q.AssingWithPO * 1.0) / SUM(q.TotalAssign * 1.0)) * 100
AS NUMERIC (10,2))
FROM
(
SELECT COUNT(DISTINCT a.AssignmentID) AS TotalAssign
,(SELECT COUNT(DISTINCT a2.AssignmentID)FROM Assignments a2
WHERE a2.PODeliveryDate <> '19001212'
AND a2.AssignmentID = a.AssignmentID ) AS AssingWithPO
FROM Assignments a
WHERE a.StaffID = 59
AND (a.CreatedDate BETWEEN '20130101' AND '20141231')
GROUP BY a.AssignmentID
)q;
ADDED
I have simplified this query, thanks to #Gordon
SELECT SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*)) * 100 as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231';
Now would it be okay to use ISNULL like that?
ISNULL((SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*)) * 100 as AssignWithPO,0)
Execution Plan of both queries
You don't need the second level of subqueries. You can use conditional aggregation instead. I think the following will do what you want:
SELECT CAST((SUM(a.AssignWithPO * 1.0) / SUM(a.TotalAssign * 1.0)) * 100 as NUMERIC (10,2))
FROM (SELECT COUNT(*) AS TotalAssign,
SUM(case when a.PODeliveryDate <> '19001212' then 1 else 0 end) as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231'
GROUP BY a.AssignmentID
) a;
I'm not 100% sure, because I don't understand the relationships between AssignmentId, StaffId, and CreatedDate, but my assumption is that the rows counted for AssignWithPO are subject to the same conditions as the TotalCount.
You don't need the count(distinct) because AssignmentId is necessarily unique because of the group by. Assuming there is no overlap between the values, you don't need the group by either, nor the outer query:
SELECT COUNT(*) AS TotalAssign,
SUM(case when a.PODeliveryDate <> '19001212' then 1.0 else 0.0 end) / COUNT(*) as AssignWithPO
FROM Assignments a
WHERE a.StaffID = 59 AND
a.CreatedDate BETWEEN '20130101' AND '20141231';

"Timeout expired" error, when executing view in SQL Server 2008

I've written a query in SQL Server 2008. The query takes about 4 minutes to execute.
I need this query as a View. So, I've created a view with this query and when I try to execute the view creation script, it shows the following error:
Timeout Expired.
The timeout period elapsed prior to completion of the operation or the server is not responding.
The query is:
SELECT t.jrnno,
(SELECT SUM(t1.amount)
FROM dbo.T_sh AS t1
WHERE (t1.b_or_s = '1') AND (t1.jrnno = t.jrnno)) AS buy,
(SELECT SUM(t2.amount)
FROM dbo.T_sh AS t2
WHERE (t2.b_or_s = '2') AND (t2.jrnno = t.jrnno)) AS sale,
SUM(t.amount) AS Total,
SUM(t.h_crg) AS Howla,
SUM(t.l_crg) AS Laga,
SUM(t.taxamt) AS Tax,
SUM(t.commsn) AS Commission
FROM dbo.T_sh AS t
WHERE (t.tran_type = 'S')
AND (t.jrnno NOT IN (SELECT DISTINCT jrnno
FROM dbo.T_ledger))
GROUP BY t.jrnno
T_sh and T_ledger both tables have about 100K rows. What could be the possible reason and how can I overcome this?
Update:
select
t.jrnno,
SUM(CASE WHEN t.b_or_s = 1 THEN t.amount ELSE NULL END) buy,
SUM(CASE WHEN t.b_or_s = 2 THEN t.amount ELSE NULL END) sale,
SUM(t.amount) AS Total,
SUM(t.h_crg) AS Howla,
SUM(t.l_crg) AS Laga,
SUM(t.taxamt) AS Tax,
SUM(t.commsn) AS Commission
FROM
dbo.t_sh t
WHERE
t.tran_type = 'S'
AND NOT EXISTS(SELECT 1 FROM dbo.T_ledger x where x.jrnno = t.jrnno)
group by
t.jrnno
It solved my problem. Thanks everyone for your quick response.
Try this query:
select
t.jrno,
SUM(CASE WHEN t1.b_or_s = 1 THEN t.amount ELSE NULL END) buy,
SUM(CASE WHEN t1.b_or_s = 2 THEN t.amount ELSE NULL END) sale,
SUM(t.amount) AS Total,
SUM(t.h_crg) AS Howla,
SUM(t.l_crg) AS Laga,
SUM(t.taxamt) AS Tax,
SUM(t.commsn) AS Commission
FROM dbo.t_sh t
WHERE t.tran_type = 'S'
AND NOT EXISTS(SELECT 1 FROM dbo.T_ledger x x.jrno = t.jrno)
Your query only needs to scan dbo.T_sh once:
SELECT t.jrnno,
SUM(CASE WHEN t.b_or_s = 1 THEN t.amount ELSE NULL END) AS buy,
SUM(CASE WHEN t.b_or_s = 2 THEN t.amount ELSE NULL END) AS sale,
SUM(t.amount) AS Total,
SUM(t.h_crg) AS Howla,
SUM(t.l_crg) AS Laga,
SUM(t.taxamt) AS Tax,
SUM(t.commsn) AS Commission
FROM dbo.T_sh AS t
WHERE t.tran_type = 'S'
AND t.jrnno NOT IN (SELECT DISTINCT
tl.jrnno
FROM dbo.T_ledger tl)
GROUP BY t.jrnno