Sum values in two different tables and join results keeping the columns - sql

I have two tables: one with downtime and the other with productive time.
I want to have a table like this
But I am getting this
In the result, I am getting twice the downtime of the sum for the report 04102021-1, but as can be seen in the second picture, the value is present only once.
The script I am using is the following:
SELECT WAJ.REPORTCODE,--BASIC_REPORT_TABLE.TECHNICIAN,BASIC_REPORT_TABLE.JOBREPORTCODE,
SUM(CASE WHEN DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED) /3600.0
END) AS PRODUCTION_TIME,
SUM(CASE WHEN DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED) /3600.0
END) AS DOWNTIME
FROM WORK_AT_JOB WAJ,WAITING_AT_SITE WAS
WHERE (WAJ.REPORTCODE=WAS.REPORTCODE AND WAJ.REPORTCODE LIKE '04102021%') GROUP BY WAJ.REPORTCODE
After the #xQbert comment, I tried this:
SELECT WAS.REPORTCODE,
SUM(CASE WHEN DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAJ.TIMESTARTED,WAJ.TIMEFINISHED) /3600.0
END) AS PRODUCTION_TIME,
SUM(CASE WHEN DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED)<0
THEN (86400+DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED))/3600.0
ELSE DATEDIFF(SECOND,WAS.TIMESTARTED,WAS.TIMEFINISHED) /3600.0
END) AS DOWNTIME
FROM WAITING_AT_SITE WAS
JOIN WORK_AT_JOB WAJ
ON (WAJ.REPORTCODE=WAS.REPORTCODE AND WAS.REPORTCODE LIKE '04102021%') GROUP BY WAS.REPORTCODE
But I got the same result.
May you give some advice to get the result I want?
Thanks in advance

You could use conditional aggregation for this, but the easiest, and probably most performant, way to do this is to pre-aggregate the results before you join.
SELECT
waj.REPORTCODE
waj.PRODUCTION_TIME,
was.DOWNTIME
FROM (
SELECT
waj.REPORTCODE,
SUM(CASE WHEN v.diff < 0 THEN 86400 + v.diff ELSE v.diff END / 3600.0) AS PRODUCTION_TIME
FROM WORK_AT_JOB waj
CROSS APPLY (VALUES( DATEDIFF(SECOND, waj.TIMESTARTED, waj.TIMEFINISHED) )) v(diff)
WHERE waj.REPORTCODE LIKE '04102021%'
GROUP BY
waj.REPORTCODE
) waj
JOIN (
SELECT
was.REPORTCODE,
SUM(CASE WHEN v.diff < 0 THEN 86400 + v.diff ELSE v.diff END / 3600.0) AS PRODUCTION_TIME
FROM WAITING_AT_SITE was
CROSS APPLY (VALUES( DATEDIFF(SECOND, was.TIMESTARTED, was.TIMEFINISHED) )) v(diff)
WHERE was.REPORTCODE LIKE '04102021%'
GROUP BY
was.REPORTCODE
) was ON waj.REPORTCODE = was.REPORTCODE;
Note the use of CROSS APPLY (VALUES to avoid code repetition.

Related

SQL/Oracle issue, I receive this error " REPORTS.WEK_MTD_BILL_SUM_HIST_T.BILL_CYCLE: invalid identifier at line 48" , is there anyway to fix this?

enter image description here
above is the image of the code, my objective is to have 4 columns demonstrating the prices for a bill cycle, and 4 columns demonstrating the average of all bill cycles one year before the initial bill cycle.
the first inner select works by itself and adds the 4 columns to the right in the picture below, the problem starts when I want to add second inner select the 4 columns for yearly average. initially I didn't have the "select * from", but I couldn't find a better way
the base of the problem is that I can't implement 2 Where clauses on the same column.
below is the snap shot of what I get from running only the first inner select.
enter image description here
I have tried union , join, cross join ... but none seem to work, one person suggested "window function" but I haven't worked with that yet.
this is the error below:
enter image description here
RAW CODE TEXT IS BELOW:
VARIABLE START_DATE CHAR
EXEC :START_DATE :=''
VARIABLE END_DATE CHAR
EXEC :END_DATE=''
select * from
(SELECT
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BILL_CYCLE",
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" AS NET_TOTAL,
SUM(
SUM(
CASE WHEN "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BLI_TYPE" = 'Charges'
THEN "WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" END)) OVER() AS TOTAL_CHARGES,
SUM(
SUM(
CASE WHEN "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BLI_TYPE" = 'Credits'
THEN "WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" END)) OVER() AS TOTAL_CREDIT,
SUM(
SUM(
CASE WHEN "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BLI_TYPE" = 'Previous Weekly Billing Net Total'
THEN "WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" END)) OVER() AS PRV_INVOICED,
SUM(
SUM(
CASE WHEN "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BLI_TYPE" = 'Total Due / Receivable'
THEN "WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT"
ELSE 0 END)) OVER() AS Net_Due_Total
FROM "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"),
(SELECT
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BILL_CYCLE",
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" AS NET_TOTAL,
(SUM(
SUM(
CASE WHEN "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BLI_TYPE" = 'Total Due / Receivable' AND ( "REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BILL_CYCLE" BETWEEN '1-JAN-2022 4' AND :END_DATE)
THEN "WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" ELSE 0 END)) OVER()/COUNT(DISTINCT(BILL_CYCLE))) AS X
FROM
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"
WHERE
("REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BILL_CYCLE" BETWEEN :START_DATE AND :END_DATE )
)
GROUP BY
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."MTD_TOT" ,
"REPORTS"."WEK_MTD_BILL_SUM_HIST_T"."BILL_CYCLE"
;

ORACLE - How to count the total of two queries(with different WHERE clauses) from same table?

I want to capture the aggregate of each number but am not sure how to gather all the results from a couple of queries(All data from the same table) that have different 'WHERE' criteria.
For example,
1st query:
SELECT * FROM tbl
WHERE tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF')
AND tbl.send_date > TRUNC(SYSDATE) - 30
GROUP BY tbl.job_nbr;
2nd query:
SELECT * FROM tbl
WHERE tbl.from IN ('GGGB','GVCE','GMWA','GTYR')
AND tbl.to IN ('GGGBP2','GVCEP3','GMWAP1','GTYRP3')
AND tbl.send_date > TRUNC(SYSDATE) - 30
GROUP BY tbl.job_nbr;
I'm not sure if just using a WHERE clause satisfy my requirement
WHERE tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF')
AND tbl.from IN ('GGGB','GVCE','GMWA','GTYR')
AND tbl.to IN ('GGGBP2','GVCEP3','GMWAP1','GTYRP3')
Where I get confused is that results that are produced by my 1st query don't need to meet the 1st AND operator condition of the 2nd query. It seems if I incorporate all of my listed values in the 'tbl.from IN' condition and use the AND operator('tbl.to IN') that the returned results will only be those listed in the 'tbl.from IN' condition with the 'tbl.to IN' column.
Both queries produce separate results obviously, but I want to combine set of results so that I can count all occurrences, GROUP'ed BY unique job number(tbl.job_nbr).
*Note: I'm a little concerned about performance as well, because I want to incorporated in a larger query.
I'm sure its something simple, or at least somewhat simple, but I've spent a lot of time trying to figure this out. If anyone would be kind enough help me out, I would greatly appreciate it!
If I did not explain clearly enough or anyone has additional questions, I'll do my best to clarify.
I think you want conditional aggregation:
SELECT tbl.job_nbr,
SUM(CASE WHEN tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF')
THEN 1 ELSE 0
END) as cnt1,
SUM(CASE WHEN tbl.from IN ('GGGB', 'GVCE', 'GMWA', 'GTYR') AND
AND tbl.to IN ('GGGBP2', 'GVCEP3', 'GMWAP1', 'GTYRP3')
THEN 1 ELSE 0
END) as cnt2
FROM tbl
WHERE tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF')
AND tbl.send_date > TRUNC(SYSDATE) - 30
GROUP BY tbl.job_nbr;
use case when, i mean conditional aggregation:
select tbl.job_nbr,
sum(case when tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF') then 1 else 0 end)
as firstquery_count,
sum(case when tbl.to IN ('GGGBP2','GVCEP3','GMWAP1','GTYRP3' then 1 else 0 end)
as secondndtquery_count
from tbl where tbl.send_date > TRUNC(SYSDATE) - 30
group by tbl.job_nbr
from comments it seems you want add both count so you can use cte
with cte as
(
select tbl.job_nbr,
sum(case when tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF') then 1 else 0 end)
as firstquery_count,
sum(case when tbl.to IN ('GGGBP2','GVCEP3','GMWAP1','GTYRP3' then 1 else 0 end)
as secondndtquery_count
from tbl where tbl.send_date > TRUNC(SYSDATE) - 30
group by tbl.job_nbr
) select job_nbr,firstquery_count+secondndtquery_count as total
from cte
but with using cte you could do the same with 1st query just by using addition of that two count
From what I understand you need 1 counter, so combine the conditions with OR:
SELECT tbl.job_nbr, COUNT(*) counter FROM tbl
WHERE
tbl.send_date > TRUNC(SYSDATE) - 30
AND
(
tbl.from IN ('NIVA', 'TIRB', 'RIFG', 'PWDF')
OR (
tbl.from IN ('GGGB','GVCE','GMWA','GTYR')
AND
tbl.to IN ('GGGBP2','GVCEP3','GMWAP1','GTYRP3')
)
)
GROUP BY tbl.job_nbr;

compare two different date ranges sales in two columns

I want to compare two different date ranges sales in two columns.. I am using query below but its giving wrong sales.. please correct my query
select s1.Itm_cd,s1.Itm_Name,Sum(S1.amount),Sum(s2.amount)
from salestrans s1,salestrans s2
where s1.Itm_cd = S2.Itm_cd
and S1.Tran_dt between '20181101' and'20181130'
and S2.Tran_dt between '20171101' and '20171130'
group by s1.Itm_cd,s1.Itm_Name
Order by s1.Itm_cd
I suspect that you want conditional aggregation here:
WITH cte AS (
SELECT
s1.Itm_cd,
s1.Itm_Name,
SUM(CASE WHEN s1.Tran_dt BETWEEN '20181101' AND '20181130'
THEN s1.amount ELSE 0 END) AS sum_2018,
SUM(CASE WHEN s1.Tran_dt BETWEEN '20171101' AND '20171130'
THEN s1.amount ELSE 0 END) AS sum_2017
FROM salestrans s1
GROUP BY
s1.Itm_cd,
s1.Itm_Name
)
SELECT
Itm_cd,
Itm_Name,
sum_2018,
sum_2017,
CASE WHEN COALESCE(sum_2017, 0) <> 0
THEN FORMAT(100.0 * (sum_2018 - sum_2017) / sum_2017, 'N', 'en-us')
ELSE 'NA' END AS growth_pct
FROM cte
ORDER BY
Itm_cd;
Please try the following
select s1.Itm_cd,s1.Itm_Name,Sum(S1.amount),Sum(s2.amount)
from salestrans s1,salestrans s2
where s1.Itm_cd = S2.Itm_cd
and Convert(Varchar(10),S1.Tran_dt,112) between '20181101' and'20181130'
and Convert(Varchar(10),S2.Tran_dt,112) between '20171101' and '20171130'
group by s1.Itm_cd,s1.Itm_Name
Order by s1.Itm_cd
Here the logic is that in right side while comparision you are providing only date and not any separator and time. The same way should be applied to the column in left side for comparision.
if(Convert(Varchar(10), getdate(),112) = '20181224')
print 'Matched'
else
print 'Not Matched'
if(getdate() = '20181224')
print 'Matched'
else
print 'Not Matched'
Here the output is Matched for first and Not Matched because in first case both side same format has been taken for comparison.

Alternative approach to multiple left joins

I have the following queries to count sales by route
SELECT DISTINCT q.sales_route,
y.yesterday,
t.today
FROM tblquotesnew q
left join (SELECT tblquotesnew.sales_route,
Count(tblquotesnew.sales_route) AS Yesterday
FROM tblquotesnew
WHERE tblquotesnew.date_sent_to_registrations =
Trunc(SYSDATE - 1)
AND sales_route IS NOT NULL
GROUP BY tblquotesnew.sales_route) y
ON q.sales_route = y.sales_route
left join (SELECT tblquotesnew.sales_route,
Count(tblquotesnew.sales_route) AS Today
FROM tblquotesnew
WHERE tblquotesnew.date_sent_to_registrations =
Trunc(SYSDATE)
AND sales_route IS NOT NULL
GROUP BY tblquotesnew.sales_route) t
ON q.sales_route = t.sales_route
I then have 6 other left joins to count current and previous week, month, and year.
This approach does work, but I was wondering if this is a more efficient (in terms of lines of code) way of pulling together this data?
I think you just need conditional aggregation:
select q.sales_route,
sum(case when q.date_sent_to_registrations = trunc(SYSDATE - 1)
then 1 else 0
end) as yesterday,
sum(case when q.date_sent_to_registrations = trunc(SYSDATE)
then 1 else 0
end) as today
from tblquotesnew q
group by sales_route
You may use conditional aggregation
SELECT sales_route,
sum(CASE WHEN date_sent_to_registrations = Trunc(SYSDATE)
AND sales_route IS NOT NULL
THEN 1 ELSE 0 END) today,
sum(CASE WHEN date_sent_to_registrations = Trunc(SYSDATE - 1)
AND sales_route IS NOT NULL
THEN 1 ELSE 0 END) yesterday
FROM tblquotesnew
GROUP BY sales_route
conditional aggregation leads to one sequential scan of your table which may be ok in many cases. An alternative solution is to use subqueries behind SELECT which may be sometimes more efficient. For example, if you access small subselect of data and you can create indexes to support it.

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

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;