T-SQL AVG of multiple columns in a row - sql-server

I'm trying to select the average sales per person per territory out of the AdventureWorks database.
Since this is aggregating multiple columns in a row instead of multiple rows in a column, it seems like I'd need a sub-query, temp table, maybe a CTE, but I'm not sure how to identify which direction to take or how to write it.
Desired result:
| SalesTerritory | SalesPeople | 2011 | 2012 | 2013 | 2014 | AvgSales
+----------------+-------------+--------+---------+---------+---------+----------
| Australia | 1 | NULL | NULL | 184105 | 1237705 | [avg]
| Canada | 2 | 115360 | 3426082 | 2568323 | etc... | [avg]
Code:
SELECT
pvt.SalesTerritory,
COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]),
SUM(pvt.[2012]),
SUM(pvt.[2013]),
SUM(pvt.[2014])
--What's the best way to AVG the sales by year by sales person for each territory here?
FROM
(SELECT
st.[Name] AS [SalesTerritory],
soh.[SalesPersonID],
soh.[SubTotal],
YEAR(DATEADD(m, 6, soh.[OrderDate])) AS [FiscalYear]
FROM
[Sales].[SalesPerson] sp
INNER JOIN
[Sales].[SalesOrderHeader] soh ON sp.[BusinessEntityID] = soh.[SalesPersonID]
INNER JOIN
[Sales].[SalesTerritory] st ON sp.[TerritoryID] = st.[TerritoryID]
INNER JOIN
[HumanResources].[Employee] e ON soh.[SalesPersonID] = e.[BusinessEntityID]
INNER JOIN
[Person].[Person] p ON p.[BusinessEntityID] = sp.[BusinessEntityID]) AS soh
PIVOT
(SUM([SubTotal]) FOR [FiscalYear] IN ([2011], [2012], [2013], [2014])) AS pvt
GROUP BY
pvt.SalesTerritory

You have several options:
1) Use cross apply. Query would look like:
select
*
from
(
--put your query here
) t
cross apply (select AvgSales = avg(v) from (values ([2011]), ([2012]), ([2013]), ([2014])) q(v)) q
2) Count average by yourself
SELECT
pvt.SalesTerritory,
COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]),
SUM(pvt.[2012]),
SUM(pvt.[2013]),
SUM(pvt.[2014]),
ISNULL(SUM(pvt.[2011]), 0) + ISNULL(SUM(pvt.[2012]), 0)
+ ISNULL(SUM(pvt.[2013]), 0) + ISNULL(SUM(pvt.[2014]), 0)
/ CASE WHEN SUM(pvt.[2011]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2012]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2013]) > 0 THEN 1 ELSE 0 END
+ CASE WHEN SUM(pvt.[2014]) > 0 THEN 1 ELSE 0 END
FROM
...
GROUP BY
pvt.SalesTerritory

As I understand your question, you need the average of the yearly sales per salesperson, for each territory. Uzi's answer provides the average of the yearly sales for all salespersons together, for each territory. You could divide that result by the number of salespersons, or use a query like this:
SELECT pvt.SalesTerritory, COUNT(pvt.SalesPersonID) AS SalesPeople,
SUM(pvt.[2011]) AS [2011], SUM(pvt.[2012]) AS [2012],
SUM(pvt.[2013]) AS [2013], SUM(pvt.[2014]) AS [2014],
AVG(pvt.AvgSubTotal) AS AvgSubTotal
FROM (
SELECT y.SalesTerritory, y.SalesPersonID, y.FiscalYear, y.SubTotal,
AVG(y.SubTotal) OVER (PARTITION BY y.SalesTerritory) AS AvgSubTotal
FROM (
SELECT x.SalesTerritory, x.SalesPersonID, x.FiscalYear, SUM(x.SubTotal) AS SubTotal
FROM (
SELECT st.Name AS SalesTerritory, soh.SalesPersonID, soh.SubTotal,
YEAR(DATEADD(m, 6, soh.OrderDate)) AS FiscalYear
FROM Sales.SalesPerson sp
INNER JOIN Sales.SalesOrderHeader soh ON sp.BusinessEntityID = soh.SalesPersonID
INNER JOIN Sales.SalesTerritory st ON sp.TerritoryID = st.TerritoryID
INNER JOIN HumanResources.Employee e ON soh.SalesPersonID = e.BusinessEntityID
INNER JOIN Person.Person p ON p.BusinessEntityID = sp.BusinessEntityID
) x
GROUP BY x.SalesTerritory, x.SalesPersonID, x.FiscalYear
) y
) AS soh
PIVOT (SUM(SubTotal) FOR FiscalYear IN ([2011], [2012], [2013], [2014])) AS pvt
GROUP BY pvt.SalesTerritory;
The result is different for the Northwest territory, but I'm not sure which result you want.

Related

Join with sub query vs Join to table - why is join to table incorrect?

I have 2 queries;
1 with sub query join - this is pulling everything back correctly
1 with a join - this is pulling back an incorrect calculation in the GrossAnnualDebit, and overall is much lower than the GrossAnnualDebit figure from the sub query joins.
SELECT prty_id AS PropertyID,
ISNULL(SUM(tr.grs_val_trans), 0) + ISNULL(SUM(voi.grs_valtrs), 0) AS GrossAnnualDebit
FROM qlfdat..hgmprty1 p1
LEFT JOIN
(
SELECT prty_ref,
SUM(grs_val_trans) AS grs_val_trans
FROM qlfdat..hratrans
WHERE trans_ppyy BETWEEN 201805 AND 201904
AND trans_type = 'D'
GROUP BY prty_ref
) AS tr ON tr.prty_ref = p1.prty_id
LEFT JOIN
(
SELECT prty_ref,
SUM(grs_valtrs) AS grs_valtrs
FROM qlfdat..hraptvtt
WHERE trans_ppyy BETWEEN 201805 AND 201904
GROUP BY prty_ref
) AS voi ON voi.prty_ref = p1.prty_id
GROUP BY prty_id;
SELECT prty_id AS PropertyID,
ISNULL(SUM(tr.grs_val_trans), 0) + ISNULL(SUM(voi.grs_valtrs), 0) AS GrossAnnualDebit
FROM qlfdat..hgmprty1 p1
LEFT JOIN qlfdat..hratrans AS tr ON tr.prty_ref = p1.prty_id
AND tr.trans_type = 'D'
AND tr.trans_ppyy BETWEEN 201805 AND 201904
LEFT JOIN qlfdat..hraptvtt AS voi ON voi.prty_ref = p1.prty_id
AND voi.trans_ppyy BETWEEN 201805 AND 201904
AND voi.trans_ppyy = tr.trans_ppyy
GROUP BY prty_id;
I could tell exactly what's the issue without sample code, but a difference I could see is on second query, your voi table is no longer left join with p1 table, you left join it with tr table, that might cause your issue.

SAP B1 query using pivot for yearly comparison

Requirement: I want to compare the years(2018) sales and gross profit over last year(2017).
Solution: I have tried using the query below and I get the expected results.
Month | prevSales | prevGP | currentSales | currGP
Jan | 1234567.00| 1234567.00| 1234567.00 | 1234567.00
Feb | 1234567.00| 1234567.00| 1234567.00 | 1234567.00
Problem: The query took so long to produce results, it's almost one minute to display results.
SELECT P.[monName],
ISNULL([2017],0) as [prev],
ISNULL(P.[prevGP],0) [prevGP],
ISNULL([2018],0) as [curr],
ISNULL(P.[currGP],0) [currGP]
FROM (
SELECT LEFT(DATENAME(MONTH,T1.DocDate),3) [monName],
MONTH(T1.DocDate) [monNum],
ROUND((T1.Doctotal-T1.VatSum-T1.TotalExpns),0) AS [BAL],
(SELECT Sum(A.GrosProfit)
FROM OINV A
WHERE A.CANCELED='N' AND A.DocStatus='C' AND RIGHT(A.NumAtCard,9)<>'CANCELLED'
AND YEAR(A.DocDate)=YEAR(GETDATE())-1 AND MONTH(A.DocDate)=MONTH(T1.DocDate) ) [prevGP],
(SELECT SUM(B.GrosProfit)
FROM OINV B
WHERE B.CANCELED='N' AND B.DocStatus='C' AND RIGHT(B.NumAtCard,9)<>'CANCELLED'
AND YEAR(B.DocDate)=YEAR(GETDATE()) AND MONTH(B.DocDate)=MONTH(T1.DocDate) ) [currGP],
year(T1.Docdate) as [year]
FROM dbo.OCRD T0
LEFT JOIN dbo.OINV T1 ON T1.CardCode = T0.CardCode
Where RIGHT(T1.Numatcard,9)<>'CANCELLED' AND T1.CANCELED='N'
AND T0.[CardType] ='C' AND T1.DocStatus='C'
) S
PIVOT ( SUM(S.[BAL]) FOR [year] IN ([2017],[2018])) P
What could I possibly do to make the query efficient. I believe there is something to do with the pivot.
Thank you.
The problem with your view are the subqueries. If you want to improve the performance of your query you need to avoid the use of those subqueries.
For example, you can create an sql view that returns the information you need. Then you join your current query with that view and you remove the subqueries.
You could do it like this.
create view InvoiceInfoByMonth
as
select sum(GrosProfit) as GrosProfit, year(docdate) as DocYear, month(docdate) as DocMonth
from OINV
WHERE CANCELED='N' AND DocStatus='C' AND RIGHT(NumAtCard,9)<>'CANCELLED'
group by year(DocDate), month(DocDate)
GO
SELECT P.[monName],
ISNULL([2017],0) as [prev],
ISNULL(P.[prevGP],0) [prevGP],
ISNULL([2018],0) as [curr],
ISNULL(P.[currGP],0) [currGP]
FROM (
SELECT LEFT(DATENAME(MONTH,T1.DocDate),3) [monName],
MONTH(T1.DocDate) [monNum],
ROUND((T1.Doctotal-T1.VatSum-T1.TotalExpns),0) AS [BAL],
T2.GrosProfit as prevGP, T3.GrosProfit as currGP,
year(T1.Docdate) as [year]
FROM dbo.OCRD T0
LEFT JOIN dbo.OINV T1 ON T1.CardCode = T0.CardCode
left join InvoiceInfoByMonth T2 ON T2.DocYear = year(getdate())-1 and month(T1.DocDate) = T2.DocMonth
left join InvoiceInfoByMonth T3 ON T3.DocYear = year(getdate()) and month(T1.DocDate) = T3.DocMonth
Where RIGHT(T1.Numatcard,9)<>'CANCELLED' AND T1.CANCELED='N'
AND T0.[CardType] ='C' AND T1.DocStatus='C'
) S
PIVOT ( SUM(S.[BAL]) FOR [year] IN ([2017],[2018])) P
In my system, the query execution time improved from 1 minute to 1 second.

SELECT DISTINCT showing duplicate dates per customer email

I am trying to retrieve information for the past ten months, but am having a couple of errors. First, my query is getting data from as far back as 2013. Secondly, I am seeing duplicates in my results based on the PolEffDate field, like this:
EntityID | PolEffDate | EMail | CustNo | Producer | BusinessPhone
abcde-12345-fghij-67890 | 2013-09-24 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
abcde-12345-fghij-67890 | 2013-12-01 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
abcde-12345-fghij-67890 | 2014-09-24 | somewhere#email.com | 31000 | Bob Builder | 123-456-7890
Here is my SQL Query:
SELECT DISTINCT
CONVERT(VarChar(36), Customer.CustId) AS EntityID
, BasicPolInfo.PolEffDate, Customer.EMail, Customer.CustNo
, (isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,''))
AS Producer, Employee.BusFullPhone
FROM
Customer INNER JOIN BasicPolInfo ON Customer.CustId = BasicPolInfo.CustId INNER JOIN
Transaction ON BasicPolInfo.PolId = Transaction.PolId INNER JOIN
GeneralBranch ON Customer.GLBrnchCode = GeneralBranch.GLBrnchCode INNER JOIN
GeneralDepartment ON Customer.GLDeptCode = GeneralDepartment.GLDeptCode INNER JOIN
GeneralDivision ON Customer.GLDivCode = GeneralDivision.GLDivCode INNER JOIN
Employee ON BasicPolInfo.ExecCode = Employee.EmpCode
WHERE
BasicPolInfo.PolExpDate >= DATEADD(MONTH, -10,CONVERT(VARCHAR(11),GETDATE(),106))
AND BasicPolInfo.PolExpDate <= CONVERT(VARCHAR(11),GETDATE(),106)
AND Customer.Active = 'Y'
AND Customer.typeCust = 'P'
Thank you for the help. I will try my best to answer any questions.
Daniel, the duplication you are seeing is caused because you have multiple records in BasicPolInfo for each CustID value. You can confirm this by running the following query:
SELECT CustID, COUNT(*)
FROM BasicPolInfo
GROUP BY CustID
HAVING COUNT(*) > 1
Depending on your schema, this may not be an issue - after all, there is probably a perfectly legitimate reason for that! Multiple policies per Customer is my guess.
To resolve the duplication, I would recommend a GROUP BY with MIN() or MAX().
Your other issue, that of retrieving data from earlier dates, is because you are selecting the PolEffDate (presumably, policy effective date), but filtering the PolExpDate (presumably, policy expiration date). Which are you intending to use? Policies that have finished sometime in the last ten months could have started much earlier than that.
To resolve the wider date range, reference the same value in your SELECT and WHERE clauses.
Query below (using MAX() and PolExpDate):
SELECT
CONVERT(VarChar(36), Customer.CustId) AS EntityID,
MAX(BasicPolInfo.PolExpDate) AS PolExpDate, -- note that this is now PolExpDate
Customer.EMail,
Customer.CustNo,
(isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,'')) AS Producer,
Employee.BusFullPhone
FROM
Customer INNER JOIN
BasicPolInfo ON Customer.CustId = BasicPolInfo.CustId INNER JOIN
[Transaction] ON BasicPolInfo.PolId = [Transaction].PolId INNER JOIN
GeneralBranch ON Customer.GLBrnchCode = GeneralBranch.GLBrnchCode INNER JOIN
GeneralDepartment ON Customer.GLDeptCode = GeneralDepartment.GLDeptCode INNER JOIN
GeneralDivision ON Customer.GLDivCode = GeneralDivision.GLDivCode INNER JOIN
Employee ON BasicPolInfo.ExecCode = Employee.EmpCode
WHERE
BasicPolInfo.PolExpDate >= DATEADD(MONTH, -10,CONVERT(VARCHAR(11),GETDATE(),106))
AND BasicPolInfo.PolExpDate <= CONVERT(VARCHAR(11),GETDATE(),106)
AND Customer.Active = 'Y'
AND Customer.typeCust = 'P'
GROUP BY
CONVERT(VarChar(36), Customer.CustId),
Customer.EMail,
Customer.CustNo,
(isnull(Employee.Firstname + ' ','') + isnull(Employee.LastName,'')),
Employee.BusFullPhone

SQL - Converting static pivot into dynamic pivot

I have the following static pivot that works:
SELECT *
FROM
(
SELECT
d.CODE,
a.ANAPARA - Isnull(b.ANAPARA,0) AS ANAPARA,
d.NAME_,
c.DUEDATE,
a.TAKSIT - Isnull(b.TAKSIT,0) AS TAKSIT,
e.CURCODE
FROM
(SELECT
PARENTREF,
SUM(TOTAL) AS ANAPARA,
SUM(INTTOTAL) AS FAIZ,
SUM(BSMVTOTAL) AS BSMV,
SUM(KKDFTOTAL) AS KKDF,
SUM(TOTAL+INTTOTAL+BSMVTOTAL+KKDFTOTAL) AS TAKSIT
FROM LG_011_BNCREPAYTR
WHERE TRANSTYPE = 0
GROUP BY PARENTREF) a
LEFT OUTER JOIN (SELECT
PARENTREF,
SUM(TOTAL) AS ANAPARA,
SUM(INTTOTAL) AS FAIZ,
SUM(BSMVTOTAL) AS BSMV,
SUM(KKDFTOTAL) AS KKDF,
SUM(TOTAL+INTTOTAL+BSMVTOTAL+KKDFTOTAL) AS TAKSIT
FROM LG_011_BNCREPAYTR
WHERE TRANSTYPE = 1
GROUP BY PARENTREF) b
ON a.PARENTREF = b.PARENTREF
INNER JOIN (SELECT
PARENTREF,
CREDITREF,
DUEDATE,
OPRDATE
FROM LG_011_BNCREPAYTR) c
ON a.PARENTREF=c.PARENTREF
INNER JOIN LG_011_BNCREDITCARD d
ON c.CREDITREF=d.LOGICALREF
INNER JOIN L_CURRENCYLIST e
ON d.TRCURR=e.CURTYPE OR (d.TRCURR=0 AND e.CURTYPE=160)
WHERE e.FIRMNR=11
) x
PIVOT
(
SUM(TAKSIT)
FOR CURCODE IN ([USD],[EUR],[YEN])
) xx
It creates the following table:
CODE ANAPARA NAME_ DUEDATE EUR USD YEN
001 103.35 CAR LOAN 2015-01-01 NULL 150.01 NULL
002 106.89 CONSUMER LOAN 2015-03-10 190.35 NULL NULL
003 110.44 CAR LOAN 2015-04-20 NULL NULL 200.51
004 103.22 CAR LOAN 2015-05-04 150.02 NULL NULL
However, I would like to convert it into a dynamic pivot. Hence, I would like to change the code so that it gets the distinct currency codes from e.CURCODE and produces the same pivot table.
THANKS A LOT.

To find percentage compliance using T-SQL

I'm not an expert in T-SQL so here I'm trying to find the % compliance for flu vaccine ,TB test and resiprator test by supervisor for medical staffs. Each employee has a supervisor name linked to their employee info. The below code works fine and it's giving me the % for the above tests. The problem is that I want to get the ID, Name and Department by Supervisor and the % compliance.
The expected output is like this:
Supervisor ID NAME Dept %Flu %TB %FIT
Elaine Jong 98% 100% 52%
001 MARY SURGERY
002 SUSAN SURGERY
James Ande 100% 98% 78%
267 JIM INPATIENT
789 SAM INPATIENT
Current OUTPUT
%Flu %TB %FIT
Elaine Jong 98% 100% 52%
James Ande 100% 98% 78%
And the Query:
SELECT E.FLDSUPRNAME AS Supervisor,
1.0*SUM(
CASE WHEN I.FLDDATE IS NULL
THEN 0 ELSE 1
END)/SUM(1) AS Percent_Flu_Compliant,
1.0*SUM(
CASE WHEN F.FLDDATE IS NULL OR (F.FLDDATE+365) < GETDATE()
THEN 0 ELSE 1
END) / SUM(1)
AS Percent_Fit_Compliant,
1.0*SUM(
CASE WHEN PPDx.FLDDATEDUE IS NULL
AND TBSSx.FLDDATEDUE IS NULL
AND CDUEx.FLDDATEDUE IS NULL
THEN 1 ELSE 0
END) /SUM(1) AS Percent_TB_Compliant
FROM EMPLOYEE E
LEFT OUTER JOIN DEPT D
ON D.FLDCODE= E.FLDDEPT
LEFT OUTER JOIN IMMUNE I ON I.FLDEMPLOYEE = E.FLDREC_NUM AND I.FLDTYPE IN ('109', '111')
AND I.FLDDATE = ( SELECT MAX(FLDDATE) FROM IMMUNE I2 WHERE E.FLDREC_NUM = I2.FLDEMPLOYEE
AND I2.FLDTYPE IN ('109','111') ) AND I.FLDDATE >= #Flu_Date AND I.FLDDATE <= GETDATE()
LEFT OUTER JOIN FITTEST F ON E.FLDREC_NUM = F.FLDEMPLOYEE
AND F.FLDDATE = (SELECT MAX(FLDDATE) FROM FITTEST F2 WHERE E.FLDREC_NUM = F2.FLDEMPLOYEE)
LEFT OUTER JOIN REQEXAM PPDx
ON PPDx.FLDEMPLOYEE = E.FLDREC_NUM
AND PPDx.FLDPHYSICAL = '110' AND
PPDx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN REQEXAM PPDL
ON PPDL.FLDEMPLOYEE = E.FLDREC_NUM
AND PPDL.FLDPHYSICAL = '110'
LEFT OUTER JOIN REQEXAM TBSSx
ON TBSSx.FLDEMPLOYEE = E.FLDREC_NUM
AND TBSSx.FLDPHYSICAL = 'TBSS' AND
TBSSx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN REQEXAM TBSSL
ON TBSSL.FLDEMPLOYEE = E.FLDREC_NUM
AND TBSSL.FLDPHYSICAL = 'TBSS'
LEFT OUTER JOIN REQEXAM CDUEx
ON CDUEx.FLDEMPLOYEE = E.FLDREC_NUM
AND CDUEx.FLDPHYSICAL = '109' AND
CDUEx.FLDDATEDUE <= getdate()
LEFT OUTER JOIN EMP S
ON S.FLDREC_NUM = E.FLDREC_NUM
WHERE E.FLDCOMP = #company
AND E.FLDSTATUS = 'A'
AND E.FLDSUPRNAME <> ' '
AND E.FLDID <> ' '
GROUP BY E.FLDSUPRNAME
ORDER BY E.FLDSUPRNAME
If I add ID,NAME and Dept on select and group by , SUM(1) will turn to 1 or 0, so I'm getting either 100% or 0% for all supervisors.
Any help on this is really appreciated.
thanks for your time.
USE an UNION, add blank columns to your first query and remove the order by:
SELECT (CASE WHEN ID IS NULL THEN Supervisor ELSE '' END) ,ID, name,dept,Percent_Flu_Compliant,Percent_TB_Compliant,Percent_Fit_Compliant FROM
(
SELECT E.FLDSUPRNAME AS Supervisor, NULL as ID, NULL as name, NULL as dept
(...)
GROUP BY hiddensupervisor, Supervisor, ID, name, dept
UNION ALL
SELECT E.FLDSUPRNAME Supervisor, E.id, E.name, E.dept, NULL as Percent_Flu_Compliant, NULL as Percent_TB_Compliant, NULL asPercent_Fit_Compliant
FROM Employee
) as q
ORDER BY supervisor, (CASE WHEN ID IS NULL THEN 1 ELSE 0 END),ID
we add the hidden supervisor column to be able to fit employees under their supervisor but leave that field blank there (we also could not add it and use case in the outer query, dunno which one would be faster). Apparently we have to try with case

Resources