How to pivot on 2 fields - sql

Here is the table I have:
PeriodID RecordID Basis Amount1 Amount2
1 1 IFRS 10 100
1 2 IFRS 20 200
2 1 IFRS 15 150
2 2 IFRS 25 250
1 1 CGAAP 30 300
1 2 CGAAP 40 400
2 1 CGAAP 35 350
2 2 CGAAP 45 450
I would like to pivot on the PeriodID and Basis field so that the result would have following columns:
RecordID
Period1IFRSAmount1
Period2IFRSAmount1
PeriodID1IFRSAmount2
PeriodID2IFRSAmount2
Period1CGAAPAmount1
Period2CGAAPAmount1
PeriodID1CGAAPAmount2
PeriodID2IFRSAmount22

First of all, you need to create the ouput that you need as rows. That is why union part is really important. After you construct your data you can easily use PIVOT.
Following query written in SQL SERVER. It should be similar to other databases as well.
SELECT *
FROM
(
SELECT RecordID, 'Period' + CONVERT(VARCHAR, PeriodID) + Basis + 'Amount1' AS Basis, Amount1 AS Amount
FROM TableName
UNION ALL
SELECT RecordID, 'Period' + CONVERT(VARCHAR, PeriodID) + Basis + 'Amount2' AS Basis, Amount2 AS AMOUNT
FROM TableName
) AS Q
PIVOT (
SUM(Amount)
FOR Basis IN
(
[Period1IFRSAmount1],
[Period2IFRSAmount1],
[Period1IFRSAmount2],
[Period2IFRSAmount2],
[Period1CGAAPAmount1],
[Period2CGAAPAmount1],
[Period1CGAAPAmount2],
[Period2CGAAPAmount2]
)
) AS P

Related

Aggregate or similar function in SQL Views

There is a table CUST_ORDERS
CustID OrderID InvoiceAmt
1 1 100
1 2 60
2 3 90
3 4 10
3 5 20
I want to create a SQL VIEW for this which contains aggregate of InvoiceAmt for each Customer
CustID InvoiceAmt
1 160
2 90
3 30
As can be seen above, For CustID 1, InvoiceAmt is 100 + 60 = 160
How can this be achieved using a VIEW ?
If not, can it be achieved using triggers ?
create view
create view my_view as
select custID, sum(InvoiceAmt) sum_invoice_amt
from CUST_ORDERS
group by custID
then select from view
select * from my_view
or explicitally
select custID, sum_invoice_amt
from my_view

How to Unpivot with multiple column

Below is my table structure. i wants to unpivot below data.
amount user amount1 user1 amount2 user2 amount3 user3 amount4 user4
10 1 20 2 30 3 40 4 50 5
Now i wants result as below.
amount user
10 1
20 2
30 3
40 4
50 5
You want to unpivot the data. I recommend apply:
select v.*
from t cross apply
(values (t.amount, t.user),
(t.amount1, t.user1),
(t.amount2, t.user2),
(t.amount3, t.user3),
(t.amount4, t.user4)
) v(amount, user);
You can try the following it will work for your specific case here:
SELECT 'user' + CAST(ROW_NUMBER() OVER(partition BY 1 ORDER BY amountx) AS VARCHAR) AS [user],amountx
FROM [table]
UNPIVOT(
amountx
FOR amntx IN (amount,amount1,amount2,amount3,amount4)
) AS UP
http://rextester.com/EWXV86982

SQL - Return dataset pivoted on one column with multiple aggregates

Using TSQL 2012 here, essentially I have a dataset that looks like the following:
Period Values OtherValues SiteName MoreColumns
1 12 45 Site 1 34
2 34 6 Site 1 346
2 56 79 Site 1 345
3 3 78 Site 1 67
3 4 67 Site 1 8
What I would like to return is a dataset that groups on site and sums all the other columns based on the period they're against.
Site P1V P2v P3V P1OtherV P2OtherV P3OtherV
Site 1 12 90 7 45 85 145
I know I can do it with a case in the style of:
SELECT CASE WHEN Period = '1' THEN Sum(Values) As P1Values,
CASE WHEN Period = '2' THEN Sum(Values) As P2Values,
CASE WHEN Period = '3' THEN Sum(Values) As P3Values
.....
But surely there's a more elegant solution for this? The dataset should return three sums (for each period) for 7 columns, so in total 21 sums, with the potential to grow.
I would use UNPIVOT/PIVOT sequence:
SELECT * FROM
(
SELECT Period+Col Period, SiteName, Value FROM
Src UNPIVOT (Value FOR Col IN (SapValues,OtherValues)) U
) U
PIVOT (SUM(Value) FOR Period IN (P1SapValues,P2SapValues,P3SapValues,
P1OtherValues,P2OtherValues,P3OtherValues)) P
You can do it using PIVOT and making source table with UNION
WITH T1 AS
(
SELECT Period + 'SapValues' as Period, SapValues as Value, SiteName FROM T
UNION ALL
SELECT Period + 'OtherValues' as Period, OtherValues as Value, SiteName FROM T
)
SELECT *
FROM T1
PIVOT
(
Sum(Value)
FOR Period IN ([P1SapValues],[P2SapValues],[P3SapValues],
[P1OtherValues],[P2OtherValues],[P3OtherValues])
) AS PivotTable;

Update column value of one row from other rows

I have the following table:
sno name pid amount total
1 Arif 0 100 null
2 Raj 1 200 null
3 Ramesh 2 100 null
4 Pooja 2 100 null
5 Swati 3 200 null
6 King 4 100 null
I want total of each person such that it gives total sum of amount of its descendants.
For ex.
for RAJ total will be : total= amount of(raj+ramesh+pooja+swati+king)
for SWATI :Total=amount of swati only.
You could try something like this:
WITH hierarchified AS (
SELECT
sno,
amount,
hierarchyID = CAST(sno AS varchar(500))
FROM yourTable
WHERE pid = 0
UNION ALL
SELECT
t.sno,
t.amount,
hierarchyID = CAST(h.hierarchyID + '/' + RTRIM(t.sno) AS varchar(500))
FROM yourTable t
INNER JOIN hierarchified h ON t.pid = h.sno
)
UPDATE yourTable
SET total = t.amount + ISNULL(
(
SELECT SUM(amount)
FROM hierarchified
WHERE hierarchyID LIKE h.hierarchyID + '/%'
),
0
)
FROM yourTable t
INNER JOIN hierarchified h ON t.sno = h.sno;
Note that this query (which you can try on SQL Fiddle) would probably not be very efficient on a large dataset. It might do as a one-off query, and then it would likely be better to organise updating the totals each time the table is updated, i.e. using triggers.

How to find range of a number where the ranges come dyamically from another table?

If I had two tables:
PersonID | Count
-----------------
1 | 45
2 | 5
3 | 120
4 | 87
5 | 60
6 | 200
7 | 31
SizeName | LowerLimit
-----------------
Small | 0
Medium | 50
Large | 100
I'm trying to figure out how to do a query to get a result similar to:
PersonID | SizeName
-----------------
1 | Small
2 | Small
3 | Large
4 | Medium
5 | Medium
6 | Large
7 | Small
Basically, one table specifies an unknown number of "range names" and their integer ranges associated. So a count range of 0 to 49 from the person table gets a 'small' designation. 50-99 gets 'medium' etc. But I need it to be dynamic because I do not know the range names or integer values. Can I do this in a single query or would I have to write a separate function to loop through the possibilities?
Try this out:
SELECT PersonID, SizeName
FROM
(
SELECT
PersonID,
(SELECT MAX([LowerLimit]) FROM dbo.[Size] WHERE [LowerLimit] < [COUNT]) As LowerLimit
FROM dbo.Person
) A
INNER JOIN dbo.[SIZE] B ON A.LowerLimit = B.LowerLimit
With Ranges As
(
Select 'Small' As Name, 0 As LowerLimit
Union All Select 'Medium', 50
Union All Select 'Large', 100
)
, Person As
(
Select 1 As PersonId, 45 As [Count]
Union All Select 2, 5
Union All Select 3, 120
Union All Select 4, 87
Union All Select 5, 60
Union All Select 6, 200
Union All Select 7, 31
)
, RangeStartEnd As
(
Select R1.Name
, Case When Min(R1.LowerLimit) = 0 Then -1 Else MIN(R1.LowerLimit) End As StartValue
, Coalesce(MIN(R2.LowerLimit), 2147483647) As EndValue
From Ranges As R1
Left Join Ranges As R2
On R2.LowerLimit > R1.LowerLimit
Group By R1.Name
)
Select P.PersonId, P.[Count], RSE.Name
From Person As P
Join RangeStartEnd As RSE
On P.[Count] > RSE.StartValue
And P.[Count] <= RSE.EndValue
Although I'm using common-table expressions (cte for short) which only exist in SQL Server 2005+, this can be done with multiple queries where you create a temp table to store the equivalent of the RangeStartEnd cte. The trick is to create a view that has a start column and end column.
SELECT p.PersonID, Ranges.SizeName
FROM People P
JOIN
(
SELECT SizeName, LowerLimit, MIN(COALESCE(upperlimit, 2000000)) AS upperlimit
FROM (
SELECT rl.SizeName, rl.LowerLimit, ru.LowerLimit AS UpperLimit
FROM Ranges rl
LEFT OUTER JOIN Ranges ru ON rl.LowerLimit < ru.LowerLimit
) r
WHERE r.LowerLimit < COALESCE(r.UpperLimit, 2000000)
GROUP BY SizeName, LowerLimit
) Ranges ON p.Count >= Ranges.LowerLimit AND p.Count < Ranges.upperlimit
ORDER BY PersonID