Query to transform table in SQL - sql

My query is as below
SELECT SupplierId,StakeholderSupplierId,Percentage
FROM std_Stakeholders
ORDER BY SupplierId
which yields me result as below
SupplierId StakeholderSupplierId Percentage
1 3175 68.00
2929 5504 25.00
5504 1 68.25
5504 3238 50.00
5504 2810 23.00
I want to transform this output as .
SupplierId StakeholderSupplierId1 Percentage StakeholderSupplierId2 Percentage StakeholderSupplierId3 Percentage
1 3175 68.00
2929 5504 25.00
5504 1 68.68 3238 50.00 2810 23.00
I tried with joins and pivoting . couldn't succeed in writing a proper query to get the desired output . Can anyone help me out
Note: StakeholderSupplierId is not static . it may vary from 1 to n

I don't know if this is a solution for your problem (depends on what you need to do after this query), but for problems like this I have use the MySQL GROUP_CONCAT function. For a similar solution in SQL Server check this question.
With a GROUP_CONCAT function you can get a result table like this:
SupplierId StakeholderSupplierIds Percentages
1 3175 68.00
2929 5504 25.00
5504 1, 3238, 2810 68.68, 50.00, 23.00

Well, you can do this with a two-step process but equally I expect it is possible with a PIVOT - however as an example (old style approach for the last step):
declare #stakeholdersForPivot table
(
SupplierId int,
StakeholderId int,
StakeholderSupplierId int,
Percentage decimal(19,2)
)
; with orderedStakeholders as
(
select SupplierId
, ROW_NUMBER() over (partition by SupplierId order By StakeholderSupplierId) StakeholderId
,StakeholderSupplierId, Percentage
from std_Stakeholders
)
insert into #stakeholdersForPivot (SupplierId, StakeholderId, StakeholderSupplierId, Percentage)
select SupplierId, StakeholderId, StakeholderSupplierId, Percentage
from orderedStakeholders
select SupplierId
, sum(case when StakeholderId = 1 then StakeholderSupplierId else null end) StakeholderSupplierId1
, sum(case when StakeholderId = 1 then Percentage else null end) Percentage1
, sum(case when StakeholderId = 2 then StakeholderSupplierId else null end) StakeholderSupplierId2
, sum(case when StakeholderId = 2 then Percentage else null end) Percentage2
, sum(case when StakeholderId = 3 then StakeholderSupplierId else null end) StakeholderSupplierId3
, sum(case when StakeholderId = 3 then Percentage else null end) Percentage3
from #stakeholdersForPivot
group by SupplierId
order by SupplierId
Obviously you have to be explicit about the maximum number of stakeholder suppliers you're expecting per supplier but you can extend this as required.

Here's the query that uses the "GROUP BY MAX" trick. Should work across databases. The drawback is that the columns list is static.
SELECT
supplierid,
max(StakeholderSupplierId1) AS StakeholderSupplierId1,
max(percentage1) AS percentage,
max(StakeholderSupplierId2) AS StakeholderSupplierId2,
max(percentage2) AS percentage,
max(StakeholderSupplierId3) AS StakeholderSupplierId3,
max(percentage3) AS percentage
FROM
(
SELECT
supplierid,
CASE WHEN ranking = 1 THEN stakeholdersupplierid END AS StakeholderSupplierId1,
CASE WHEN ranking = 1 THEN percentage END AS percentage1,
CASE WHEN ranking = 2 THEN stakeholdersupplierid END AS StakeholderSupplierId2,
CASE WHEN ranking = 2 THEN percentage END AS percentage2,
CASE WHEN ranking = 3 THEN stakeholdersupplierid END AS StakeholderSupplierId3,
CASE WHEN ranking = 3 THEN percentage END AS percentage3
FROM
(
SELECT
supplierid,
stakeholdersupplierid,
percentage,
rank() OVER (PARTITION BY supplierid ORDER BY percentage DESC) AS ranking
FROM
std_stakeholders
) AS t
) AS t
GROUP BY
supplierid
;

Related

How to query for Total of Integer Records in SQL

I have a table structured like this;
shopid times
shop1 5
shop2 20
shop1 6
shop1 100
shop2 100
My work in progress query;
SELECT
sum(case when shopid='shop1' then times end) as shop1
,sum(case when shopid='shop2' then times end) as shop2
,sum(times) as total
from table3
group by shopid
order by shopid
Outcome
shop1 shop2 total
111 NULL 111
NULL 120 120
I need the TOTAL for each records, expected like this. Would you be able to point me to the right direction?
shop1 shop2 total
111 NULL 111
NULL 120 120
TOTAL 111 120 231
Why not just use a single query with a single row?
select sum(case when shopid = 'shop1' then times end) as shop1,
sum(case when shopid = 'shop2' then times end) as shop2,
sum(times) as total
from table3 ;
(You may want `where shopid in ('shop1', 'shop2').)
If you want different rows, then you don't need so many columns. You can get the total using GROUPING SETS:
select coalesce(shopid, 'Total'), sum(times)
from table3
group by grouping sets ( (shopid), () );
EDIT:
Or if you really want you can do:
select (case when shopid is null then 'Total' end),
sum(case when shopid = 'shop1' then times end) as shop1,
sum(case when shopid = 'shop2' then times end) as shop2,
sum(times) as total
from table3
group by grouping sets ( (shopid), () );

T-SQL calculate the percent increase or decrease between the earliest and latest for each project

I have a table like below, I am trying to run a query in T-SQL to get the earliest and latest costs for each project_id according to the date column and calculate the percent cost increase or decrease and return the data-set show in the second table (I have simplified the table in this question).
project_id date cost
-------------------------------
123 7/1/17 5000
123 8/1/17 6000
123 9/1/17 7000
123 10/1/17 8000
123 11/1/17 9000
456 7/1/17 10000
456 8/1/17 9000
456 9/1/17 8000
876 1/1/17 8000
876 6/1/17 5000
876 8/1/17 10000
876 11/1/17 8000
Result:
(Edit: Fixed the result)
project_id "cost incr/decr pct"
------------------------------------------------
123 80% which is (9000-5000)/5000
456 -20%
876 0%
Whatever query I run I get duplicates.
This is what I tried:
select distinct
p1.Proj_ID, p1.date, p2.[cost], p3.cost,
(nullif(p2.cost, 0) / nullif(p1.cost, 0)) * 100 as 'OVER UNDER'
from
[PROJECT] p1
inner join
(select
[Proj_ID], [cost], min([date]) min_date
from
[PROJECT]
group by
[Proj_ID], [cost]) p2 on p1.Proj_ID = p2.Proj_ID
inner join
(select
[Proj_ID], [cost], max([date]) max_date
from
[PROJECT]
group by
[Proj_ID], [cost]) p3 on p1.Proj_ID = p3.Proj_ID
where
p1.date in (p2.min_date, p3.max_date)
Unfortunately, SQL Server does not have a first_value() aggregation function. It does have an analytic function, though. So, you can do:
select distinct project_id,
first_value(cost) over (partition by project_id order by date asc) as first_cost,
first_value(cost) over (partition by project_id order by date desc) as last_cost,
(first_value(cost) over (partition by project_id order by date desc) /
first_value(cost) over (partition by project_id order by date asc)
) - 1 as ratio
from project;
If cost is an integer, you may need to convert to a representation with decimal places.
You can use row_number and OUTER APPLY over top 1 ... prior to SQL 2012
select
min_.projectid,
latest_.cost - min_.cost [Calculation]
from
(select
row_number() over (partition by projectid order by date) rn
,projectid
,cost
from projectable) min_ -- get the first dates per project
outer apply (
select
top 1
cost
from projectable
where
projectid = min_.projectid -- get the latest cost for each project
order by date desc
) latest_
where min_.rn = 1
This might perform a little better
;with costs as (
select *,
ROW_NUMBER() over (PARTITION BY project_id ORDER BY date) mincost,
ROW_NUMBER() over (PARTITION BY project_id ORDER BY date desc) maxcost
from table1
)
select project_id,
min(case when mincost = 1 then cost end) as cost1,
max(case when maxcost = 1 then cost end) as cost2,
(max(case when maxcost = 1 then cost end) - min(case when mincost = 1 then cost end)) * 100 / min(case when mincost = 1 then cost end) as [OVER UNDER]
from costs a
group by project_id

How can I do grouping in my case using SQL Server?

I wants to calculate sum of quantity based on Quantity value
For Example
ItemNo Quantity
------------------
111 5
111 -2
111 3
112 10
I want to do grouping by ItemNo and calculate like below
ItemNo Quantity Positive Negative
-----------------------------------------
111 6 8 -2
112 10 10 0
I tried like this
SELECT
ItemNo,
Sum(Quantity),
Case when Quantity >= 0 then sum(quantity) else 0 end POSITIVE,
Case when Quantity < 0 then sum(quantity) else 0 end Negative
From
Sales
Group By
ItemNo,
Quantity
I know this grouping is wrong. How my query should be?
thanks
Just put the SUM() around your CASE() statement:
SELECT
ItemNo,
Sum(Quantity),
SUM(Case when Quantity >= 0 then quantity else 0 end) POSITIVE,
SUM(Case when Quantity < 0 then quantity else 0 end) Negative
From
Sales
Group By
ItemNo;
Also, remove Quantity from your GROUP BY. You are aggregating quantity with a sum() so it's nonsense to GROUP BY it as well.
I would leave this as a comment to JNevill's answer if I had the reputation, but you would also want to give the quantity sum an alias to get the results in the question. For example: SELECT
ItemNo,
Sum(Quantity) Quantity,
SUM(Case when Quantity >= 0 then Quantity else 0 end) Positive,
SUM(Case when Quantity < 0 then Quantity else 0 end) Negative
From
Sales
Group By
ItemNo;

SQL Running Total Reset on Condition

I have the following table:
Transaction History Table
TransactionHistoryId ProductCode Type Quantity PurchasePrice CurrentPrice
1 Product1 B 10 3.00 2.00
2 Product1 B 5 7.00 2.00
3 Product1 S -7 7.00 2.00
4 Product1 S -8 3.00 3.00
5 Product1 B 4 10.00 10.00
6 Product1 B 5 12.00 12.00
8 Product2 B 8 20.00 20.00
I would like to acheive the following table:
TransactionHistoryId ProductCode Type Quantity PurchasePrice QtyRunning PriceRunning
1 Product1 B 10 3.00 10 30.00
2 Product1 B 5 7.00 15 65.00
3 Product1 S -7 7.00 8 65.00
4 Product1 S -8 3.00 0 0.00
5 Product1 B 4 10.00 4 40.00
6 Product1 B 5 12.00 9 100.00
8 Product2 B 8 20.00 8 160.00
Create Table SQL
IF OBJECT_ID('TEMPDB..#TransactionHistory') IS NOT NULL
DROP TABLE #TransactionHistory
create table #TransactionHistory
(TransactionHistoryId int,
ProductCode varchar(10),
Type char(1),
Quantity smallint,
PurchasePrice decimal(18,2),
CurrentPrice decimal(18,2)
)
insert into #TransactionHistory
values
(1,'Product1','B',10,3.00,2.00),
(2,'Product1','B',5,7.00,2.00),
(3,'Product1','S',-7,7.00,2.00),
(4,'Product1','S',-8,3.00,3.00),
(5,'Product1','B',4,10.00,10.00),
(6,'Product1','B',5,12.00,12.00),
(8,'Product2','B',8,20.00,20.00)
Rules
PriceRunningTotal resets when the quantity running total is 0
PriceRunningTotal sums up only Type = 'B' (buys), when Type = 'S' (sold) keep the previous purchase price running total
Notice there is a Product 2 so it should have it's own running count independent of Product 1
Purpose
A query to ultimately find out the following:
Product Quantity AdjustedPurchasePrice
Product1 9 $11.11
Product2 8 $20
I used the following SQL Server 2012 query to get the result, but I feel it could be done much better:
Query
SELECT *,
PriceRunningTotalFinal =
SUM(CASE
WHEN QuantityRunningTotal = 0 THEN -1 * PriceRunningTotal
WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM (
SELECT TransactionHistoryId, ProductCode, Type, Quantity, PurchasePrice,
QuantityRunningTotal = SUM(Quantity) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
),
PriceRunningTotal = SUM(CASE WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM TransactionHistory
) AS Results1
ORDER BY ProductCode;
Problem
Ideally I would of liked to use the QuantityRunningTotal within another query but I can't nest windowed functions.
Anyone know of a more efficient way to achieve this result?
Hmmm. I think something like this:
select th.*,
sum(case when type = 'B' then Quantity * PurchasePrice
else 0
end) over (partition by grp, ProductCode order by TransactionHistoryId
) as PriceRunningTotal
from (select th.*,
sum(case when running_quantity = 0 then 1 else 0 end) over (partition by ProductCode order by TransactionHistoryId) as grp
from (select th.*,
sum(quantity) over (partition by ProductCode order by TransactionHistoryId
) as running_quantity
from TransactionHistory th
) th;
I'm not sure if this is the same logic as your query. For this query:
The innermost subquery calculates the running quantity.
The middle subquery calculates a group based on the number of times the running quantity has been 0.
The outermost query then calculates the running price.

Get percentiles of data-set with group by month

I have a SQL table with a whole load of records that look like this:
| Date | Score |
+ -----------+-------+
| 01/01/2010 | 4 |
| 02/01/2010 | 6 |
| 03/01/2010 | 10 |
...
| 16/03/2010 | 2 |
I'm plotting this on a chart, so I get a nice line across the graph indicating score-over-time. Lovely.
Now, what I need to do is include the average score on the chart, so we can see how that changes over time, so I can simply add this to the mix:
SELECT
YEAR(SCOREDATE) 'Year', MONTH(SCOREDATE) 'Month',
MIN(SCORE) MinScore,
AVG(SCORE) AverageScore,
MAX(SCORE) MaxScore
FROM SCORES
GROUP BY YEAR(SCOREDATE), MONTH(SCOREDATE)
ORDER BY YEAR(SCOREDATE), MONTH(SCOREDATE)
That's no problem so far.
The problem is, how can I easily calculate the percentiles at each time-period? I'm not sure that's the correct phrase. What I need in total is:
A line on the chart for the score (easy)
A line on the chart for the average (easy)
A line on the chart showing the band that 95% of the scores occupy (stumped)
It's the third one that I don't get. I need to calculate the 5% percentile figures, which I can do singly:
SELECT MAX(SubQ.SCORE) FROM
(SELECT TOP 45 PERCENT SCORE
FROM SCORES
WHERE YEAR(SCOREDATE) = 2010 AND MONTH(SCOREDATE) = 1
ORDER BY SCORE ASC) AS SubQ
SELECT MIN(SubQ.SCORE) FROM
(SELECT TOP 45 PERCENT SCORE
FROM SCORES
WHERE YEAR(SCOREDATE) = 2010 AND MONTH(SCOREDATE) = 1
ORDER BY SCORE DESC) AS SubQ
But I can't work out how to get a table of all the months.
| Date | Average | 45% | 55% |
+ -----------+---------+-----+-----+
| 01/01/2010 | 13 | 11 | 15 |
| 02/01/2010 | 10 | 8 | 12 |
| 03/01/2010 | 5 | 4 | 10 |
...
| 16/03/2010 | 7 | 7 | 9 |
At the moment I'm going to have to load this lot up into my app, and calculate the figures myself. Or run a larger number of individual queries and collate the results.
Whew. This was a real brain teaser. First, my table schema for testing was:
Create Table Scores
(
Id int not null identity(1,1) primary key clustered
, [Date] datetime not null
, Score int not null
)
Now, first, I calculated the values using a CTE in SQL 2008 in order to check my answers and then I built a solution that should work in SQL 2000. So, in SQL 2008 we do something like:
;With
SummaryStatistics As
(
Select Year([Date]) As YearNum
, Month([Date]) As MonthNum
, Min(Score) As MinScore
, Max(Score) As MaxScore
, Avg(Score) As AvgScore
From Scores
Group By Month([Date]), Year([Date])
)
, Percentiles As
(
Select Year([Date]) As YearNum
, Month([Date]) As MonthNum
, Score
, NTile( 100 ) Over ( Partition By Month([Date]), Year([Date]) Order By Score ) As Percentile
From Scores
)
, ReportedPercentiles As
(
Select YearNum, MonthNum
, Min(Case When Percentile = 45 Then Score End) As Percentile45
, Min(Case When Percentile = 55 Then Score End) As Percentile55
From Percentiles
Where Percentile In(45,55)
Group By YearNum, MonthNum
)
Select SS.YearNum, SS.MonthNum
, SS.MinScore, SS.MaxScore, SS.AvgScore
, RP.Percentile45, RP.Percentile55
From SummaryStatistics As SS
Join ReportedPercentiles As RP
On RP.YearNum = SS.YearNum
And RP.MonthNum = SS.MonthNum
Order By SS.YearNum, SS.MonthNum
Now for a SQL 2000 solution. In essence, the trick is to use a couple of temporary tables to tally the occurances of the scores.
If object_id('tempdb..#Working') is not null
DROP TABLE #Working
GO
Create Table #Working
(
YearNum int not null
, MonthNum int not null
, Score int not null
, Occurances int not null
, Constraint PK_#Working Primary Key Clustered ( MonthNum, YearNum, Score )
)
GO
Insert #Working(MonthNum, YearNum, Score, Occurances)
Select Month([Date]), Year([Date]), Score, Count(*)
From Scores
Group By Month([Date]), Year([Date]), Score
GO
If object_id('tempdb..#SummaryStatistics') is not null
DROP TABLE #SummaryStatistics
GO
Create Table #SummaryStatistics
(
MonthNum int not null
, YearNum int not null
, Score int not null
, Occurances int not null
, CumulativeTotal int not null
, Percentile float null
, Constraint PK_#SummaryStatistics Primary Key Clustered ( MonthNum, YearNum, Score )
)
GO
Insert #SummaryStatistics(YearNum, MonthNum, Score, Occurances, CumulativeTotal)
Select W2.YearNum, W2.MonthNum, W2.Score, W2.Occurances, Sum(W1.Occurances)-W2.Occurances
From #Working As W1
Join #Working As W2
On W2.YearNum = W1.YearNum
And W2.MonthNum = W1.MonthNum
Where W1.Score <= W2.Score
Group By W2.YearNum, W2.MonthNum, W2.Score, W2.Occurances
Update #SummaryStatistics
Set Percentile = SS.CumulativeTotal * 100.0 / MonthTotal.Total
From #SummaryStatistics As SS
Join (
Select SS1.YearNum, SS1.MonthNum, Max(SS1.CumulativeTotal) As Total
From #SummaryStatistics As SS1
Group By SS1.YearNum, SS1.MonthNum
) As MonthTotal
On MonthTotal.YearNum = SS.YearNum
And MonthTotal.MonthNum = SS.MonthNum
Select GeneralStats.*, Percentiles.Percentile45, Percentiles.Percentile55
From (
Select Year(S1.[Date]) As YearNum
, Month(S1.[Date]) As MonthNum
, Min(S1.Score) As MinScore
, Max(S1.Score) As MaxScore
, Avg(S1.Score) As AvgScore
From Scores As S1
Group By Month(S1.[Date]), Year(S1.[Date])
) As GeneralStats
Join (
Select SS1.YearNum, SS1.MonthNum
, Min(Case When SS1.Percentile >= 45 Then Score End) As Percentile45
, Min(Case When SS1.Percentile >= 55 Then Score End) As Percentile55
From #SummaryStatistics As SS1
Group By SS1.YearNum, SS1.MonthNum
) As Percentiles
On Percentiles.YearNum = GeneralStats.YearNum
And Percentiles.MonthNum = GeneralStats.MonthNum
Without the data, I'm not sure if I'm doing this right, but maybe this will help get you there with two queries per year instead of 24...
SELECT MAX(SubQ.SCORE), MyMonth FROM
(SELECT TOP 45 PERCENT SCORE , MONTH(SCOREDATE) as MyMonth
FROM SCORES
WHERE YEAR(SCOREDATE) = 2010
ORDER BY SCORE ASC) AS SubQ
group by MyMonth