Related
I have the following table structure
Create Table PivotTabSample (
ID INT IDENTITY(100000001,1) NOT NULL,
Product Nvarchar(30),
StoreNumber INT,
StoreSalesEstimate DECIMAL,
StoreSalesActual DECIMAL
)
The sample values are populated with the following insert -
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0001','101',500,450)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0002','101',300,350)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0003','101',50,61)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0004','101',100,900)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0005','101',10,9)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0001','102',1500,1450)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0002','102',1400,3500)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0003','102',150,610)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0004','102',800,900)
I am trying to do a PIVOT on this data set. and I run the following query -
SELECT Product, [101], [102]
FROM PivotTabSample
PIVOT
( min(StoreSalesActual)
FOR StoreNumber IN ([101],[102])
)AS p
The result that is get is not desired manner - I do not get in one the Product and Store details. There are two Product rows for each Store.
I do not want that. There is no aggregation required here, but I am not sure how to achieve it.
I would like to have the results like this -
Product 101ActualSales 101EstimatedSales 102ActualSales 102estimatedSales
P001 100 101 90 91
P002
P003
P001
Please advise.
WITH CTE
AS (SELECT Product,
ISNULL([101], 0) AS '101ActualSales',
ISNULL([102], 0) AS '102ActualSales',
0 AS '101EstimatedSales',
0 AS '102EstimatedSales'
FROM PivotTabSample
PIVOT
(
SUM(StoreSalesActual)
FOR StoreNumber IN ([101], [102])
) AS p)
SELECT product,
SUM([101ActualSales]) AS [101actualsales],
SUM([101EstimatedSales]) AS [101EstimateSales],
SUM([102ActualSales]) AS [102actualsales],
SUM([102EstimatedSales]) AS [102EstimateSales]
FROM
(
SELECT *
FROM CTE
UNION
SELECT pt.Product,
0 AS '101ActualSales',
0 AS '102ActualSales',
ISNULL([101], 0) AS '101EstimateSales',
ISNULL([102], 0) AS '102EstimateSales'
FROM PivotTabSample
PIVOT
(
SUM(StoreSalesEstimate)
FOR StoreNumber IN ([101], [102])
) AS pt
) AS x
GROUP BY product;
http://rextester.com/GFID38296
Having just had a similar problem at work where I've had to modify a pivot that had the same output as yours, I've found that wrapping into a CTE helped. I just need to figure out why, but I suspect adding the StoreSalesActual has helped...
WITH cte AS
(
SELECT Product,
StoreNumber,
StoreSalesActual
FROM #PivotTabSample
)
SELECT DISTINCT Product, [101], [102]
FROM cte
PIVOT
(
MAX(StoreSalesActual)
FOR StoreNumber IN ([101],[102])
)AS p
GROUP BY Product, [101], [102]
I have a table T :
CREATE TABLE T
(
id INT,
type VARCHAR(200),
type_value VARCHAR(10),
value VARCHAR(200)
);
INSERT INTO T VALUES (1, 'RoomColor', 'room1', 'yellow');
INSERT INTO T VALUES (1, 'RoomColor', 'room2', 'red');
INSERT INTO T VALUES (2, 'RoomColor', 'room1', 'blue');
INSERT INTO T VALUES (2, 'RoomColor', 'room1', 'pink');
INSERT INTO T VALUES (3, 'RoomColor', 'room1', 'white');
INSERT INTO T VALUES (3, 'RoomColor', 'room2', 'grey');
INSERT INTO T VALUES (3, 'RoomColor', 'room2', 'brown');
INSERT INTO T VALUES (4, 'RoomColor', 'room3', 'green');
I need to transform it into :
id BedRoomColor DiningRoomColor
-------------------------------------------
1 yellow red
2 blue pink
3 white grey
4 green null
Logic behind the transformation:
If there are more than two room type_value then discard the third room type_value
For same id if there are more than one room type_value ( for example room1,room1 or room2,room2 or room1,room2) then use first type_value to create as BedRoomColor and second type_value to create DiningRoomColor
If there is only 1 room type_value (for eg. room1 or room2 or room3) for an id then corresponding value ( red,green,yellow etc ) will be placed in BedRoomColor and DiningRoomColor will be null
I am struggling with this logic for couple of days. Can anyone please help me.
Thanks
You can use this script
;WITH CTE AS (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY id ORDER BY type_value) FROM T
)
SELECT id, [1] BedRoomColor, [2] DiningRoomColor FROM
(SELECT id,value, RN FROM CTE ) SRC
PIVOT (MAX(value) FOR RN IN ([1], [2]) ) AS PVT
Result:
id BedRoomColor DiningRoomColor
----------- ------------------ ---------------
1 yellow red
2 blue pink
3 white grey
4 green NULL
Another way and with adding type to query is:
;with tt as (
select *,
row_number() over (partition by [type], id order by type_value) rn
-- ^^^^^^ I add type to support other types if there is
from t
)
select id,
max(case when [type] = 'RoomColor' and rn = 1 then [value] end) 'BedRoomColor',
max(case when [type] = 'RoomColor' and rn = 2 then [value] end) 'DiningRoomColor'
from tt
group by id;
SQL Server Fiddle Demo
try this:
with tmp as (
select T.*, rownumber() over(patition by id order by type_value) rang
from T
)
select f1.id, f1.value as BedRoomColor, f2.value as DiningRoomColor
from tmp f1
left outer join tmp f2 on f1.id=f2.id and f2.rang=2
where f1.rang=1
My table is:
SBType|SBName|Qty
===================
SMDB SB01 1
SMDB SB01 4
SMDB SB02 2
SMDB SB02 5
SMDB SB03 3
SMDB SB03 6
My desired output is:
SB01 | SB02 | SB03
==================
1 2 3
4 5 6
This is what my code looks like:
SELECT *
FROM (
SELECT
SM.SBName,ISNULL(ES.Qty,0)Qty
FROM RE_ES_SwitchBoard_Mast SM
left outer join RE_ES_Estimations ES on SM.PrCode=ES.PrCode and
Sm.SBType=ES.SBType and SM.SBName=ES.SBName
Where SM.PrCode='PR004' and SM.SBType='SMDB'
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
)AS pvthere
and the result of my attempt looks like:
SB01 SB02 SB03
1 2 3
I have tried with MAX(Qty) but it is not working.
Thanks in advance.
You are almost there.
By adding ROW_NUMBER() OVER (PARTITION BY SBName ORDER BY Qty) rn to the source of PIVOT clause you get multiple rows for different SBName instead of one grouped row. Your query should look like:
SELECT SB01, SB02, SB03
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY SB.SBName ORDER BY Qty) rn,
SB.SBName,ISNULL(ES.Qty,0) Qty
FROM RE_ES_SwitchBoard_Mast SM
left outer join RE_ES_Estimations ES on SM.PrCode=ES.PrCode and
Sm.SBType=ES.SBType and SM.SBName=ES.SBName
Where SM.PrCode='PR004' and SM.SBType='SMDB'
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
)AS pvthere
A verifiable example here:
CREATE TABLE #sample
(
SBType varchar(MAX),
SBName varchar(MAX),
Qty int
)
INSERT INTO #sample VALUES ('SMDB','SB01',1)
INSERT INTO #sample VALUES ('SMDB','SB01',4)
INSERT INTO #sample VALUES ('SMDB','SB02',2)
INSERT INTO #sample VALUES ('SMDB','SB02',5)
INSERT INTO #sample VALUES ('SMDB','SB03',3)
INSERT INTO #sample VALUES ('SMDB','SB03',6)
SELECT SB01, SB02, SB03
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY SBName ORDER BY Qty) rn, SBName,ISNULL(Qty,0) Qty
FROM #sample
) as s
PIVOT
(
Max(Qty)
FOR [SBName] IN (SB01, SB02, SB03)
) AS pvthere
DROP TABLE #sample
Dynamic query is the only way to use varchar columns in pivot. Have a look at below code to get idea.
First step is to generate comma separated list of items for column you need to use in pivot.
Then you can use this generated list in dynamic query for pivot columns.
Note: For example purpose I have used temp table. Replace it with your actual table.
CREATE TABLE #temptable
(
SBType VARCHAR(20),
SBName VARCHAR(20),
Qty INT
)
INSERT INTO #temptable SELECT 'SMDB','SB01',1
INSERT INTO #temptable SELECT 'SMDB','SB01',4
INSERT INTO #temptable SELECT 'SMDB','SB02',2
INSERT INTO #temptable SELECT 'SMDB','SB02',5
INSERT INTO #temptable SELECT 'SMDB','SB03',3
INSERT INTO #temptable SELECT 'SMDB','SB03',6
SELECT * FROM #temptable
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(SBName)
from #temptable
group by SBName
order by SBName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #cols
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT *
FROM
(
SELECT SBType,SBName,Qty,
row_number() over (partition by SBName order by Qty) as rn
FROM #temptable
) src
PIVOT
(
MIN(Qty)
FOR SBName IN (' + #cols + ')
) piv;'
EXEC(#query)
DROP TABLE #temptable
I am sure this is a quick one, but tried everything, well apart from the answer. I have a pivot script which works fine, apart from the result in the columns, I need to divide by 100. So result / 100
Script is
SELECT
USERID AS UserId,APPOINTMENTDATE,
isnull ([1],0) as'other',isnull ([2],0) as 'Medicare'
FROM
(SELECT invoices.USERID, APPOINTMENTDATE,
[total],PAYERCODE,users.LOCATIONID
FROM APPOINTMENTS
where
APPOINTMENTDATE between '2017-01-22' and '2017-01-23'
) AS SourceTable
PIVOT
(
Sum(total)
FOR PAYERCODE IN ([1], [2])
) AS PivotTable;
So what I would like to do is :
Sum(total) change to something like Sum(total/100)
Any thoughts..
Cheers
Added this sample as it works. Heres the fiddle: http://sqlfiddle.com/#!6/8eef1/1/0
The issue with your sample code is that you are trying to do a select on the table itself rather than the Pivot. You will need to define your columns inside your 'SourceTable' and then Pivot the PayerCode and then do your Select * FROM (Sourcetable Pivottable)
drop table TestingPivot
Create table TestingPivot
( Total INT,
Payercode INT,
LocationID INT,
UserID INT,
Name VARCHAR(100) )
Insert into TestingPivot values(100, 1, 1, 1, 'Ryan')
Insert into TestingPivot values(200, 2, 2, 2, 'Mike')
Insert into TestingPivot values(300, 3, 3, 3, 'John')
Select * from (
Select Total/100 as NewTotal, PayerCode, UserID, LocationID,
case when PayerCode = 1 then 'Other'
when PayerCode = 2 then 'Medicare'
else ''
end as Type
from TestingPivot
) as t
Pivot
(
Sum(NewTotal)
FOR PayerCode IN ("1","2")
) as P;
I have the below table (#temp1) where I need to replace the string in the column'Formula' with the matching input 'VALUE' column based on the group 'Yearmonth'.
The 'Formula' column may be of any mathematical expression for better understanding I have mentioned a simple example below.
IDNUM formula INPUTNAME VALUE YEARMONTH
---------------------------------------------------------------------
1 imports(398)+imports(399) imports(398) 17.000 2003:1
2 imports(398)+imports(399) imports(398) 56.000 2003:2
3 imports(398)+imports(399) imports(399) 15.000 2003:1
4 imports(398)+imports(399) imports(399) 126.000 2003:2
For eg :From the above table i need the output as
Idnum Formula Yearmonth
1. 17.00 +15.00 2003:1
2. 56.00 +126.00 2003:2
I tried with the below different query from various suggestions but coludnt achieve it. Could someone help me this out ?
Type1 :
SELECT
REPLACE(FORMULA, INPUTName, AttributeValue) AS realvalues,
yearmonth
FROM #temp1
GROUP BY yearmonth
TYPE2 :
USING XML PATH... In this case it got worked but I need to replace only the strings with the values and not to stuff the strings based on mathematcal operation.(Because the formula might be of any type).
SELECT
IDNUM = MIN(IDNUM),
FORMULA =
(SELECT STUFF(
(SELECT ' +' + CONVERT(VARCHAR(10), Value)
FROM #temp1
WHERE YEARMONTH = t1.YEARMONTH
FOR XML PATH(''))
,1, 2, '')),
YEARMONTH
FROM #TEMP1 t1
GROUP BY YEARMONTH
TYPE3:Using Recursions...This is returning only the null values...
;with t as (
select t.*,
row_number() over (partition by yearmonth order by idnum) as seqnum,
count(*) over (partition by yearmonth) as cnt
from #temp1 t
)
,cte as (
select t.seqnum, t.yearmonth, t.cnt,
replace(formula, inputname, AttributeValue) as formula1
from t
where seqnum = 1
union all
select cte.seqnum, cte.yearmonth, cte.cnt,
replace(CTE.formula1, T.inputname, T.AttributeValue) as formula2
from cte join
t
on cte.yearmonth = t.yearmonth
AND cte.seqnum = t.seqnum + 1
)
select row_number() over (order by (select null)) as id,formula1
from cte
where seqnum = cnt
This is full working example using recursive CTE:
DECLARE #DataSource TABLE
(
[IDNUM] TINYINT
,[formula] VARCHAR(MAX)
,[INPUTNAME] VARCHAR(128)
,[VALUE] DECIMAL(9,3)
,[YEARMONTH] VARCHAR(8)
);
INSERT INTO #DataSource ([IDNUM], [formula], [INPUTNAME], [VALUE], [YEARMONTH])
VALUES ('1', 'imports(398)+imports(399)', 'imports(398)', '17.000', '2003:1')
,('2', 'imports(398)+imports(399)', 'imports(398)', '56.000', '2003:2')
,('3', 'imports(398)+imports(399)', 'imports(399)', '15.000', '2003:1')
,('4', 'imports(398)+imports(399)', 'imports(399)', '126.000', '2003:2')
,('5', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(391)', '5.000', '2003:3')
,('6', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(392)', '10.000', '2003:3')
,('7', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(393)', '3.000', '2003:3')
,('8', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(394)', '-5.000', '2003:3');
WITH DataSource AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY [YEARMONTH] ORDER BY [IDNUM]) AS [ReplacementOrderID]
,[YEARMONTH]
,[formula]
,[INPUTNAME] AS [ReplacementString]
,[VALUE] AS [ReplacementValue]
FROM #DataSource
),
RecursiveDataSource AS
(
SELECT [ReplacementOrderID]
,[YEARMONTH]
,REPLACE([formula], [ReplacementString], [ReplacementValue]) AS [formula]
FROM DataSource
WHERE [ReplacementOrderID] = 1
UNION ALL
SELECT DS.[ReplacementOrderID]
,DS.[YEARMONTH]
,REPLACE(RDS.[formula], DS.[ReplacementString], DS.[ReplacementValue]) AS [formula]
FROM RecursiveDataSource RDS
INNER JOIN DataSource DS
ON RDS.[ReplacementOrderID] + 1 = DS.[ReplacementOrderID]
AND RDS.[YEARMONTH] = DS.[YEARMONTH]
)
SELECT RDS.[YEARMONTH]
,RDS.[formula]
FROM RecursiveDataSource RDS
INNER JOIN
(
SELECT [YEARMONTH]
,MAX([ReplacementOrderID]) AS [ReplacementOrderID]
FROM DataSource
GROUP BY [YEARMONTH]
) DS
ON RDS.[YEARMONTH] = DS.[YEARMONTH]
AND RDS.[ReplacementOrderID] = DS.[ReplacementOrderID]
ORDER BY RDS.[YEARMONTH]
Generally, you simply want to perform multiple replacements over a string in one statement. You can have many replacement values, just play with the MAXRECURSION option.
--Create sample data
DROP TABLE #temp1
CREATE TABLE #temp1 (IDNUM int, formula varchar(max), INPUTNAME varchar(max), VALUE decimal, YEARMONTH varchar(max))
INSERT INTO #temp1 VALUES
(1, 'imports(398)+imports(399)', 'imports(398)', 17.000, '2003:1'),
(2, 'imports(398)+imports(399)', 'imports(398)', 56.000, '2003:2'),
(3, 'imports(398)+imports(399)', 'imports(399)', 15.000, '2003:1'),
(4, 'imports(398)+imports(399)', 'imports(399)', 126.000, '2003:2')
--Query
;WITH t as (
SELECT formula, YEARMONTH, IDNUM
FROM #temp1
UNION ALL
SELECT REPLACE(a.formula, b.INPUTNAME, CAST(b.VALUE AS varchar(100))) AS formula, a.YEARMONTH, a.IDNUM
FROM t a
JOIN #temp1 b ON a.YEARMONTH = b.YEARMONTH AND a.formula LIKE '%' + b.INPUTNAME + '%'
)
SELECT MIN(IDNUM) AS IDNUM, formula, YEARMONTH
FROM t
WHERE formula not LIKE '%imports(%'
GROUP BY formula, YEARMONTH