SQL Server Pivot on this data - sql-server

I'm having a rough go trying to pivot my data :( It is dynamic data and structured like this:
| Date | Source | Amount |
--------------------------------------------
| 12/1/2016 | Source1 | $0 |
| 12/1/2016 | Source2 | $2 |
| 12/1/2016 | Source3 | $5 |
| 12/1/2016 | Source4 | $4 |
There can be unlimited sources and I want to pivot it by source/date:
| Date | Source1 | Source 2 | Source 3 | Source 4 |
--------------------------------------------------------------------------------------
| 12/1/2016 | $0 | $2 | $5 | $4 |
Something like that, anyway.
I have tried coding a lot of ways, so I'll just put in what I thought it could be:
SELECT myDate , Source, Amount
FROM mydb
PIVOT
(max(source) FOR source IN (select distinct source from mydb) as myPivotTable
WHERE (myDate > #StartDate)
of course, that doesn't work. This was going to be part of a stored procedure, just not quite there. Was hoping to pivot on that data so I can do some line trends in SSRS.
I also followed another example and tried this:
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(source)
from mydb where myDate > #StartDate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
And then tried to use that in the place of the "select distinct". However, the sources are text and that didn't seem to do much for me.

Here is a simple Dynamic Pivot on Source
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([Source]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Date],' + #SQL + '
From YourTable
Pivot (sum(Amount) For [Source] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Date Source1 Source2 Source3 Source4
2016-12-01 0 2 5 4
Now, If you want Source to be sequenced Like Source 1, Source 2 it would require just a minor tweak
FYI the generated SQL would look like this
Select [Date],[Source1],[Source2],[Source3],[Source4]
From YourTable
Pivot (sum(Amount) For [Source] in ([Source1],[Source2],[Source3],[Source4]) ) p

Related

Convert Access TRANSFORM/PIVOT query to SQL Server with multiple table [duplicate]

TRANSFORM Avg(CASE WHEN [temp].[sumUnits] > 0
THEN [temp].[SumAvgRent] / [temp].[sumUnits]
ELSE 0
END) AS Expr1
SELECT [temp].[Description]
FROM [temp]
GROUP BY [temp].[Description]
PIVOT [temp].[Period];
Need to convert this query for sql server
I have read all other posts but unable to convert this into the same
Here is the equivalent version using the PIVOT table operator:
SELECT *
FROM
(
SELECT
CASE
WHEN sumUnits > 0
THEN SumAvgRent / sumUnits ELSE 0
END AS Expr1,
Description,
Period
FROM temp
) t
PIVOT
(
AVG(Expr1)
FOR Period IN(Period1, Period2, Period3)
) p;
SQL Fiddle Demo
For instance, this will give you:
| DESCRIPTION | PERIOD1 | PERIOD2 | PERIOD3 |
---------------------------------------------
| D1 | 10 | 0 | 20 |
| D2 | 100 | 1000 | 0 |
| D3 | 50 | 10 | 2 |
Note that When using the MS SQL Server PIVOT table operator, you have to enter the values for the pivoted column. However, IN MS Access, This was the work that TRANSFORM with PIVOT do, which is getting the values of the pivoted column dynamically. In this case you have to do this dynamically with the PIVOT operator, like so:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct
',' +
QUOTENAME(Period)
FROM temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #query = ' SELECT Description, ' + #cols + '
FROM
(
SELECT
CASE
WHEN sumUnits > 0
THEN SumAvgRent / sumUnits ELSE 0
END AS Expr1,
Description,
Period
FROM temp
) t
PIVOT
(
AVG(Expr1)
FOR Period IN( ' + #cols + ')
) p ';
Execute(#query);
Updated SQL Fiddle Demo
This should give you the same result:
| DESCRIPTION | PERIOD1 | PERIOD2 | PERIOD3 |
---------------------------------------------
| D1 | 10 | 0 | 20 |
| D2 | 100 | 1000 | 0 |
| D3 | 50 | 10 | 2 |

Join multiple "exec" as one output SQL server

I'm tring to get multiple results from a dynamic exec to be columns of a table in a single output using SQL Server, right now the exec gives me multiple outputs as different temporal tables.
Is there a way to merge them together and get a single output?
This is my code:
Declare #status nvarchar(255)
Declare POINTER cursor global for select distinct status from intermedio
open POINTER
fetch NEXT from POINTER into #status
while (##FETCH_STATUS=0)
begin
exec('select distinct(
Select COUNT(*) from (
SELECT DISTINCT PKN
FROM INTERMEDIO
WHERE PIECE IN ('+'''+891''','''+75'''+') and ESTATUS = '''+#status+'''
group by PKN
having count(*)=2
) ser) as '+#status);
fetch next from POINTER into #status
end;
close POINTER;
DEALLOCATE POINTER;
Edit: Tried to explain myself better, once again excuse my poor english.
My table has three columns
+-----------+-------+---------+
| PKN | PIECE | ESTATUS |
+-----------+-------+---------+
| Set_one | +891 | A1 |
| Set_one | +75 | A1 |
| Set_one | +45 | A1 |
| Set_two | +891 | A3 |
| Set_two | +75 | A3 |
| Set_three | +700 | B1 |
+-----------+-------+---------+
I'm trying to get the count of the PKNs that have both (+891, +75) and count how many exist under ESTATUS
The output I'm expecting is something like:
+----+----+----+
| A1 | A3 | B1 |
+----+----+----+
| 1 | 1 | 0 |
+----+----+----+
But it gives me the rows on different tables.
The reason I'm doing it like this is because the table can have many different ESTATUS at any time, and many different pieces on different PKN, the result will change constantly and the table generated will be different each time I execute the query.
I'm sorry if the question isn't clear, as english is not my first language.
You are looking for a dynamic pivot and a conditional aggregation. Here's an example.
create table #tempTable (PKN varchar(64), PIECE varchar(64), ESTATUS varchar(2))
insert into #tempTable
values
('Set_one','+891','A1'),
('Set_one','+75','A1'),
('Set_one','+45','A1'),
('Set_two','+891','A3'),
('Set_two','+75','A3'),
('Set_three','+700','B1')
select
ESTATUS
--here is the conditional aggregation
,floor(count(case when PIECE = '+891' or PIECE = '+75' then PKN end)/2) as CT
into #staging
from
#tempTable
group by ESTATUS
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(ESTATUS)
FROM (SELECT DISTINCT ESTATUS FROM #staging) AS ESTATUS
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT ' + #ColumnName + '
FROM #staging
PIVOT(Sum(CT)
FOR ESTATUS IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
drop table #tempTable
drop table #staging
RETURNS
+----+----+----+
| A1 | A3 | B1 |
+----+----+----+
| 1 | 1 | 0 |
+----+----+----+

Need to Dynamically Pivot Table Making Date Column New Table Headers

I've seen just about every kind of pivot example on the Internet, both text based tutorial and video. Not one is solving the issue at hand.
What I want is to pivot the following table:
Sales Table
SalesId | SalesDate | SalesLocation | SalesAmount
-----------------------------------------------------
1 | 2012-03 | New York, NY | 3,000
2 | 2012-04 | Miami, FL | 2,500
3 | 2012-05 | Carmel, CA | 2,850
4 | 2012-06 | Berkeley, CA | 1,900
5 | 2012-07 | Akron, OH | 4,200
6 | 2012-08 | Portland, OR | 2,200
I would like to PIVOT this table to show each row as a column, not distinct SUM of columns. Each row is specifically it's own column, with the date as the header, as if the table has been turned 90 degrees clockwise like an Excel spreadsheet, for date forecasting:
Account Info | 2012-03 | 2012-04 | 2012-05 | 2012-06
---------------------------------------------------------------------
SalesId | 1 | 2 | 3 | 4
SalesLocation | New York, NY | Miami, FL | Carmel, CA | Berkeley, CA
SalesAmount | 3,000 | 2,500 | 2,850 | 1,900
Most of the tutorials on this subject involve creating sums of several rows that have either the same city, the same status or the same type.
For this situation, each row already has a distinct unique date that will never be duplicated, so all records are unique and therefore do not require a function like SUM.
So far, after viewing an incredibly clear solution by JoyceW on the PIVOT Using Date Column thread at the ASP.net forums, I have gotten this far:
declare #cols varchar(max)
select #cols = (select distinct SalesDate from SalesTable for xml path(''))
select #cols = replace(#cols, '', '[')
select #cols = replace(#cols, '', '],')
select #cols = left(#cols, len(#cols) - 1)
declare #query varchar(max)
select #query = 'select * from (select SalesId, SalesLocation, SalesAmount
from SalesTable) src pivot (max(SalesAmount) for SalesDate in ('
+ #cols + ')) piv;'
execute(#query)
While this is not what I was looking for, it's the only tutorial that has allowed me to actually return a result set where the top columns are actually the dates (yay), as seen below:
SalesId | SalesLocation | SalesAmount | 2012-03 | 2012-04 | 2012-05
1 | New York, NY | 3,000 | {null} | {null} | 4
2 | Miami, FL | 2,500 | {null} | 3 | {null}
3 | Carmel, CA | 2,850 | {null} | {null} | {null}
4 | Berkeley, CA | 1,900 | 1 | {null} | {null}
5 | Akron, OH | 4,200 | {null} | {null} | {null}
6 | Portland, OR | 2,200 | {null} | 2 | {null}
Not really sure what's going on here, but it's a lot closer than I was before, I'm actually receiving a result set. Any ideas about how to fine tune this where the rows showing up need to be rotated or pivoted as well?
It seems that I'm only pivoting the headers and not the entire table as I would like. Any suggestions would be greatly appreciated.
As you want multiple aggregation columns in your pivot you have to do the iterations for each aggregation value. I am using Sales as the table name. Please change the table name if it is different for you. Below query should give you the result you want.
DECLARE #cols AS NVARCHAR(MAX),
#Convertcols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#SQL AS NVARCHAR(MAX),
#PivotColumn Varchar(100),
#i Int = 1,
#AggColumn Varchar(100)
DECLARE #Columns TABLE (ID int IDENTITY(1,1), AccountInfo Varchar(max))
SELECT #cols = STUFF((SELECT ', '+ QUOTENAME(SalesDate)
from Sales
group by SalesDate
order by SalesDate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #Convertcols = STUFF((SELECT ', CAST( '+ QUOTENAME(SalesDate) + ' AS VARCHAR(max)) AS ' + QUOTENAME(SalesDate)
from Sales
group by SalesDate
order by SalesDate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
INSERT INTO #Columns
SELECT C.name
FROM SYS.COLUMNS C
INNER JOIN SYS.TABLES T ON C.OBJECT_ID = T.OBJECT_ID
WHERE T.NAME = 'Sales'
AND C.name <> 'SalesDate'
While #i <= (SELECT MAX(ID) FROM #Columns)
BEGIN
SELECT #AggColumn = AccountInfo FROM #Columns Where ID = #i
set #query = 'SELECT AccountInfo,' + #Convertcols +
' FROM
(
SELECT ''' + #AggColumn + ''' AS AccountInfo, SalesDate, '+ #AggColumn +'
FROM Sales
) X
PIVOT
(
Max('+ #AggColumn +')
FOR SalesDate in (' + #cols + ')
) P '
SET #SQL = CONCAT(#SQL, CHAR(10), CASE WHEN #i = 1 THEN '' ELSE 'UNION ' END, CHAR(10) , #query)
SET #i = #i+1
END
EXECUTE (#SQL)
Result:
In order for you to be able to PIVOT, you'll need to have at least one non-pivoted column - something which your model is lacking.
Documentation: https://technet.microsoft.com/en-us/library/ms177410%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396

SQL Server Transpose Rows into Columns using IDs as Column Names

I have a large file that has the following fields:
Table 1:
+---------+--------+-----------+
| User_Id | Key_Id | Value |
+---------+--------+-----------+
| 100 | 74 | 37 |
| 100 | 65 | Male |
| 100 | 279 | G235467 |
+---------+--------+-----------+
and I have another file that tells what each 'Key_Id' is called (they are column names) e.g.
Table 2:
+--------+------------------+
| Key_Id | Key |
+--------+------------------+
| 65 | Gender |
| 66 | Height |
| 74 | Age |
| 279 | ReferenceNo |
I want to create a table using the Key_Id names found in the Key column of table 2, transpose all of the values from table 1 into table 2, but also include the User_Id from table 1 as this relates to an individual.
PS. Table 2 has nearly 300 keys that would need turning into individual fields
So ultimately I would like a table that looks like this:
+---------+---------+--------+-------+--------------+--------+
| User_Id | Gender | Height | Age | ReferenceNo | etc |
+---------+---------+--------+-------+--------------+--------+
| 100 | Male | | 37 | G235467 | |
So that each User_Id is a row and that all the Keys are columns with their respective values
You can use a dynamic sql query as below.
Query
declare #sql as varchar(max);
select #sql = 'select t1.[User_Id], ' + stuff((select +
', max(case t2.[Key_Id] when ' + cast([Key_Id] as varchar(100)) +
' then t1.[Value] end) as [' + [Key] + '] '
from Table2
for xml path('')
), 1, 2, '') +
'from Table1 t1 left join Table2 t2 on t1.[Key_Id] = t2.[Key_Id] group by t1.[User_Id];'
exec(#sql);
Find a demo here
You need to get a coma-separated list of those 300 key names to be used in PIVOT/UNPIVOT operators in T-SQL like described here
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot
you can use pivot as below:
Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in ([Gender], [Height], [Age], [ReferenceNo]) ) p
For dynamic list of keys you can use dynamic sql as below:
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select ','+QuoteName([Key]) from table2 group by [Key] for xml path('')),1,1,'')
Set #Query = 'Select * from (
Select u.UserId, k.[key], u.[Value] from table1 u
join table2 k on u.keyid = k.keyid ) a
pivot ( max([Value]) for [key] in (' + #cols1 + ') ) p '
Select #Query --Check the generated query and execute by uncommenting below query
--exec sp_executesql #Query

SQL Server 2008 Vertical data to Horizontal

I apologize for submitting another question on this topic, but I've read through many of the answers on this and I can't seem to get it to work for me.
I have three tables I need to join and pull info on. One of the tables is only 3 columns and stores the data vertically. I would like to transpose that data to a horizontal format.
The data will look like this if I just join and pull:
SELECT
a.app_id,
b.field_id,
c.field_name,
b.field_value
FROM table1 a
JOIN table2 b ON a.app_id = b.app_id
JOIN table3 c ON b.field_id = c.field_id --(table3 is a lookup table for field names)
Result:
app_id | field_id | field_name | field_value
-----------------------------------------------------
1234 | 101 | First Name | Joe
1234 | 102 | Last Name | Smith
1234 | 105 | DOB | 10/15/72
1234 | 107 | Mailing Addr | PO BOX 1234
1234 | 110 | Zip | 12345
1239 | 101 | First Name | Bob
1239 | 102 | Last Name | Johnson
1239 | 105 | DOB | 12/01/78
1239 | 107 | Mailing Addr | 1234 N Star Ave
1239 | 110 | Zip | 12456
Instead, I would like it to look like this:
app_id | First Name | Last Name | DOB | Mailing Addr | Zip
--------------------------------------------------------------------------
1234 | Joe | Smith | 10/15/72 | PO BOX 1234 | 12345
1239 | Bob | Johnson | 12/01/78 | 1234 N Star Ave | 12456
In the past, I just resorted to looking up all the field_id's I needed in my data and created CASE statements for each one. The app the users are using contains data for multiple products, and each product contains different fields. Considering the number of products supported and the number of fields for each product (many, many more than the basic example I showed, above) it takes a long time to look them up and write out huge chunks of CASE statements.
I was wondering if there's some cheat-code out there to achieve what I need without having to look up the field_ids and writing things out. I know the PIVOT function is likely what I'm looking for, however, I can't seem to get it to work correctly.
Think you guys could help out?
You can use the PIVOT function to convert your rows of data into columns.
Your original query can be used to retrieve all the data, the only change I would make to it would be to exclude the column b.field_id because this will alter the final display of the result.
If you have a known list of field_name values that you want to turn into columns, then you can hard-code your query:
select app_id,
[First Name], [Last Name], [DOB],
[Mailing Addr], [Zip]
from
(
SELECT
a.app_id,
c.field_name,
b.field_value
FROM table1 a
INNER JOIN table2 b
ON a.app_id = b.app_id
INNER JOIN table3 c
ON b.field_id = c.field_id
) d
pivot
(
max(field_value)
for field_name in ([First Name], [Last Name], [DOB],
[Mailing Addr], [Zip])
) piv;
See SQL Fiddle with Demo.
But if you are going to have an unknown number of values for field_name, then you will need to implement dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Field_name)
from Table3
group by field_name, Field_id
order by Field_id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT app_id,' + #cols + '
from
(
SELECT
a.app_id,
c.field_name,
b.field_value
FROM table1 a
INNER JOIN table2 b
ON a.app_id = b.app_id
INNER JOIN table3 c
ON b.field_id = c.field_id
) x
pivot
(
max(field_value)
for field_name in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. Both of these this will give a result:
| APP_ID | FIRST NAME | LAST NAME | DOB | MAILING ADDR | ZIP |
------------------------------------------------------------------------
| 1234 | Joe | Smith | 10/15/72 | PO Box 1234 | 12345 |
| 1239 | Bob | Johnson | 12/01/78 | 1234 N Star Ave | 12456 |
Try this
SELECT
[app_id]
,MAX([First Name]) AS [First Name]
,MAX([Last Name]) AS [Last Name]
,MAX([DOB]) AS [DOB]
,MAX([Mailing Addr]) AS [Mailing Addr]
,MAX([Zip]) AS [Zip]
FROM Table1
PIVOT
(
MAX([field_value]) FOR [field_name] IN ([First Name],[Last Name],[DOB],[Mailing Addr],[Zip])
) T
GROUP BY [app_id]
SQL FIDDLE DEMO
bluefeet's answer was the right one for me, but I needed distinct on the column list:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT Distinct ',' + QUOTENAME(Field_name)
from Table3
group by field_name, Field_id
order by ',' + QUOTENAME(Field_name)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT app_id,' + #cols + '
from
(
SELECT
a.app_id,
c.field_name,
b.field_value
FROM table1 a
INNER JOIN table2 b
ON a.app_id = b.app_id
INNER JOIN table3 c
ON b.field_id = c.field_id
) x
pivot
(
max(field_value)
for field_name in (' + #cols + ')
) p '
execute sp_executesql #query;
This would solve using group by and MAX function, instead of pivot:
SELECT PK_ID, MAX(PHONE) AS PHONE, MAX(MAIL) AS MAIL
FROM (
SELECT
PK_ID,
CASE
WHEN CONTACT_ALIAS.CONTACT_TYPE = 'COMPANY' THEN CONTACT_ALIAS.CONTACT_VALUE
END AS PHONE ,
CASE
WHEN CONTACT_ALIAS.CONTACT_TYPE = 'BUSINESS' THEN CONTACT_ALIAS.CONTACT_VALUE
END AS MAIL
FROM T_CONTACT_EMPLOYERS CONTACT_ALIAS
WHERE CONTACT_ALIAS.CONTACT_TYPE IN ('COMPANY' , 'BUSINESS')
) TEMP
GROUP BY PK_ID
USe of SQL Pivot
SELECT [Id], [FirstName], [LastName], [Email]
FROM
(
SELECT Id, Att_Id, Att_Value FROM VerticalTable
) as source
PIVOT
(
MAX(Att_Value) FOR Att_Id IN ([FirstName], [LastName], [Email])
) as target

Resources