This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 1 year ago.
I tried to search before asking, but I didn't find something similar to the one I try to figure out. I use sql server to achieve that.
Current Situation
Target
Based on the Year, I want to pivot:
Col as the name of a new column
value should be the value of the column.
In that example, the first 36 rows should become one row. For every year there should be one row.
A B C D YEAR E F HiBioInsec HiChemInsec etc
76 1 191 4 2020 5000 2000
76 1 191 4 2021 5000 2000
I tried with pivot and max but I didn't got the expected output.
Any thoughts?
PIVOT seems like exactly what you need, actually. Does yours look like this at all? This worked for me.
CREATE TABLE dbo.YourTable
(
A int,
B int,
C int,
D int,
[Year] int,
E int,
F int,
col varchar(30),
[value] int
);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2020, 'HiBioInsec', 5000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2020, 'HiChemInsec', 2000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2021, 'HiBioInsec', 5000);
INSERT dbo.YourTable (A,B,C,D,[Year],col, [value])
VALUES (76, 1, 191, 4, 2021, 'HiChemInsec', 2000);
SELECT A,B,C,D,[Year],E,F
, pvt.HiBioInsec
, pvt.HiChemInsec
FROM
(
SELECT A,B,C,D,[Year],E,F,col, SUM([value]) AS SumValue
FROM dbo.YourTable [tbl]
GROUP BY A,B,C,D,[Year],E,F,col
) src
PIVOT (SUM(SumValue) FOR col IN ([HiBioInsec],[HiChemInsec])) pvt
Can you post your SQL?
You could also try something like this, but I don't see how you can get around using an aggregate.
SELECT A,B,C,D,[Year],E,F
, SUM(HiBioInsec) AS HiBioInsec
,SUM(HiChemInsec) AS HiChemInsec
FROM
(
SELECT A,B,C,D,[Year],E,F, [value] AS HibioInsec ,NULL AS HiChemInsec
FROM dbo.YourTable WHERE col = 'HiBioInsec'
UNION ALL
SELECT A,B,C,D,[Year],E,F, NULL AS HibioInsec , [value] AS HiChemInsec
FROM dbo.YourTable WHERE col = 'HiChemInsec'
) tbl
GROUP BY A,B,C,D,[Year],E,F
Related
Scenario: I have a table with Year and Gap columns. What I need the output as, starting from the given year value it incremented up to the value in the gap column.
i.e., If the YearVal is 2001, and Gap is 3, I need the output as
Result
--------
2001
2002
2003
What I have tried:
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES (3, 2001);
;WITH FinalResult AS (
SELECT YearVal AS [YR] FROM #ResultYears
UNION ALL
SELECT [YR] + 1 FROM FinalResult
WHERE [YR] + 1 <= (SELECT YearVal + (Gap -1) FROM #ResultYears)
)
SELECT * FROM FinalResult;
db<>fiddle demo with one entry in the table.
Using the query above, I can achieve the expected result. But if the table have more than one entry, the query is not working.
i.e., If I have the entries in the table as below:
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
How can I modify the query to achieve my expected result?
db<>fiddle demo with more than one entry in the table.
Is this what you're after?
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 AS I
FROM N N1, N N2), --100 is more than enough
Years AS(
SELECT RY.YearVal + T.I AS [Year],
RY.Gap,
RY.YearVal
FROM #ResultYears RY
JOIN Tally T ON RY.Gap > T.I)
SELECT *
FROM Years Y
ORDER BY Y.YearVal;
Personally I prefer a tally table over a rCTE; they are far quicker, especially with large datasets, or where the rCTE would have to do a high volume of recursion.
Demo on db<>fiddle
Initially Create one user defined table type function which return the Gap years
CREATE FUNCTION [dbo].[ufn_GetYears]
(
#i_Gap INT,#Year INT
)
RETURNS #Temp TABLE
(
Years INT
)
AS
BEGIN
;WITH CTE
AS
(
SELECT 1 AS Seq,DATEFROMPARTS ( #Year,01,01) AS Years
UNION ALL
SELECT seq +1,DATEADD(YEAR,1,Years)
FROM Cte
WHERE Seq < #i_Gap
)
INSERT INTO #Temp
SELECT DATEPART(YEAR,Years )
FROM CTE
RETURN
END
Sample Data
DECLARE #ResultYears TABLE
(Gap INT,
YearVal INT
);
INSERT INTO #ResultYears (Gap, YearVal) VALUES
(3, 2001), (4, 2008), (1, 2014), (2, 2018);
Sql Query to get the expected result using CROSS APPLY
SELECT R.Gap,dt.Years
FROM #ResultYears R
CROSS APPLY [dbo].[ufn_GetYears](R.Gap,R.YearVal) AS dt
Result
Gap Years
---------
3 2001
3 2002
3 2003
4 2008
4 2009
4 2010
4 2011
1 2014
2 2018
2 2019
If for a reason, you prefer recursive CTE (which is definetly slower)
DECLARE #ResultYears TABLE (Gap INT, YearVal INT);
INSERT INTO #ResultYears (Gap, YearVal) VALUES (3, 2001), (4, 2008), (1, 2014), (2, 2018);
;WITH FinalResult AS (
SELECT YearVal, Gap, YearVal [YR] FROM #ResultYears
UNION ALL
SELECT YearVal, Gap, [YR] + 1
FROM FinalResult
WHERE [YR] + 1 <= YearVal + (Gap -1)
)
SELECT * FROM FinalResult
ORDER BY [YR];
You need to keep original row parameters in the recursive part. this way recursion runs as desired.
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'm trying to create result set with 3 columns. Each column coming from the summation of 1 Column of Table A but grouped by different ID's. Here's an overview of what I wanted to do..
Table A
ID Val.1
1 4
1 5
1 6
2 7
2 8
2 9
3 10
3 11
3 12
I wanted to create something like..
ROW SUM.VAL.1 SUM.VAL.2 SUM.VAL.3
1 15 21 33
I understand that I can not get this using UNION, I was thinking of using CTE but not quite sure with the logic.
You need conditional Aggregation
select 1 as Row,
sum(case when ID = 1 then Val.1 end),
sum(case when ID = 2 then Val.1 end),
sum(case when ID = 3 then Val.1 end)
From yourtable
You may need dynamic cross tab or pivot if number of ID's are not static
DECLARE #col_list VARCHAR(8000)= Stuff((SELECT ',sum(case when ID = '+ Cast(ID AS VARCHAR(20))+ ' then [Val.1] end) as [val.'+Cast(ID AS VARCHAR(20))+']'
FROM Yourtable
GROUP BY ID
FOR xml path('')), 1, 1, ''),
#sql VARCHAR(8000)
exec('select 1 as Row,'+#col_list +'from Yourtable')
Live Demo
I think pivoting the data table will yield the desired result.
IF OBJECT_ID('tempdb..#TableA') IS NOT NULL
DROP TABLE #TableA
CREATE TABLE #TableA
(
RowNumber INT,
ID INT,
Value INT
)
INSERT #TableA VALUES (1, 1, 4)
INSERT #TableA VALUES (1, 1, 5)
INSERT #TableA VALUES (1, 1, 6)
INSERT #TableA VALUES (1, 2, 7)
INSERT #TableA VALUES (1, 2, 8)
INSERT #TableA VALUES (1, 2, 9)
INSERT #TableA VALUES (1, 3, 10)
INSERT #TableA VALUES (1, 3, 11)
INSERT #TableA VALUES (1, 3, 12)
-- https://msdn.microsoft.com/en-us/library/ms177410.aspx
SELECT RowNumber, [1] AS Sum1, [2] AS Sum2, [3] AS Sum3
FROM
(
SELECT RowNumber, ID, Value
FROM #TableA
) a
PIVOT
(
SUM(Value)
FOR ID IN ([1], [2], [3])
) AS p
This technique works if the ids you are seeking are constant, otherwise I imagine some dyanmic-sql would work as well if changing ids are needed.
https://msdn.microsoft.com/en-us/library/ms177410.aspx
I need show sum col(item) under col with SQL code ? it's possible
Code item
---- ----
1 30
3 40
4 50
9 80
---- ----
Total 200
Use Rollup to get the summary row
SELECT CASE
WHEN Grouping(code) = 1 THEN 'Total'
ELSE Cast(code AS VARCHAR(50))
END,
Sum(item)
FROM Yourtable
GROUP BY code WITH rollup
DECLARE #Table1 TABLE
(Code int, item int)
;
INSERT INTO #Table1
(Code, item)
VALUES
(1, 30),
(3, 40),
(4, 50),
(9, 80)
;
Script :
select Code , sum(item)item
from #Table1
group by GROUPING SETS((Code) , ())
order by Code DESC
select * from (select * from #Table1
union
select null, sum(item) item from #Table1)a
order by item
Select
Code,
item
from
# table_name
Union All
select
Null,
sum(item)item
from
# table_name
As we are using union all so distinct and order by operation will be saved.
I'm trying to convert this table
ID TestID Elapsed ActionID
===================================
1 1 16 a1
2 1 17 a2
3 1 13 a3
4 1 14 a4
5 2 19 a1
6 2 21 a2
7 2 11 a3
8 2 22 a4
To this
TestID a1 a2 a3 a4
======================================
1 16 17 13 14
2 19 21 11 22
is this possible?
Yes, if there is only one action id for each testid
There is the pivot operator that Ajoe mentioned, but I think the traditional
syntax is easier to understand (if not immediately obvious).
You group rows by testid, so you will get one row of results
per each testid. What you select is the "max" in each group where the acitionid is a certain one. Or the min, or the average, or the sum - this is
predicated on there being only one item in each group.
SELECT testid,
MAX(CASE WHEN actionid = 'a1' THEN elapsed ELSE null END) AS a1,
MAX(CASE WHEN actionid = 'a2' THEN elapsed ELSE null END) AS a2,
MAX(CASE WHEN actionid = 'a3' THEN elapsed ELSE null END) AS a3,
MAX(CASE WHEN actionid = 'a4' THEN elapsed ELSE null END) AS a4
FROM results
GROUP BY testid
If you are using SQL Server 2005 (or above), here's the query, with proof of concept. Enjoy:
--Proof of concept structure and data creation
create table #t (ID int, TestID int, Elapsed int, ActionID varchar(10))
insert into #t (ID, TestID, Elapsed, ActionID) values
(1, 1, 16, 'a1'),
(2, 1, 17, 'a2'),
(3, 1, 13, 'a3'),
(4, 1, 14, 'a4'),
(5, 2, 19, 'a1'),
(6, 2, 21, 'a2'),
(7, 2, 11, 'a3'),
(8, 2, 22, 'a4');
--end of structure and data creating
--actual query starts here
DECLARE #cols VARCHAR(1000)
DECLARE #sqlquery VARCHAR(2000)
SELECT #cols = STUFF(( SELECT distinct ',' + QuoteName([ActionID])
FROM #t FOR XML PATH('') ), 1, 1, '')
SET #sqlquery = 'SELECT * FROM
(SELECT TestID, Elapsed, ActionID
FROM #t ) base
PIVOT (SUM(Elapsed) FOR [ActionID]
IN (' + #cols + ')) AS finalpivot'
--Depending on your approach, you might want to use MAX instead of SUM.
--That will depend on your business rules
EXECUTE ( #sqlquery )
--query ends here
--proof of concept cleanup
drop table #t;
This will work no matter how many different values you have in ActionID. It dynamically assembles a query with PIVOT. The only way you can do PIVOT with dynamic columns is by assembling the the query dynamically, which can be done in SQL Server.
Other examples:
SQL Server PIVOT perhaps?
Pivot data in T-SQL
How do I build a summary by joining to a single table with SQL Server?