I am trying to create a pivot table in SQL 2008R2. I'm trying to reproduce and Access Pivot table in SQL. When I run the following script, I get one record for each pivot column instead of one record with two populated pivoted columns.
SELECT * FROM
(SELECT DataView.MAEID
, DataView.MoYr
, DataView.ChemicalName
, TblCategories.CatDesc
, DataView.[CAS#]
, DataView.HAP
, Sum([SpecAE]/2000) AS [Total SpecAE Tons]
, SUM([SpecAE]) AS [SpecAE]
FROM TblCategories
INNER JOIN DataView
ON TblCategories.CatID = DataView.Category
GROUP BY DataView.MAEID
, DataView.MoYr
, DataView.ChemicalName
, DataView.[CAS#]
, DataView.HAP
, TblCategories.CatDesc) TBL
PIVOT (
Sum([SpecAE])
FOR CatDesc IN ([INCIINERABLE LIQUIDS], [Supplemental Fuels])
)pvt
Any thoughts?
You haven't provided sample data, so I'll explain your issue with an example. Let's say I have a very simple table with some very simple values:
DECLARE #T TABLE
(
MainColumn INT NOT NULL,
PivotColumn INT NOT NULL,
SumColumn INT NOT NULL
);
INSERT #T VALUES
(1, 1, 10), (1, 1, 20), (1, 1, 30),
(1, 2, 60),
(1, 3, 50),
(2, 1, 10), (2, 1, 15),
(2, 2, 20),
(3, 1, 10),
(3, 2, 10),
(4, 1, 150);
If I perform the following query:
SELECT MainColumn,
PivotColumn,
PivotValue = SUM(SumColumn),
OtherSum = SUM(SumColumn / 5)
FROM #T
GROUP BY MainColumn, PivotColumn
ORDER BY MainColumn, PivotColumn
I get:
+------------+-------------+------------+----------+
| MainColumn | PivotColumn | PivotValue | OtherSum |
+------------+-------------+------------+----------+
| 1 | 1 | 60 | 12 |
| 1 | 2 | 60 | 12 |
| 1 | 3 | 50 | 10 |
| 2 | 1 | 25 | 5 |
| 2 | 2 | 20 | 4 |
| 3 | 1 | 10 | 2 |
| 3 | 2 | 10 | 2 |
| 4 | 1 | 150 | 30 |
+------------+-------------+------------+----------+
Now if I use a PIVOT to pivot the PivotValue for each PivotColumn, it's going to group by MainColumn AND OtherSum column. A pivot groups by every column that isn't part of the pivot.
So my result set will be split into (MainColumn=1, OtherSum=12), (MainColumn=1, OtherSum=10), (MainColumn=2, OtherSum=5), (MainColumn=2, OtherSum=4), etc... I will get a new line for each of these values. If the OtherSum value was unique for each line, I'd expect 8 lines with a pivot.
If I remove OtherSum from my result set, my result set is just going to group by MainColumn alone, so it'll all be on one line for each distinct MainColumn value, since that's the only column the pivot would group by.
If getting the other sum value is important, I can do something like the following:
SELECT P.MainColumn,
Val1A = P.[1],
Val1B = P.[1] / 5,
Val2A = P.[2],
Val2B = P.[2] / 5,
Val3A = P.[3],
Val3B = P.[3] / 5
FROM
(
SELECT MainColumn,
PivotColumn,
PivotValue = SUM(SumColumn)
FROM #T
GROUP BY MainColumn, PivotColumn
) AS T
PIVOT
(
SUM(PivotValue) FOR PivotColumn IN ([1], [2], [3])
) AS P;
Related
I've a very large table with ID variables in two columns. I'd like to find the minimum associated ID after applying transitivity. For example, if ID1 = 1 and ID2 = 2 for one record and ID1 = 2 and ID2 = 3 for another record, I'd like the results to ultimately yield three records where the first column has an ID (1, 2, and 3) and the second column has a Min_ID (all equal to 1, in the above example).
I've determined that at most there are 6 ways IDs can be related, and I believe the below is a minimum functioning example.
Is there a more efficient way to do this?
Source
| ID1 | ID2 |
|------|-------|
| 1 | 1 |
| 2 | 2 |
| 1 | 1 |
| 1 | 2 |
| 2 | 11 |
| 11 | 13 |
| 13 | 99 |
| 99 | 1000 |
| 1000 | 97887 |
| 3 | 5 |
| 5 | 17 |
| 17 | 19 |
| 23 | 34 |
Results
| ID | Min_ID |
|-------|--------|
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
| 5 | 3 |
| 11 | 1 |
| 13 | 1 |
| 17 | 3 |
| 19 | 3 |
| 23 | 23 |
| 34 | 23 |
| 99 | 1 |
| 1000 | 1 |
| 97887 | 1 |
Functioning albeit potentially-inefficient working example:
IF OBJECT_ID('tempdb..##IDs') IS NOT NULL DROP TABLE ##IDs
GO
CREATE TABLE ##IDs
(
ID1 int
,ID2 int
);
INSERT INTO ##IDs (ID1, ID2)
VALUES
(1, 1),
(2, 2),
(1, 1),
(1, 2),
(2, 11),
(11, 13),
(13, 99),
(99, 1000),
(1000, 97887),
(3, 5),
(5, 17),
(17, 19),
(23, 34)
;
WITH t1 AS
(
SELECT
ID1
,ID2
FROM ##IDs
UNION
SELECT
ID2
,ID1
FROM ##IDs
),
t2 AS
(
SELECT
*
FROM t1
UNION
SELECT
a.ID1
,b.ID2
FROM t1 a
LEFT JOIN t1 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t1 a
LEFT JOIN t1 b ON
a.ID2 = b.ID1
),
t3 AS
(
SELECT
*
FROM t2
UNION
SELECT
a.ID1
,b.ID2
FROM t2 a
LEFT JOIN t2 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t2 a
LEFT JOIN t2 b ON
a.ID2 = b.ID1
),
t4 AS
(
SELECT
*
FROM t3
UNION
SELECT
a.ID1
,b.ID2
FROM t3 a
LEFT JOIN t3 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t3 a
LEFT JOIN t3 b ON
a.ID2 = b.ID1
)
SELECT
ID1 AS [ID]
,MIN(ID2) Min_ID
FROM t4
GROUP BY
ID1
The complexity here for me was finding an appropriate stopping condition for the linking recursion. That said, it's really quite simple: stop linking when the ID links to itself.
IF OBJECT_ID('tempdb..##IDs') IS NOT NULL DROP TABLE ##IDs
GO
CREATE TABLE ##IDs
(
ID1 int
,ID2 int
);
INSERT INTO ##IDs (ID1, ID2)
VALUES
(1, 1),
(2, 2),
(1, 1),
(1, 2),
(2, 11),
(11, 13),
(13, 99),
(99, 1000),
(1000, 97887),
(3, 5),
(5, 17),
(17, 19),
(23, 34)
;
WITH t1 AS
(
SELECT
ID1
,ID2
FROM ##IDs
UNION
SELECT
ID2
,ID1
FROM ##IDs
UNION ALL
SELECT
c.ID1
,t1.ID2
FROM ##IDs c
INNER JOIN t1 ON
c.ID2 = t1.ID1
WHERE
c.ID1 <> c.ID2
UNION ALL
SELECT
t1.ID2
,c.ID1
FROM ##IDs c
INNER JOIN t1 ON
c.ID2 = t1.ID1
WHERE
c.ID1 <> c.ID2
)
SELECT
ID1 AS [ID]
,MIN(ID2) Min_ID
FROM t1
GROUP BY
ID1
;
I cannot summarize numbers in the table (SQL-Server) after pivoting and I will be very grateful for your advice.
Better if I explain the problem on the example:
Existing table:
+-------+-----------+-----------+-------------------+
| # | $$$$$ | Fire | Water |
+-------+-----------+-----------+-------------------+
| 1 | 5 | 1 | 5 |
| 1 | 4 | 1 | 5 |
| 1 | 10 | 1 | 5 |
| 2 | 3 | 3 | 8 |
| 2 | 4 | 3 | 8 |
+-------+-----------+-----------+-------------------+
Desired output:
+-------+-----------+-----------+-------------------+
| # | $$$$$ | Fire | Water |
+-------+-----------+-----------+-------------------+
| 1 | 19 | 1 | 5 |
| 2 | 7 | 3 | 8 |
+-------+-----------+-----------+-------------------+
I tend to believe that I already tried all the solutions I found with summarizing and grouping by, but it was not solved, so I rely on you. Thanks in advance. The code I used to create the table:
WITH Enerc AS
(
SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
FROM
data1 AS a1
LEFT JOIN
data2 AS a2 ON a1.[id] = a2.[id]
)
SELECT *
FROM Enerc
PIVOT
(SUM(gross_claim) FOR [cause_of_loss] IN ([Fire], [Water])) AS PivotTable;
No need to pivot. Your desired result should be got by grouping and using SUM:
SELECT
a1.[#],
SUM(a1.[$$$$$]),
a1.[Fire]
a1.[Water]
from data1 as a1
group by a1.[#], a1.[Fire], a1.[Water]
Let me show an example:
DECLARE #Hello TABLE
(
[#] INT,
[$$$$$] INT,
[Fire] INT,
[Water] INT
)
INSERT INTO #Hello
(
#,
[$$$$$],
Fire,
Water
)
VALUES
( 1, -- # - int
5, -- $$$$$ - int
1, -- Fire - int
5 -- Water - int
)
, (1, 4, 1, 5)
, (1, 10, 1, 5)
, (2, 3, 3, 8)
, (2, 4, 3, 8)
SELECT
h.#,
SUM(h.[$$$$$]),
h.Fire,
h.Water
FROM #Hello h
GROUP BY h.#, h.Fire, h.Water
try group by after the pivot.
With Enerc as
(SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
from data1 as a1
left join data2 as a2
on a1.[id] = a2.[id]
)
select *
into tmp
from Enerc
PIVOT
(sum(gross_claim)
FOR [cause_of_loss] in (
[Fire], [Water]))
as PivotTable
select [#], sum([$$$$$])as [$$$$$], Fire, Water
from #tmp
group by [#],Fire, Water
EDIT: in case of permission denied:
With Enerc as
(SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
from data1 as a1
left join data2 as a2
on a1.[id] = a2.[id]
),phase2 as(
select *
from Enerc
PIVOT
(sum(gross_claim)
FOR [cause_of_loss] in (
[Fire], [Water]))
as PivotTable)
select [#], sum([$$$$$])as [$$$$$], Fire, Water
from phase2
group by [#],Fire, Water
I have a table like:
+====+========+========+
| Id | name | value |
+====+========+========+
| 1 | a | 7 |
+----+--------+--------+
| 2 | c | 7 |
+----+--------+--------+
| 1 | g | 1 |
+----+--------+--------+
| 2 | c | 2 |
+----+--------+--------+
| 4 | g | 5 |
+----+--------+--------+
| 6 | t | 4 |
+----+--------+--------+
I need to write two (2) queries to generate two reports, according to this two conditions:
Report Output1=if id and name same (id,name,val)
Report Output2=if id same but different name(id,name,val)
How to write those two queries?
Your conditions is not clear, but mybe this what you want:
DECLARE #T TABLE (Id INT, Name VARCHAR(25), Value INT);
DECLARE #YourId INT = 1;
DECLARE #YourName VARCHAR(25) ='a';
/**/
INSERT INTO #T VALUES
(1, 'a', 7),
(2, 'c', 7),
(1, 'g', 1),
(2, 'c', 2),
(4, 'g', 5),
(6, 't', 4);
/*First query*/
SELECT *
FROM #T
WHERE ID = #YourID AND Name = #YourName;
/*Second query*/
SELECT *
FROM #T
WHERE ID = #YourID;
If you want the result of both queries in one result, then you can use UNION ALL as:
SELECT *
FROM #T
WHERE ID = #YourID AND Name = #YourName
UNION ALL
SELECT *
FROM #T
WHERE ID = #YourID;
Demo.
Well, I am not sure, what OP wanted, but maybe it was something like this?
// records where id and value are the same
SELECT * FROM #T WHERE ID=Value;
// other records having the same ids as abobe, but DIFFERENT values
SELECT * FROM #T WHERE ID IN
(SELECT ID FROM #T WHERE ID=Value)
AND Id!=Value;
Results:
Id Name Value
1 g 1
2 c 2
Id Name Value
1 a 7
2 c 7
Thanks to #Sami for providing the fiddle which I modified into this DEMO.
I have 3 tables in Table 2 we have columns with columnName Field they can grow Dynamically at that time we have just 5 columns for Each CTypeId they can be 6 or 10 etc. In Table3 we have the column values.
For example AccountManager From Table 2 have value in Table 3 Jack / Kate
similarly other columns and their values are
ColumnName | Values
Channel | PS
StartDate | 06/03/2017
I want Result Like this
I have tried using Pivot Function with the following query:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = (select STUFF((select ',' + '[' + Convert(varchar(200), ColumnName) + ']' from CharityTypeInformationDynamicFields FOR XML PATH('')), 1,1, ''))
Declare #sql nvarchar(max)
= 'Select *
from
(select cd.Id, cd.Value, ci.ColumnName
from Table3 cd
Inner Join Table2 ci
on ci.Id = cd.DynamicFieldID
) as s
Pivot(MAX(Value) ForColumnName IN ('+#columns+')) as pvt'
Select #sql
But the query gives the result:
What do I need to change to achieve my desired output?
There are a few issues that you need to solve in order to get the result you desire. But before trying a dynamic sql version of a query I'd always recommend that you try get your final result by writing a hard-coded or static version first. This allows you to get the desired result without bugs and then convert it to dynamic sql as your final query.
First, let's get your table structures and sample data into a reusable script. It appears that you only need table2 and table3 to get your end result:
create table #table2
(
id int,
ctypeid int,
columnname varchar(50)
)
insert into #table2
values
(1, 20, 'Account Manager'), (2, 20, 'Channel'),
(3, 20, 'Start Date'), (4, 20, 'End Date'),
(5, 20, 'Gross Annual'), (6, 6, 'Account Manager'),
(7, 6, 'Channel'), (8, 6, 'Start Date'),
(9, 6, 'End Date'), (10, 6, 'Gross Annual');
create table #table3
(
id int,
table2id int,
value varchar(50)
)
insert into #table3
values
(1, 1, 'Jack / Kate'), (2, 2, 'PS'), (3, 3, '06/03/2017'),
(4, 4, '07/03/2017'), (5, 5, '2500'), (6, 6, 'Ollie'),
(7, 7, 'D2D'), (8, 8, '06/03/2017'), (9, 9, '06/03/2017'),
(10, 10, '5232'), (11, 1, 'Jack'), (12, 2, 'PSP'),
(13, 3, '06/03/2017'), (14, 4, '07/03/2017'), (15, 5, '7000'),
(16, 1, 'Jack Sparrow'), (17, 2, 'PS Sparrow'), (1, 3, '06/03/2017'),
(19, 4, '07/03/2017'), (20, 5, '3000'), (21, 6, 'John'),
(22, 7, 'JEDF'), (23, 8, '06/03/2017'), (24, 9, '06/03/2017'),
(25, 10, '5232');
Next, you need to write your PIVOT query. Your final result only includes the values from 3 columns CTypeId, Value, and ColumnName, so the start of your query PIVOT would be:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
Demo. But since you're aggregating string values in the Value column, you will only return one row for each CTypeId:
+---------+-----------------+---------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+---------+------------+------------+---------------+
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+---------+------------+------------+---------------+
which is not what you want, so you need to do something to allow for multiple rows. If you look at a sample of the data that is returned by the subquery:
+---------+-------------+------------------+
| CTypeId | Value | ColumnName |
+---------+-------------+------------------+
| 20 | Jack / Kate | Account Manager |
| 20 | PS | Channel |
| 20 | 06/03/2017 | Start Date |
| 20 | 07/03/2017 | End Date |
| 20 | 2500 | Gross Annual |
| 6 | Ollie | Account Manager |
| 6 | D2D | Channel |
| 6 | 06/03/2017 | Start Date |
| 6 | 06/03/2017 | End Date |
| 6 | 5232 | Gross Annual |
+---------+-------------+------------------+
You'll see that you have unique data over a combination of CTypeId and ColumnName values, so you can create a unique row number using the windowing function row_number in your subquery which can be used to uniquely group the data for a pivot. By changing the above PIVOT code to:
select
CTypeId,
[Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual]
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) d
pivot
(
max(Value)
for ColumnName in ([Account Manager], [Channel], [Start Date],
[End Date], [Gross Annual])
) piv
order by CTypeId
See demo, you get the desired result:
+---------+-----------------+------------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date | End Date | Gross Annual |
+---------+-----------------+------------+------------+------------+---------------+
| 6 | John | D2D | 06/03/2017 | 06/03/2017 | 5232 |
| 6 | Ollie | JEDF | 06/03/2017 | 06/03/2017 | 5232 |
| 20 | Jack | PS | 06/03/2017 | 07/03/2017 | 2500 |
| 20 | Jack / Kate | PS Sparrow | 06/03/2017 | 07/03/2017 | 3000 |
| 20 | Jack Sparrow | PSP | 06/03/2017 | 07/03/2017 | 7000 |
+---------+-----------------+------------+------------+------------+---------------+
Once you've got your final result you want, it's easy to convert the query to dynamic SQL:
Declare #Columns nvarchar(max)
Declare #a nvarchar(max)
Set #Columns = stuff((select distinct ',' + quotename(ColumnName)
from #table2
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');
Declare #sql nvarchar(max)
= 'Select CTypeId, '+#Columns+'
from
(
select ci.CTypeId, cd.Value, ci.ColumnName,
rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
from #Table3 cd
Inner Join #Table2 ci
on ci.Id = cd.Table2Id
) as s
Pivot(MAX(Value) For ColumnName IN ('+#columns+')) as pvt
order by CTypeId'
execute(#sql);
See Demo. This gives the same result as the hard-coded version with the flexibility of dynamic sql.
My script
SELECT ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id, opt.Description, count(ans.Option_Id) as [Count]
FROM Answers ans
LEFT OUTER JOIN Questions que
ON ans.Questions_Id = que.Id
LEFT OUTER JOIN Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1
and ans.Questions_Id = 1
GROUP By ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id, opt.Description
ORDER BY 2, 5 desc
I am trying to get the top number responses (Description) for each Answer_Numeric. The result at the moment looks like this:
| Questions_Id | Answer_Numeric | Option_Id | Description | Count
-----------------------------------------------------------------------
| 1 | 1 | 27 | Technology | 183
| 1 | 1 | 24 | Personal Items | 1
| 1 | 2 | 28 | Wallet / Purse | 174
| 1 | 2 | 24 | Personal Items | 3
| 1 | 2 | 26 | Spiritual | 1
| 1 | 3 | 24 | Personal Items | 53
| 1 | 3 | 25 | Food / Fluids | 5
| 1 | 3 | 26 | Spiritual | 5
| 1 | 3 | 27 | Technology | 1
| 1 | 3 | 28 | Wallet / Purse | 1
As from the example data from above I need it to look like this:
| Questions_Id | Answer_Numeric | Option_Id | Description | Count
-----------------------------------------------------------------------
| 1 | 1 | 27 | Technology | 183
| 1 | 2 | 28 | Wallet / Purse | 174
| 1 | 3 | 24 | Personal Items | 53
I am pretty sure that I need to have a max or something in my Having clause but everything I have tried has not worked. Would really appreciate any help on this.
You can use ROW_NUMBER:
SELECT Questions_Id, Answer_Numeric, Option_Id, Description, [Count]
FROM (
SELECT ans.Questions_Id,ans.Answer_Numeric,ans.Option_Id,
opt.Description, count(ans.Option_Id) as [Count],
ROW_NUMBER() OVER (PARTITION BY ans.Questions_Id, ans.Answer_Numeric
ORDER BY count(ans.Option_Id) DESC) AS rn
FROM Answers ans
LEFT OUTER JOIN Questions que
ON ans.Questions_Id = que.Id
LEFT OUTER JOIN Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1
and ans.Questions_Id = 1
GROUP By ans.Questions_Id,
ans.Answer_Numeric,
ans.Option_Id,
opt.Description) AS t
WHERE t.rn = 1
ORDER BY 2, 5 desc
Alternatively you can use RANK so as to handle ties, i.e. more than one rows per Questions_Id, Answer_Numeric partition sharing the same maximum Count number.
Use row_number():
SELECT *
FROM (SELECT ans.Questions_Id, ans.Answer_Numeric, ans.Option_Id, opt.Description,
count(*) as cnt,
row_number() over (partition by ans.Questions_Id, ans.Answer_Numeric
order by count(*) desc) as seqnum
FROM Answers ans LEFT OUTER JOIN
Questions que
ON ans.Questions_Id = que.Id LEFT OUTER JOIN
Options opt
ON ans.Option_Id = opt.Id
WHERE que.Survey_Id = 1 and ans.Questions_Id = 1
GROUP By ans.Questions_Id, ans.Answer_Numeric, ans.Option_Id, opt.Description
) t
WHERE seqnum = 1
ORDER BY 2, 5 desc;
we can get the same result set in different ways and I have taken sample data set you just merge your joins in this code
declare #Table1 TABLE
(Id int, Answer int, OptionId int, Description varchar(14), Count int)
;
INSERT INTO #Table1
(Id, Answer, OptionId, Description, Count)
VALUES
(1, 1, 27, 'Technology', 183),
(1, 1, 24, 'Personal Items', 1),
(1, 2, 28, 'Wallet / Purse', 174),
(1, 2, 24, 'Personal Items', 3),
(1, 2, 26, 'Spiritual', 1),
(1, 3, 24, 'Personal Items', 53),
(1, 3, 25, 'Food / Fluids', 5),
(1, 3, 26, 'Spiritual', 5),
(1, 3, 27, 'Technology', 1),
(1, 3, 28, 'Wallet / Purse', 1)
;
SELECT tt.Id, tt.Answer, tt.OptionId, tt.Description, tt.Count
FROM #Table1 tt
INNER JOIN
(SELECT OptionId, MAX(Count)OVER(PARTITION BY OptionId ORDER BY OptionId)AS RN
FROM #Table1
GROUP BY OptionId,count) groupedtt
ON
tt.Count = groupedtt.RN
WHERE tt.Count <> 5
GROUP BY tt.Id, tt.Answer, tt.OptionId, tt.Description, tt.Count
OR
select distinct Count, Description , Id , Answer from #Table1 e where 1 =
(select count(distinct Count ) from #Table1 where
Count >= e.Count and (Description = e.Description))