SQL - Converting static pivot into dynamic pivot - sql-server

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.

Related

T-SQL AVG of multiple columns in a row

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.

pivot or un pivot sql server2005

My Pivot
SELECT *
FROM
(
SELECT projectallocation.proAllocationID AS Sno,
temp.intro_name AS IntroTop,
projectallocation.introtoplevelEmpid AS TopEmp,
(companystructure.csshortname) AS Level,
introducermaster.intro_name AS IntroLow,
projectallocation.introlevelEmpid AS EmpID
FROM projectallocation
INNER JOIN dbo.IntroducerMaster ON dbo.introducermaster.empid = projectallocation.introLevelEmpid
INNER JOIN introducermaster AS temp ON temp.empiD = projectallocation.introtopLevelEmpid
INNER JOIN companyStructure ON companyStructure.HLevel = projectallocation.introleveID
WHERE projectallocation.projectID = 1
AND projectallocation.introleveID = 4
GROUP BY IntroducerMaster.Intro_Name,
temp.intro_name,
companyStructure.CSShortName,
projectallocation.proAllocationID,
projectallocation.introlevelEmpid,
projectallocation.introtoplevelEmpid,
projectallocation.introtoplevelid
) b PIVOT(MAX(introlow) FOR level IN(ch,
ed,
dir,
gm,
agm,
bdm,
smm)) PVT;
Output
sno Introtop topempid empid ch ed dir gm agm bdm smm
---------------------------------------------------------------------
1 Jhon.A emp01 emp05 null null null null null ajju.R null
output has 49 rows i include only one......
I want
sno Introtop topempid ch ed dir gm agm bdm smm empid
--------------------------------------------------------------------------
1 Jhon.A emp01 null null null null null ajju.R null emp005
the empid should be in last please help me!!!!
The Solution is Simple, change the SELECT * And replace the * with the Column Names in any order you wish.
Like this
SELECT
sno,
Introtop,
topempid,
ch,
ed,
dir,
gm,
agm,
bdm,
smm,
empid
FROM

How to filter SQL query based on multiple values in the same column

I need to write a query that pulls only employees who are missing their degrees entered into our ERP system. For example, we have a table where their degrees are listed.
Employee ID FName LName Degree
100 John Smith BA
200 Bill Jones BS
300 Alice Waters BA
300 Alice Waters MA
400 Joe Lewis MA
They would like me to pull from this table, only Joe Lewis because he doesn't have a bachelors degree entered in the system, but since he has a master's degree, the assumption is he also has a bachelor's, and someone just missed entering it into the system.
I've tried using EXCEPT filtering on Bachelors degrees, however, that still yields
Employee ID FName LName Degree
300 Alice Waters MA
400 Joe Lewis MA
And I don't want Alice in the list because she has a bachelors degree coded into the system.
Any thoughts on how I might approach this would be much appreciated.
If this is just for MA and BA, you can use conditional aggregation:
select empid, fname, lname
from t
group by empid, fname, lname
having sum(case when degree = 'BA' then 1 else 0 end) = 0 and
sum(case when degree = 'MA' then 1 else 0 end) > 0;
Or, you can use exists:
select t.*
from t
where degree = 'MA' and
not exists (select 1
from t t2
where t2.empid = t.empid and t2.degree = 'BA'
);
You could go with a left join of the Masters subset against the Bachelors subset:
select m.EmployeeId, m.FName, m.LName
from (select * from Employee where Degree in ('MA')) m
left join (select * from Employee where Degree in ('BA', 'BS')) b
on m.EmployeeId = b.EmployeeId
where b.EmployeeId is null
Maybe you should consider to use a query without subqueries...
SELECT E.EmployeeId, E.FName, E.LName
FROM Employee AS E
LEFT JOIN Employee AS F ON (E.EmployeeId = F.EmployeeId AND F.Degree = 'BA')
WHERE E.Degree <> 'BA' AND F.EmployeeId IS NULL
Or (if BS must be considered as well) :
SELECT E.EmployeeId, E.FName, E.LName
FROM Employee AS E
LEFT JOIN Employee AS F ON (E.EmployeeId = F.EmployeeId AND F.Degree IN('BA','BS'))
WHERE E.Degree <> 'BA' AND E.Degree <> 'BS' AND F.EmployeeId IS NULL
keep in mind subqueries are often slow, depending on how many row is concerned...

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

Selecting values based on value in another column with one being NULL

I have the following table setup
Acc Currency Alias
1 NULL A
1 USD B
1 EUR C
I want to extract the alias by giving the input as Acc. The Currency and Acc are joined to another table.
It should work as follows -
If acc = 1 and currency is USD then B, if EUR then C, if null then A
Can someone help with this please ?
Did you actually try to write any SQL yourself?
SELECT ...
FROM otherTable ot
(INNER) JOIN thisTable tt
ON ot.Acc = tt.Acc AND ot.Currency = tt.Currency
...
Matching on NULL explicitly
SELECT *
FROM othertbl o
JOIN thistbl t on o.acc = t.acc
and (o.currency = t.currency or o.currency is null AND t.currency is null)
Or in your case, using the NULL currency from thistbl as a fallback
SELECT ...
FROM othertbl o
JOIN thistbl t on o.acc = t.acc and o.currency = t.currency
UNION ALL
SELECT ...
FROM othertbl o
JOIN thistbl t on o.acc = t.acc and t.currency IS NULL
WHERE NOT EXISTS (select *
from thistbl t2
where o.acc = t2.acc and o.currency = t2.currency)

Resources