Nested select and joins - sql

I use the code below to calculate the required information from other tables. I used joins to display Names instead of IDs and to get required sums from other tables. I used COALESCE to convert null to Zero.
I had to used it again if I need to sum already COALESCED values, the above code is hard to understand and it's getting harder because I need to add more information, and this is just a small part of the main project so it will be really hard to work with it and will have many error and bugs.
Does it have to be so complicated? Or did I do it wrong? If it has to be complicated like this is there any replacement to get same results with easier way and code? Another RDBMS or anything else?
SELECT P.PROJ_ID, P.PROJ_STATUS, P.TYPE_ID, PT.TYPE_NAME, P.CLASS_ID, CLA.CLASS_NAME, P.PROJ_NO, P.PROJ_YEAR, P.PROJ_NAME, P.OLD_PROJ_NAME,
P.AGENCY_ID, A.AGENCY_NAME, P.CONTRACT_NO, P.CONTRACT_DATE, P.MINISTRY_ID, M.MINISTRY_NAME,
P.DIRECTORATE_ID, DIR.DIRECTORATE_NAME,
P.COST, P.ESTIMATED_COST, COALESCE(CO.ADDED_COSTS, 0) AS ADDED_COSTS, (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) AS TOTAL_COST,
P.ALLOCATION,
COALESCE(EPY.PAST_YEARS, 0) AS PAST_YEARS,
COALESCE(EF.PAST_MONTHS, 0) AS PAST_MONTHS,
COALESCE(ECM.CURRENT_MONTH, 0) AS CURRENT_MONTH,
COALESCE(ECY.CURRENT_YEAR, 0) AS CURRENT_YEAR,
COALESCE(E.TOTAL_EXPENSES, 0) AS TOTAL_EXPENSES,
COALESCE(CASH_EPY.CASH_PAST_YEARS, 0) AS CASH_PAST_YEARS,
COALESCE(CASH_EF.CASH_PAST_MONTHS, 0) AS CASH_PAST_MONTHS,
COALESCE(CASH_ECM.CASH_CURRENT_MONTH, 0) AS CASH_CURRENT_MONTH,
COALESCE(CASH_ECY.CASH_CURRENT_YEAR, 0) AS CASH_CURRENT_YEAR,
COALESCE(CASH_E.CASH_TOTAL_EXPENSES, 0) AS CASH_TOTAL_EXPENSES,
COALESCE(TOTAL_E.TOTAL_EXPENSES_CASH, 0) AS TOTAL_EXPENSES_CASH,
((COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) - COALESCE(E.TOTAL_EXPENSES, 0)) AS REMAINING,
P.DURATION, COALESCE(DU.ADDED_DURATIONS, 0) AS ADDED_DURATIONS,
(COALESCE(P.DURATION, 0) + COALESCE(DU.ADDED_DURATIONS, 0)) AS TOTAL_DURATION, P.START_DATE, P.FINISH_DATE,
P.GOVERNORATE_ID, G.GOVERNORATE_NAME, P.PROVINCE_ID, PR.PROVINCE_NAME, P.DISTRICT_ID, D.DISTRICT_NAME,
P.TOWN_ID, T.TOWN_NAME,
COALESCE( (E.TOTAL_EXPENSES / (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)))/100, 0) AS FINANCIAL_ACHIEVEMENT,
P.MATERIAL_ACHIEVEMENT, P.NOTES
FROM PROJECTS P
INNER JOIN PROJECTS_TYPES PT
ON P.TYPE_ID = PT.TYPE_ID
INNER JOIN CLASSES CLA
ON P.CLASS_ID = CLA.CLASS_ID
INNER JOIN AGENCIES A
ON P.AGENCY_ID = A.AGENCY_ID
LEFT JOIN MINISTRIES M
ON P.MINISTRY_ID = M.MINISTRY_ID
LEFT JOIN DIRECTORATES DIR
ON P.DIRECTORATE_ID = DIR.DIRECTORATE_ID
INNER JOIN GOVERNORATES G
ON P.GOVERNORATE_ID = G.GOVERNORATE_ID
LEFT JOIN PROVINCES PR
ON P.PROVINCE_ID = PR.PROVINCE_ID
LEFT JOIN DISTRICTS D
ON P.DISTRICT_ID = D.DISTRICT_ID
LEFT JOIN TOWNS T
ON P.TOWN_ID = T.TOWN_ID
-- ADDED COSTS
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) as ADDED_COSTS
FROM COSTS
GROUP BY PROJ_ID ) AS CO
ON P.PROJ_ID = CO.PROJ_ID
-- EXPENSES FROM PAST YEARS
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as PAST_YEARS
FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS EPY
ON P.PROJ_ID= EPY.PROJ_ID
-- EXPENSES FROM PAST MONTHS IN CUREENT YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as PAST_MONTHS
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS EF
ON P.PROJ_ID= EF.PROJ_ID
-- EXPENSES FROM CURRENT MONTH AND YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CURRENT_MONTH
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS ECM
ON P.PROJ_ID= ECM.PROJ_ID
-- SUM OF EXPENSES IN CURRENT YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CURRENT_YEAR
FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS ECY
ON P.PROJ_ID= ECY.PROJ_ID
-- TOTAL EXPENSES FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS E
ON P.PROJ_ID= E.PROJ_ID
-- CASH DEDUCTIONS SUMS
-- CASH DEDUCTIONS FROM PAST YEARS
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_PAST_YEARS
FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_EPY
ON P.PROJ_ID= CASH_EPY.PROJ_ID
-- CASH DEDUCTIONS FROM PAST MONTHS IN CUREENT YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_PAST_MONTHS
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_EF
ON P.PROJ_ID= CASH_EF.PROJ_ID
-- CASH DEDUCTIONS FROM CURRENT MONTH AND YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_CURRENT_MONTH
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_ECM
ON P.PROJ_ID= CASH_ECM.PROJ_ID
-- SUM OF CASH DEDUCTIONS IN CURRENT YEAR
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_CURRENT_YEAR
FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_ECY
ON P.PROJ_ID= CASH_ECY.PROJ_ID
-- TOTAL CASH DEDUCTIONS FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_E
ON P.PROJ_ID= CASH_E.PROJ_ID
-- TOTAL EXPENSES AND CASH DEDUCTIONS FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES_CASH
FROM EXPENSES
GROUP BY PROJ_ID ) AS TOTAL_E
ON P.PROJ_ID= TOTAL_E.PROJ_ID
-- ADDED DURATIONS
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) as ADDED_DURATIONS
FROM DURATIONS
GROUP BY PROJ_ID ) AS DU
ON P.PROJ_ID= DU.PROJ_ID
ORDER BY P.PROJ_YEAR, P.TYPE_ID, P.PROJ_NO

I think it would be good to start with getting down to just one subquery on the Expenses table. Looks like your coalesces in the sub-queries are just to replace nulls with 0 on 1 column - you could just run an UPDATE to fix that, but if not, I've included a way to do that just once in the example below.
You've got (for example):
-- TOTAL CASH DEDUCTIONS FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_E
ON P.PROJ_ID= CASH_E.PROJ_ID
-- TOTAL EXPENSES AND CASH DEDUCTIONS FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES_CASH
FROM EXPENSES
GROUP BY PROJ_ID ) AS TOTAL_E
ON P.PROJ_ID= TOTAL_E.PROJ_ID
You could do something like this instead:
with MY_EXPENSES AS
(
select COALESCE(TOTAL_VALUE, 0) as MY_TOTAL_VALUE, exp.*
from EXPENSES),
EXPENSES_SUMMARY AS
(
SELECT PROJ_ID,
sum(case when CASH_DEDUCTIONS = TRUE
then MY_TOTAL_VALUE else 0
end) as CASH_TOTAL_EXPENSES,
sum(MY_TOTAL_VALUE) as TOTAL_EXPENSES_CASH
FROM MY_EXPENSES
GROUP BY PROJ_ID )
<Big Query>
LEFT OUTER JOIN EXPENSES_SUMMARY es P.PROJ_ID= TOTAL_E.PROJ_ID
Basically take the conditions out of the where clauses in the sub-query and put them in a case statement within a sum - then you can have all 15 or however many it is items from that EXPENSES table in sub-query item. Only have to coalesce the TOTAL_VALUE once instead of in every sub-query.
You could also make a view that does the summary data.
Are there any proj_id's that don't exist at all in EXPENSES, DURATION, or COSTS? If not, then once you make the above change, you shouldn't have to do any COALESCE's in the main query. If so, then you probably still need to replace a few nulls with 0's - could do that client side if you just want to get the coalesce out of the query.
In general, I find the 'with . . . select' more readable for complex queries, but that may just be a preference thing.

This is the last code faster and cleaner, thanks to aduguid and dandarc
WITH
EXPENSES_SUMS
AS
(
SELECT
PROJ_ID
, SUM(CASE
WHEN EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
THEN TOTAL_VALUE
END) AS PAST_YEARS
, SUM(CASE
WHEN EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
THEN TOTAL_VALUE
END) AS PAST_MONTHS
, SUM(CASE
WHEN EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
THEN TOTAL_VALUE
END) AS CURRENT_MONTH
, SUM(CASE
WHEN EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
THEN TOTAL_VALUE
END) AS CURRENT_YEAR
, SUM(CASE
WHEN CASH_DEDUCTIONS = FALSE
THEN TOTAL_VALUE
END) AS TOTAL_EXPENSES
, SUM(CASE
WHEN EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
THEN TOTAL_VALUE
END) AS CASH_PAST_YEARS
, SUM(CASE
WHEN EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
THEN TOTAL_VALUE
END) AS CASH_PAST_MONTHS
, SUM(CASE
WHEN EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
THEN TOTAL_VALUE
END) AS CASH_CURRENT_MONTH
, SUM(CASE
WHEN EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
THEN TOTAL_VALUE
END) AS CASH_CURRENT_YEAR
, SUM(CASE
WHEN CASH_DEDUCTIONS = TRUE
THEN TOTAL_VALUE
END) AS CASH_TOTAL_EXPENSES
, SUM(TOTAL_VALUE) AS TOTAL_EXPENSES_CASH
--you can add more columns with your conditions here in case expressions
FROM
EXPENSES
GROUP BY
PROJ_ID
)
,
COSTS_SUMS
AS
(
SELECT
PROJ_ID
, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) AS ADDED_COSTS
--you can add more columns with your conditions here in case expressions
FROM
COSTS
GROUP BY
PROJ_ID
)
,
DURATIONS_SUMS
AS
(
SELECT
PROJ_ID
, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) AS ADDED_DURATIONS
--you can add more columns with your conditions here in case expressions
FROM
DURATIONS
GROUP BY
PROJ_ID
)
SELECT
P.PROJ_ID
, P.PROJ_STATUS
, P.TYPE_ID
, PT.TYPE_NAME
, P.CLASS_ID
, CLA.CLASS_NAME
, P.PROJ_NO
, P.PROJ_YEAR
, P.PROJ_NAME
, P.OLD_PROJ_NAME
, P.AGENCY_ID
, A.AGENCY_NAME
, P.CONTRACT_NO
, P.CONTRACT_DATE
, P.MINISTRY_ID
, M.MINISTRY_NAME
, P.DIRECTORATE_ID
, DIR.DIRECTORATE_NAME
, P.COST
, P.ESTIMATED_COST
, COALESCE(CO.ADDED_COSTS, 0) AS ADDED_COSTS
, (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) AS TOTAL_COST
, P.ALLOCATION
, COALESCE(ED.PAST_YEARS, 0) AS PAST_YEARS
, COALESCE(ED.PAST_MONTHS, 0) AS PAST_MONTHS
, COALESCE(ED.CURRENT_MONTH, 0) AS CURRENT_MONTH
, COALESCE(ED.CURRENT_YEAR, 0) AS CURRENT_YEAR
, COALESCE(ED.TOTAL_EXPENSES, 0) AS TOTAL_EXPENSES
, COALESCE(ED.CASH_PAST_YEARS, 0) AS CASH_PAST_YEARS
, COALESCE(ED.CASH_PAST_MONTHS, 0) AS CASH_PAST_MONTHS
, COALESCE(ED.CASH_CURRENT_MONTH, 0) AS CASH_CURRENT_MONTH
, COALESCE(ED.CASH_CURRENT_YEAR, 0) AS CASH_CURRENT_YEAR
, COALESCE(ED.CASH_TOTAL_EXPENSES, 0) AS CASH_TOTAL_EXPENSES
, COALESCE(ED.TOTAL_EXPENSES_CASH, 0) AS TOTAL_EXPENSES_CASH
, ((COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) - COALESCE(ED.TOTAL_EXPENSES, 0)) AS REMAINING
, P.DURATION
, COALESCE(DU.ADDED_DURATIONS, 0) AS ADDED_DURATIONS
, (COALESCE(P.DURATION, 0) + COALESCE(DU.ADDED_DURATIONS, 0)) AS TOTAL_DURATION
, P.START_DATE
, P.FINISH_DATE
, P.GOVERNORATE_ID
, G.GOVERNORATE_NAME
, P.PROVINCE_ID
, PR.PROVINCE_NAME
, P.DISTRICT_ID
, D.DISTRICT_NAME
, P.TOWN_ID
, T.TOWN_NAME
, COALESCE( (ED.TOTAL_EXPENSES / (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)))/100, 0) AS FINANCIAL_ACHIEVEMENT
, P.MATERIAL_ACHIEVEMENT
, P.NOTES
FROM
PROJECTS P
INNER JOIN PROJECTS_TYPES PT ON P.TYPE_ID = PT.TYPE_ID
INNER JOIN CLASSES CLA ON P.CLASS_ID = CLA.CLASS_ID
INNER JOIN AGENCIES A ON P.AGENCY_ID = A.AGENCY_ID
LEFT JOIN MINISTRIES M ON P.MINISTRY_ID = M.MINISTRY_ID
LEFT JOIN DIRECTORATES DIR ON P.DIRECTORATE_ID = DIR.DIRECTORATE_ID
INNER JOIN GOVERNORATES G ON P.GOVERNORATE_ID = G.GOVERNORATE_ID
LEFT JOIN PROVINCES PR ON P.PROVINCE_ID = PR.PROVINCE_ID
LEFT JOIN DISTRICTS D ON P.DISTRICT_ID = D.DISTRICT_ID
LEFT JOIN TOWNS T ON P.TOWN_ID = T.TOWN_ID
LEFT JOIN EXPENSES_SUMS ED ON P.PROJ_ID = ED.PROJ_ID
LEFT JOIN COSTS_SUMS CO ON P.PROJ_ID = CO.PROJ_ID
LEFT JOIN DURATIONS_SUMS DU ON P.PROJ_ID = CO.PROJ_ID
ORDER BY
P.PROJ_YEAR
, P.TYPE_ID
, P.PROJ_NO

Related

Why is a value not returned if another value is 0 in SQL query

I have a query that does not return the expense value if the drvalue(Debtor Value) is equal to 0 or NULL,
If i change the drvalue to any value greater than 0, query returns the expenses value.
Drvalue is a sum of all the values for a specific period
Below is the query
SELECT f.vehiclenumber,f.fleettype,f.IsCreditor,
ISNULL(SUM(l.DrValue),0) AS drvalue,
CASE WHEN iscreditor=1 then Sum (DrValue) - Sum(CrValue) else null end AS Profit,
sum(l.CrValue) AS CrValue ,
sum(l.distance) AS LoadDist,
sum(l.DrValue)/DDist AS DRVal ,
d.Liters AS Liters,d.ddist AS DDist, d.DDiesel AS DDiesel, d.DieselCost AS dieslCost,
(MAX(isnull(l.Closingkm,0))-MIN(isnull(l.OpeningKM,0))) AS [CO],ISNULL(SUM(l.DrValue),0) / CASE WHEN (isnull(MAX(l.Closingkm),0)-MIN(isnull(l.OpeningKM,0)))=0 THEN 1 ELSE (isnull(MAX(l.Closingkm),0)-MIN(isnull(l.OpeningKM,0))) END AS TotalCPK,
(d.DieselCost/sum(l.DrValue)) * 100 AS DieselPerc,
SUM(jobdetails.total) AS Expenses,
count(l.vehicleNo) AS LoadCount
FROM tblVehicle AS f
LEFT JOIN (SELECT Vehicleno, loaddate, DrValue, CrValue,Distance, OpeningKM, closingkm FROM tblloads WHERE DrValue IS NOT NULL) AS l ON f.VehicleNumber = l.VehicleNo AND l.loaddate >= '2020-06-01' and l.loaddate <= '2020-06-30'
LEFT JOIN (SELECT fleet , NULLIF(SUM(Liters),0) AS Liters,NULLIF(sum(distance),0) AS DDist, NULLIF(sum(distance)/ CASE WHEN sum(Liters)=0 THEN 1 ELSE sum(Liters) END,0) AS DDiesel, NULLIF(SUM (Manual_Amount),0) AS DieselCost FROM tblinput
WHERE [Date] >= '2020-06-01' And [Date] <= '2020-06-30'
GROUP BY Fleet) AS d ON l.VehicleNo = d.Fleet
LEFT JOIN (SELECT jd.fleet, SUM(Total) AS total From tblJobDetails jd, tbljobcards WHERE tbljobcards.JobID = jd.jobid AND jobdate >= '2020-06-01' AND jobdate <= '2020-06-30' GROUP BY jd.fleet) AS jobdetails ON jobdetails.fleet = l.vehicleno
WHERE VehicleCategory <> 'T'
GROUP BY f.vehiclenumber,f.fleettype,f.IsCreditor,d.Liters,d.ddist, d.DDiesel,d.DieselCost
ORDER BY fleettype
RESULTS

Get Sum of two columns having multiple pivot

I have created SQL query which returns the total number of days in a month a person was present,now I want to add the total column and the 3 type of leaves column i.e. [Casual Leave], [Paid Leave], [Complimentary Leave].
In short I want to add the total column with [Casual Leave], [Paid Leave]and [Complimentary Leave].
My query is as below:
;WITH ToPivot
AS ( SELECT
time_tracker.date,
Users.FirstName + ' ' + Users.LastName AS username,
(CASE
WHEN
(
(datepart(hour, chk_in)) is null
OR (datepart(hour, chk_out)) is null
)
then
0
WHEN
(
(datepart(hour, chk_in)) >= 12
OR (datepart(hour, chk_out)) < 16
AND ((datepart(hour, chk_out)) - (datepart(hour, chk_in))) < 6
)
THEN
0.5
ELSE
1
END) AS late, TypeOfLeaves.leave_type, Userleavetyp.no_of_days
FROM Users
INNER JOIN time_tracker ON Users.ID = time_tracker.fk_userid
INNER JOIN Userleavetyp ON Users.ID = Userleavetyp.fk_user
INNER JOIN TypeOfLeaves ON Userleavetyp.fk_tol = TypeOfLeaves.ID
WHERE (Users.FK_Status = 1)
),
LateTotals AS (
SELECT T.username, Total = SUM(late),T.leave_type,T.no_of_days
FROM ToPivot AS T
WHERE T.date BETWEEN '2018-07-01' AND '2018-07-31' GROUP BY T.username,T.leave_type,T.no_of_days
)
SELECT distinct(pv2.username) as original ,PV2.*,L.Total
FROM ToPivot AS P
PIVOT (SUM(late) FOR date IN ("2018-07-01", "2018-07-02", "2018-07-03", "2018-07-04", "2018-07-05", "2018-07-06", "2018-07-07", "2018-07-08", "2018-07-09", "2018-07-10", "2018-07-11", "2018-07-12", "2018-07-13", "2018-07-14", "2018-07-15", "2018-07-16", "2018-07-17", "2018-07-18", "2018-07-19", "2018-07-20", "2018-07-21", "2018-07-22", "2018-07-23", "2018-07-24", "2018-07-25", "2018-07-26", "2018-07-27", "2018-07-28", "2018-07-29", "2018-07-30", "2018-07-31")) AS pv1
PIVOT (SUM(no_of_days) FOR leave_type IN ([Casual Leave], [Paid Leave], [Complimentary Leave])) AS pv2
LEFT JOIN LateTotals AS L ON L.username = pv2.username
The output for this code is as below:
I believe you just need to change your select statement as follows.
From this:
SELECT distinct(pv2.username) as original ,PV2.*,L.Total
To this:
SELECT distinct(pv2.username) as original ,PV2.*,L.Total,
CASE WHEN PV2.[Casual Leave] IS NULL THEN 0 ELSE PV2.[Casual Leave] +
CASE WHEN PV2.[Paid Leave] IS NULL THEN 0 ELSE PV2.[Paid Leave] +
CASE WHEN PV2.[Complimentary Leave] IS NULL THEN 0 ELSE PV2.[Complimentary Leave]
AS [Total Leave]

Cross Join Generating Series of Dates

I am trying to count the number of tenants that renew in a month, based off of a "dtleasefrom" (lease start date). However, I want to attribute each renewal back to the month that the last lease ended, and tie it to a generated series of dates that can be used to pivot off of. However, I cannot seem to figure out why the unit_month_date doesn't equal the dtleaseto date. See pictures and code below.
Select
ten.scode as leasename
,th.sevent as event_type
,th.dtoccurred as date_occurred
,unit.scode as unit
,p.scode as property
,th.dtapply as apply_date
,th.dtapprove as approve_date
,th.dtsigndate as sign_date
,th.dtmovein as move_in_date
,th.dtmoveout as move_out_date
,th.dtleasefrom as lease_from
,th.dtleaseto as lease_to
,th.dtnotice as notice_date
,th.crent as rent
,att.subgroup2 as zone
,(ten.sfirstname || ' ' || ten.slastname) as tenant_name
,pm.property_manager as property_manager
,(case when date_trunc('month',th2.b) = dd.month::date and th2.row_numba = 1 and th.sevent = 'Lease Signed' then 1 else 0 end) as Leases_Ending
,dd.month::date as unit_month
,(case when date_trunc('month',th2.b) = dd.month::date and EXTRACT(day from age(th.dtleasefrom,th2.b)) <= 60 and EXTRACT(day from age(th.dtleasefrom,th2.b)) >= 0 and th2.row_numba = 1 and th.sevent = 'Lease Renewal' then 1 else 0 end) as Renewals
,(case when date_trunc('month',th2.b) = dd.month::date and EXTRACT(day from age(th.dtleasefrom,th2.b)) <= 60 and th2.row_numba = 1 and th.sevent = 'Lease Renewal' then 1 else 0 end) as Renewals_All_In
,(case when th.istatus not in (1,2) and date_trunc('month',th2.b) < dd.month::date and th.sevent = 'Lease Signed' then 1 else 0 end) as MTM_tenant
,th2.row_numba
,th.hmy
FROM
yardi.tenant ten
JOIN yardi.Tenant_History th on ten.hmyperson = th.htent
CROSS JOIN (SELECT generate_series('05-01-2017'::date,'01-01-2020','1 month') as month) dd
JOIN (SELECT th.htent as a , date_trunc('month',th.dtleaseto) as b, Row_Number() over(partition by th.htent order by th.hmy) as Row_Numba FROM yardi.tenant_history th group by th.htent,th.dtleaseto,th.hmy ) th2 on th2.b = dd.month and th.htent = th2.a
JOIN yardi.unit on unit.hmy = th.hunit
JOIN yardi.property p on p.hmy = unit.hproperty
JOIN yardi.attributes att on att.hprop = p.hmy
JOIN yardi.propbut_property_management pm on pm.hcode = p.hmy
WHERE ten.istatus < 6 and th2.row_numba = 1

Oracle SQL, getting a value and using it in multiple case statements

I'm trying to figure out how to get a result from a case statement, then use that result in several other case statements throughout the query. It should work the way that I am getting the "TransactionDate", but also for "Period", "BudgetYear", "CalendarMonth", and "CalendarYear". The result of the case statement would be replacing where it has "aila.ACCOUNTING_DATE" in each of those statements. I was considering trying case statements inside of each case statement, but that would be extremely long and I was thinking there had to be a better way.
SELECT
r.TRANSACTION_ID,
CASE
WHEN TRANSACTION_TYPE = 'RECEIVE'
THEN r.PRIMARY_QUANTITY * l.UNIT_PRICE
WHEN TRANSACTION_TYPE = 'RETURN TO VENDOR'
THEN -1 * (r.PRIMARY_QUANTITY * l.UNIT_PRICE)
END AS "Total",
glcc.SEGMENT5 AS BinNumber,
CAST(SUBSTR(d.ATTRIBUTE2, -2) AS INT) AS LineNumber,
CASE
WHEN r.PRIMARY_QUANTITY * l.UNIT_PRICE = aila.AMOUNT
THEN aila.ACCOUNTING_DATE
ELSE (SELECT MAX(ACCOUNTING_DATE) FROM AP_INVOICE_LINES_ALL WHERE d.PO_DISTRIBUTION_ID = PO_DISTRIBUTION_ID)
END AS TransactionDate,
CASE
WHEN EXTRACT(MONTH FROM aila.ACCOUNTING_DATE) >= 9
THEN EXTRACT(MONTH FROM aila.ACCOUNTING_DATE) - 8
ELSE
EXTRACT(MONTH FROM aila.ACCOUNTING_DATE) + 4
END AS Period,
CASE
WHEN EXTRACT(MONTH FROM aila.ACCOUNTING_DATE) >= 9
THEN EXTRACT(YEAR FROM aila.ACCOUNTING_DATE) + 1
ELSE
EXTRACT(YEAR FROM aila.ACCOUNTING_DATE)
END AS BudgetYear,
EXTRACT(MONTH FROM aila.ACCOUNTING_DATE) AS CalendarMonth,
EXTRACT(YEAR FROM aila.ACCOUNTING_DATE) AS CalendarYear,
s.VENDOR_ID AS VendorId,
s.VENDOR_NAME AS VendorName,
h.SEGMENT1 AS PONumber,
l.LINE_NUM AS POLineNumber
FROM
RCV_TRANSACTIONS r
JOIN
PO_DISTRIBUTIONS_ALL d
ON r.PO_LINE_ID = d.PO_LINE_ID
JOIN
GL_CODE_COMBINATIONS glcc
ON d.CODE_COMBINATION_ID = glcc.CODE_COMBINATION_ID
LEFT OUTER JOIN
AP_SUPPLIERS s
ON s.VENDOR_ID = r.VENDOR_ID
LEFT OUTER JOIN
PO_HEADERS_ALL h
ON h.PO_HEADER_ID = d.PO_HEADER_ID
LEFT OUTER JOIN
PO.PO_LINES_ALL l
ON l.PO_LINE_ID = r.PO_LINE_ID
LEFT OUTER JOIN
AP_INVOICE_LINES_ALL aila
ON d.PO_DISTRIBUTION_ID = aila.PO_DISTRIBUTION_ID
AND r.PRIMARY_QUANTITY * l.UNIT_PRICE = aila.AMOUNT
WHERE
(TRANSACTION_TYPE = 'RECEIVE' or TRANSACTION_TYPE = 'RETURN TO VENDOR')
AND
(glcc.SEGMENT2 = '1070'
OR
glcc.SEGMENT2 = '1071');
You can put the joins and initial case expression in an inline view or CTE (common table expression, a.k.a subquery factoring), and then query that - you can then refer to the case expression's column alias:
WITH CTE AS (
SELECT
r.TRANSACTION_ID,
CASE
WHEN TRANSACTION_TYPE = 'RECEIVE'
THEN r.PRIMARY_QUANTITY * l.UNIT_PRICE
WHEN TRANSACTION_TYPE = 'RETURN TO VENDOR'
THEN -1 * (r.PRIMARY_QUANTITY * l.UNIT_PRICE)
END AS "Total",
glcc.SEGMENT5 AS BinNumber,
CAST(SUBSTR(d.ATTRIBUTE2, -2) AS INT) AS LineNumber,
CASE
WHEN r.PRIMARY_QUANTITY * l.UNIT_PRICE = aila.AMOUNT
THEN aila.ACCOUNTING_DATE
ELSE (SELECT MAX(ACCOUNTING_DATE) FROM AP_INVOICE_LINES_ALL WHERE d.PO_DISTRIBUTION_ID = PO_DISTRIBUTION_ID)
END AS TransactionDate,
s.VENDOR_ID AS VendorId,
s.VENDOR_NAME AS VendorName,
h.SEGMENT1 AS PONumber,
l.LINE_NUM AS POLineNumber
FROM
RCV_TRANSACTIONS r
JOIN
PO_DISTRIBUTIONS_ALL d
ON r.PO_LINE_ID = d.PO_LINE_ID
JOIN
GL_CODE_COMBINATIONS glcc
ON d.CODE_COMBINATION_ID = glcc.CODE_COMBINATION_ID
LEFT OUTER JOIN
AP_SUPPLIERS s
ON s.VENDOR_ID = r.VENDOR_ID
LEFT OUTER JOIN
PO_HEADERS_ALL h
ON h.PO_HEADER_ID = d.PO_HEADER_ID
LEFT OUTER JOIN
PO.PO_LINES_ALL l
ON l.PO_LINE_ID = r.PO_LINE_ID
LEFT OUTER JOIN
AP_INVOICE_LINES_ALL aila
ON d.PO_DISTRIBUTION_ID = aila.PO_DISTRIBUTION_ID
AND r.PRIMARY_QUANTITY * l.UNIT_PRICE = aila.AMOUNT
WHERE
(TRANSACTION_TYPE = 'RECEIVE' or TRANSACTION_TYPE = 'RETURN TO VENDOR')
AND
(glcc.SEGMENT2 = '1070'
OR
glcc.SEGMENT2 = '1071')
)
SELECT
TRANSACTION_ID,
"Total",
BinNumber,
LineNumber,
TransactionDate,
CASE
WHEN EXTRACT(MONTH FROM TransactionDate) >= 9
THEN EXTRACT(MONTH FROM TransactionDate) - 8
ELSE
EXTRACT(MONTH FROM TransactionDate) + 4
END AS Period,
CASE
WHEN EXTRACT(MONTH FROM TransactionDate) >= 9
THEN EXTRACT(YEAR FROM TransactionDateE) + 1
ELSE
EXTRACT(YEAR FROM TransactionDate)
END AS BudgetYear,
EXTRACT(MONTH FROM TransactionDate) AS CalendarMonth,
EXTRACT(YEAR FROM TransactionDate) AS CalendarYear,
VendorId,
VendorName,
PONumber,
POLineNumber
FROM
CTE;
So the CTE query is everything from your initial query minus the four expressions that referred to aila.ACCOUNTING_DATE. And the query against CTE gets all the columns from that and adds the expressions for those four, but now referring to TransactionDate instead.
Meaning this as a comment to Alex's answer, but comments don't allow proper formatting of code.
The following use of the CASE expression can be simplified somewhat. Instead of
CASE
WHEN TRANSACTION_TYPE = 'RECEIVE'
THEN r.PRIMARY_QUANTITY * l.UNIT_PRICE
WHEN TRANSACTION_TYPE = 'RETURN TO VENDOR'
THEN -1 * (r.PRIMARY_QUANTITY * l.UNIT_PRICE)
END AS "Total",
you can also write
CASE TRANSACTION_TYPE
WHEN 'RECEIVE' THEN r.PRIMARY_QUANTITY * l.UNIT_PRICE
WHEN 'RETURN TO VENDOR' THEN -1 * (r.PRIMARY_QUANTITY * l.UNIT_PRICE)
END AS "Total",
Alex used a "searched" case expression; what I show above is called a "simple" case expression. Oracle documentation: https://docs.oracle.com/cd/B28359_01/server.111/b28286/expressions004.htm

Calculate total business working days between two dates

select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
from the above code I am able to find total working days of employee between two dates.
workingdays
20
but now I want other column name total business days. I want to calculate total business days between two dates.
workingdays businessdays
20 21
how can i do this?
If you only want to exclude weekends then you can simply just exclude these using a conditional count by adding:
count(distinct case when datepart(weekday, getdate()) <= 5 then date end)
So your query becomes:
set datefirst 1;
select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays,
count(distinct case when datepart(weekday, getdate()) <= 5
then dateadd(d, 0, datediff(d, 0,checktime))
end) as weekdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
HOWEVER I would really recommend adding a calendar table to your database. It makes everything so easy, your query would become:
SELECT DaysWorked = COUNT(cio.Date),
WeekDaysWorked = COUNT(CASE WHEN c.IsWeekDay = 1 THEN cio.Date END),
WorkingDaysWorked = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN cio.Date END),
TotalDays = COUNT(*),
TotalWeekDays = COUNT(CASE WHEN c.IsWeekDay = 1 THEN 1 END),
TotalWorkingDays = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN 1 END)
FROM dbo.Calender AS c
LEFT JOIN
( SELECT DISTINCT
Date = CAST(CheckTime AS DATE)
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
) AS cio
ON c.Date = cio.Date
WHERE d.Date >= '2014-05-01'
AND d.Date <= '2014-05-30';
This way you can define public holidays, weekends, etc. It is so much more flexible than any other solution.
EDIT
I think I misunderstood your original criteria. This should work for you with no calendar table:
SET DATEFIRST 1;
DECLARE #StartDate DATE = '2014-05-01',
#EndDate DATE = '2014-05-30';
DECLARE #Workdays INT =
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
-(DATEDIFF(WEEK, #StartDate, #EndDate) * 2)
-(CASE WHEN DATEPART(WEEKDAY, #StartDate) = 7 THEN 1 ELSE 0 END)
-(CASE WHEN DATEPART(WEEKDAY, #EndDate) = 6 THEN 1 ELSE 0 END);
SELECT WorkingDays = COUNT(DISTINCT CAST(CheckTime AS DATE)),
BusinessDays = #Workdays
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
AND cio.CheckTime >= #StartDate
AND cio.CheckTime <= #EndDate;
following query calculate Fridays count between #FromDate and #ToDate variable
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2
Following query calculate Working day count and business day count between to date :
DECLARE #FromDate DATE = '2014-05-01',
#ToDate DATE = '2014-05-30'
SELECT COUNT(DISTINCT CAST(checktime AS Date)) as workingdays,
DATEDIFF(DAY,#FromDate,#ToDate) -
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2 AS BusinessDay
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>= #FromDate and checktime<=#ToDate