Get Sum of two columns having multiple pivot - sql

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]

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

SQL server pivot count function works incorrectly if used with inner query

I think there is a bug in my SQL-server pivot function because I can't find any other explanation.
I am running a simple pivot and using the exact syntax shown in MSDN.
but the pivot answer shows the exact same number for all columns with the value equivalence of the count of all weeks!!!
(left is query result and right is what I want)
here is my query:
SELECT
*
FROM (SELECT
r.cutomer_id
,c.[Week]
,r.id
FROM r
JOIN c
ON r.Create_date = c.Date
WHERE Is_ride = 1
AND ((Create_date_int BETWEEN 20190302 AND 20190319)
OR (Create_date_int BETWEEN 20190406 AND 20190426))) p
PIVOT
(
COUNT(id)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt
here are some test data representing the "p" output (i just changed id numbers, weeks are the same number you get from the query)
DROP TABLE IF EXISTS #t
CREATE TABLE #t (
customer_id INT
,WEEK INT
,id INT
)
INSERT #t (customer_id, WEEK, id)
VALUES (12032, 10, 8607)
, (43551, 10, 8721051)
, (55025, 10, 81200)
, (198874, 10, 861362)
, (99675, 10, 867081)
, (19387, 10, 863656)
, (12526, 10, 8603706)
, (19503, 10, 860924)
, (37597, 10, 860909)
, (136019, 10, 8610674);
so I thought there is something wrong with my query but then I changed the query to this:
SELECT
r.cutomer_id
,c.[Week]
,r.id INTO #t
FROM r
JOIN c
ON r.Create_date = c.Date
WHERE Is_ride = 1
AND ((Create_date_int BETWEEN 20190302 AND 20190319)
OR (Create_date_int BETWEEN 20190406 AND 20190426))
SELECT
*
FROM #t
PIVOT
(
COUNT(id)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt
and it works just fine!
also if I remove the r.id column from select and change it to count(week) it works fine!!!
also if I only change my where to
WHERE Is_ride = 1
AND ((Create_date_int BETWEEN 20190302 AND 20190319)
OR (Create_date_int BETWEEN 20190406 AND 20190426))
and passenger_id in (43551,12032,136019)
) p
it works fine!!!
can someone give me an explanation?
I have no idea why you're getting the result shown on your images. There's possibly some information missing.
Here's an MVCE with code commented to create a larger set of test data.
CREATE TABLE r(
id int identity,
customer_id int,
create_date date,
Create_date_int AS CONVERT( int, CONVERT( char(8), create_date, 112)),
is_ride bit)
INSERT INTO r(customer_id, create_date, is_ride)
SELECT customer_id, '20190307', 1
FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(customer_id)
--SELECT TOP 100000
-- ABS(CHECKSUM(NEWID())) % 10,
-- DATEADD( dd, ABS(CHECKSUM(NEWID())) % 120, '2019'),
-- 1
--FROM sys.all_columns a, sys.all_columns b;
CREATE TABLE c(
[Date] date,
[Week] AS DATEPART( wk, [Date])
)
INSERT INTO c([Date])
SELECT DATEADD( dd, ROW_NUMBER() OVER( ORDER BY (SELECT NULL))-1, '2019')
FROM sys.all_columns;
From here, I could run your original query that gives the expected results, just 1's in week 10.
I can also run an alternative query using cross tabs that is my preferred method to pivot data.
SELECT
r.customer_id
,COUNT( CASE WHEN c.[Week] = 9 THEN r.id END) AS wk9
,COUNT( CASE WHEN c.[Week] = 10 THEN r.id END) AS wk10
,COUNT( CASE WHEN c.[Week] = 11 THEN r.id END) AS wk11
,COUNT( CASE WHEN c.[Week] = 12 THEN r.id END) AS wk12
,COUNT( CASE WHEN c.[Week] = 14 THEN r.id END) AS wk14
,COUNT( CASE WHEN c.[Week] = 15 THEN r.id END) AS wk15
,COUNT( CASE WHEN c.[Week] = 16 THEN r.id END) AS wk16
,COUNT( CASE WHEN c.[Week] = 17 THEN r.id END) AS wk17
FROM r
JOIN c ON r.Create_date = c.Date
WHERE Is_ride = 1
AND (Create_date_int BETWEEN 20190302 AND 20190319
OR Create_date_int BETWEEN 20190406 AND 20190426)
GROUP BY r.customer_id
ORDER BY r.customer_id;
That also gives the correct information.
Both of these methods might benefit from preaggregation to improve their performance.
WITH ctePreAggregate AS(
SELECT
r.customer_id
,c.[Week]
,COUNT(*) AS weeklycount
FROM r
JOIN c
ON r.Create_date = c.Date
WHERE Is_ride = 1
AND (Create_date_int BETWEEN 20190302 AND 20190319
OR Create_date_int BETWEEN 20190406 AND 20190426)
GROUP BY r.customer_id
,c.[Week]
)
SELECT
*
FROM ctePreAggregate
PIVOT
(
SUM(weeklycount)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt
ORDER BY customer_id;
WITH ctePreAggregate AS(
SELECT
r.customer_id
,c.[Week]
,COUNT(*) AS weeklycount
FROM r
JOIN c
ON r.Create_date = c.Date
WHERE Is_ride = 1
AND (Create_date_int BETWEEN 20190302 AND 20190319
OR Create_date_int BETWEEN 20190406 AND 20190426)
GROUP BY r.customer_id
,c.[Week]
)
SELECT
customer_id
,SUM( CASE WHEN [Week] = 9 THEN weeklycount ELSE 0 END) AS wk9
,SUM( CASE WHEN [Week] = 10 THEN weeklycount ELSE 0 END) AS wk10
,SUM( CASE WHEN [Week] = 11 THEN weeklycount ELSE 0 END) AS wk11
,SUM( CASE WHEN [Week] = 12 THEN weeklycount ELSE 0 END) AS wk12
,SUM( CASE WHEN [Week] = 14 THEN weeklycount ELSE 0 END) AS wk14
,SUM( CASE WHEN [Week] = 15 THEN weeklycount ELSE 0 END) AS wk15
,SUM( CASE WHEN [Week] = 16 THEN weeklycount ELSE 0 END) AS wk16
,SUM( CASE WHEN [Week] = 17 THEN weeklycount ELSE 0 END) AS wk17
FROM ctePreAggregate
GROUP BY customer_id
ORDER BY customer_id;
The only issue is that you would need to add a series of ISNULL() in the column list to show NULLs instead of zeros for the PIVOT query.

Nested select and joins

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

issues in Case and when Statement

I have a question about Case and when statements. I have a list of two transtypeid like 10 and 12.
I tried to take sale1 amount like if the transtypeid 11 has a sum amount !=0 means, I need to minus the amount with sum amount of transtypeid 10
I tried a lot but nothing worked.
I have these queries I tried
select
CT.CustomerCode, C.CustomerName,
sale1 = case
when (ct.TransTypeID = 11) and (sum(ct.OVAmount - ct.OVDiscount) != 0)
then sum(ct.OVAmount - ct.OVDiscount) - sum(ct.OVAmount - ct.OVDiscount)
else 0
end,
C.CountryCode, C.CityCode
from
CustomerTransactions CT
inner join
Customers C ON CT.CustomerCode = C.CustomerCode
where
ct.TransDate >= '2015-01-01'
and ct.TransDate <= '2015-12-31'
and ct.TransTypeID in (10, 11)
group by
ct.CustomerCode, c.CustomerName, c.CountryCode, c.CityCode
Try calculate sale1 with this SQL code:
CASE WHEN
SUM(CASE WHEN ct.TransTypeID = 11
THEN ct.OVAmount - ct.OVDiscount
ELSE 0 END) != 0
THEN
SUM(CASE WHEN ct.TransTypeID = 11
THEN ct.OVAmount - ct.OVDiscount
ELSE O END)
- SUM(CASE WHEN ct.TransTypeID = 10
THEN ct.OVAmount - ct.OVDiscount
ELSE 0 END)
ELSE 0 END
I'm not sure that I understand what you need. But I give it a try since you are in hurry.
Something like this, maybe?
select
CT1.CustomerCode, C.CustomerName,
sale1 =
case
when ( sum(ct1.OVAmount - ct1.OVDiscount) != 0 )
then sum( ct1.OVAmount - ct1.OVDiscount ) - sum( ct2.OVAmount - ct2.OVDiscount )
else
0
end,
C.CountryCode, C.CityCode
from
Customers c
Inner join CustomerTransactions CT1 ON ( CT1.CustomerCode = C.CustomerCode ) And ( ct1.TransTypeID = 11 )
Inner join CustomerTransactions CT2 ON ( CT2.CustomerCode = C.CustomerCode ) And ( ct2.TransTypeID = 10 )
where
( ct1.TransDate >= '2015-01-01' )
and ( ct1.TransDate < '2016-01-01' )
and ( ct2.TransDate >= '2015-01-01' )
and ( ct2.TransDate < '2016-01-01' )
group by
ct1.CustomerCode,c.CustomerName,c.CountryCode,c.CityCode
Using CTEs:
with
cte10 ( CustomerId, amount ) as (
select
customerId, sum( amount ) as amount
from
CustomerTransaction
where
( Type = 1 )
group by CustomerId
),
cte11 ( CustomerId, amount ) as (
select
customerId, sum( amount ) as amount
from
CustomerTransaction
where
( Type = 2 )
group by CustomerId
)
select
c.Id, c.Description,
sale1 =
case
when ( cte10.amount <> 0 )
then cte10.amount - cte11.amount
else
0
end
from
Customer c
Inner join cte10 on ( cte10.CustomerId = C.id )
inner join cte11 on ( cte11.Customerid = C.id )

sql join and group by generated date range

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId
with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;
If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId