Pivot SQL query output - sql

I have a requirement where my query will return output something like this:
PermissionType IsAllowed
-------------------------
IsEdit | 1
IsDelete | 0
isRemove | 1
isPrint | 1
isReport | 0
-- | -
-- | -
-- | -
--------------------------
These rows can be dynamic depending upon the filter criteria I will pass.
So now I want to convert the above resultset to the following:
IsEdit | IsDelete | IsRemove | IsPrint | IsReport | -- | -- | --
--------------------------------------------------------------------
1 | 0 | 1 | 1 | 0 | - | - | -
I tried to use the pivot here but it asks the Column Names to be pivoted into the output and this is dynamic in my case, also it needed an aggregate function for FOR but I don't have any calculation in my case.
Anyone please help me on this.

Then try this Dynamic sql
IF OBJECT_ID('dbo.temp')IS NOT NULL
DROP TABLE temp
;WITH Cte(PermissionType, IsAllowed)
AS
(
SELECT 'IsEdit' , 1 UNION ALL
SELECT 'IsDelete' , 0 UNION ALL
SELECT 'isRemove' , 1 UNION ALL
SELECT 'isPrint' , 1 UNION ALL
SELECT 'isReport' , 0
)
SELECT *,ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS Seq INTO
temp FROM Cte
DECLARE #Sql nvarchar(max),
#Sqlcol nvarchar(max),
#ISNULLSqlcol nvarchar(max)
SELECT #Sqlcol=STUFF((SELECT ', '+QUOTENAME(PermissionType)
FROM temp ORDER BY Seq FOR XML PATH ('')),1,1,'')
SELECT #ISNULLSqlcol=STUFF((SELECT ', '+'MAX('+QUOTENAME(PermissionType) +') AS '+QUOTENAME(PermissionType)
FROM temp ORDER BY Seq FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT '+#ISNULLSqlcol+'
FROM(
SELECT * FROM temp
)AS SRC
PIVOT
(
MAX(IsAllowed) FOR PermissionType IN ('+#Sqlcol+')
) AS PVT '
PRINT #Sql
EXEC (#Sql)
IsEdit IsDelete isRemove isPrint isReport
--------------------------------------------------
1 0 1 1 0

With pivot and dynamic sql you can create a query that will include a different number of columns:
if OBJECT_ID('Test') is not null
drop table [dbo].[Test]
CREATE TABLE [dbo].[Test](PermissionType varchar(20), IsAllowed int)
insert into [dbo].[Test] values
('IsEdit' , 1)
,('IsDelete', 0)
,('isRemove', 1)
,('isPrint' , 1)
,('isReport', 0)
--this variable holds all the dates that will become column names
declare #permissionTypes nvarchar(max) = ''
--this variable contains the TSQL dinamically generated
declare #sql nvarchar(max) = ''
select #permissionTypes = #permissionTypes + ', ' + quotename(PermissionType)
from [dbo].[Test]
set #permissionTypes = RIGHT(#permissionTypes, len(#permissionTypes)-2)
set #sql = concat(
'select *
from [dbo].[Test]
pivot
(
max(isallowed)
for PermissionType in (', #permissionTypes, ')
) piv '
)
exec(#sql)
Result:
Adding a new row:
insert into [dbo].[Test] values
('IsNew' , 1)
Causes a new column to be created:

Related

Creating a TABLE to pivot and concat columns from exiting table?

I have a Table that stores customer Dynamic values from the Forms that they create.
The Table is called Dynamic_Fields_T and is formatted like this:
ID | FIELD_NAME
1 | TANK
2 | PRODUCT TYPE
3 | ODOMETER
4 | RECEIPT #
This is not joinable to our ticket Tables.
I need this to appear like this:
ID_1 | ID_2 .......ID_N
TANK | PRODUCT_TYPE ......N
Any Help is appreciated.
Assuming that your [ID] column is unique you can use a dynamic select statement with an aggregation function (min or max) to return all your rows in a single row:
--create test table
create table #Dynamic_Fields_T (
[ID] INT
,[FIELD_NAME] varchar(max)
)
--populate test table
insert into #Dynamic_Fields_T
values
(1 ,'TANK')
,(2 ,'PRODUCT TYPE')
,(3 ,'ODOMETER')
,(4 ,'RECEIPT #')
declare #sql nvarchar(max) =''
--build dynamic columns
select
#sql = #sql + ',max(case when [ID] = ''' + cast([ID] as varchar) + ''' then [FIELD_NAME] end) as [ID_' + cast([ID] as varchar) + ' ] '
from
#Dynamic_Fields_T
--build dynamic query, remove unnecessary comma
select #sql = 'select ' + stuff(#sql, 1 , 1, '') + ' from #Dynamic_Fields_T'
--execute dynamic query
execute (#sql)
result:

Dynamically create table columns with values from Pivot Table

I have a dynamic query that utilizes a pivot function and the following is an example of data in my table.
Status 1 | Week 1 |25
Status 1 | Week 1 |25
Status 1 | Week 2 |25
Status 2 | Week 1 | 2
Status 2 | Week 1 | 8
Status 2 | Week 1 | 10
Status 2 | Week 1 | 10
and this is an example of how the data is returned.
Week 1 Week 2
Status 1 | 50 25
Status 2 10 20
For my query I am passing in a week and I want to pivot on the following 5 weeks, so example, if I pass in 1, I expect to have columns from week 1 to week 6.
To help facilitate that I have written the following query.
--EXEC usp_weekReport #weeks=1, #year='2019'
ALTER PROC usp_weekReport
(
#weeks INT,
#year NVARCHAR(4)
)
AS
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX), #csql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [Housing_support_DB].[dbo].[Invoices] P
WHERE DATEPART(YEAR,P.date)='2019'--#year
AND
([week] IN (1)
OR
[week] IN (1+1)
OR
[week] IN (1+2)
OR
[week] IN (1+3)
OR
[week] IN (1+4)
OR
[week] IN (1+5)
)
GROUP BY P.[week]
) AS x;
SET #sql = N'
SELECT p.[statusName],' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
-- C.programme,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
-- INNER JOIN CapitalAccountBalances C
-- ON C.accountBalanceID=A.accountBalanceID_FK
-- WHERE A.accountBalanceID_FK=5
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
--PRINT #sql;
EXEC sp_executesql #sql;
--SET #csql = N'
--CREATE TABLE ##reportResult
--(
--statusName nvarchar(50),'+
CREATE TABLE ##reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0--,
--weekC int DEFAULT 0,
--weekD int DEFAULT 0,
--weekE int DEFAULT 0,
--weekF int DEFAULT 0
)
INSERT into ##reportResult Exec(#sql)
--INSERT ##reportResult Exec(#sql)
--SELECT statusName, weekA,weekB,weekC,weekD,weekE,weekF -- here you have "static SELECT with field names"
--FROM ##reportResult
--DROP TABLE ##reportResult
Problem
The huge problem that I have here is that, I need to send the result of this query to a tempTable...#reportResult. As a result, I need to create the table. However, if I attempt to create the table with the max amount of columns anticipated (6) I will get an invalid number of columns error. For example, in my database I only have two weeks, that's why I can only create the table with columns weekA and weekB. I also cannot do a select into.
Presently, I am trying to find a way to either create the table dynamically depending on the amount of weeks from the first part of the pivot table. Or, to manipulate the first part of the pivot to select week,week+1 etc as columns when run so that way , I can create the column with all fields.
Appreciate any help that could be provided.
You required dynamic SQL in your case as the column name is need to generate based on th Input week number. Below I have give you the script I created with your sample data using CTE. You just need to updated the script based on your table and requirement.
You can test the code changing the value of Week_No
For your final query, just use the SELECT part after removing the CTE code
DECLARE #Week_No INT = 2
DECLARE #Loop_Count INT = 1
DECLARE #Column_List VARCHAR(MAX) = '[Week '+CAST(#Week_No AS VARCHAR) +']'
WHILE #Loop_Count < 5
BEGIN
SET #Column_List = #Column_List +',[Week '+CAST(#Week_No+#Loop_Count AS VARCHAR) +']'
SET #Loop_Count = #Loop_Count + 1
END
--SELECT #Column_List
EXEC
('
WITH your_table(Status,Week_No,Val)
AS
(
SELECT ''Status 1'',''Week 1'',25 UNION ALL
SELECT ''Status 1'',''Week 1'',25 UNION ALL
SELECT ''Status 1'',''Week 2'',25 UNION ALL
SELECT ''Status 2'',''Week 1'',2 UNION ALL
SELECT ''Status 2'',''Week 1'',8 UNION ALL
SELECT ''Status 2'',''Week 1'',10 UNION ALL
SELECT ''Status 2'',''Week 1'',10
)
SELECT * FROM
(
SELECT * FROM your_table
) AS P
PIVOT
(
SUM(val)
FOR Week_No IN ('+#Column_List+')
)PVT
')

Identify distinct codes present in one column but not present in another column

I have data like the following table. First two columns are list of country codes with pipe separator. There are two group of rows with RANK as 1 and 2.
I am trying to identify the country codes which are present in CountryList1 but not present in the column CountryList1 over a give RANK. For Rank 1 rows, HN JP SK and KY is present in CountryList1 but not present in CountryList2. Likewise, for Rank 2 rows. HN is present in CountryList1 but not present in CountryList2.
I am expecting Output like second table. I do not want to use a function or a procedure but trying to accomplish it using select statement.
Input
CountryList1 || CountryList2 || RANK
================||==============||=======
HN|IN|US || GB|CA|CH|CA || 1
JP|CH || IN|US|LU || 1
HN|SK|KY || GB|CA || 1
FI || IN|MO || 1
HN|IN|US || HN || 2
JP|CH || CH|IN|US || 2
HN || NO || 2
Output
DistinctCountry1 || RAN
====================||========
HN || 1
JP || 1
SK || 1
KY || 1
JP || 2
You have an abominable data structure. You should be storing elements of a list as separate values on rows. But you can do something by splitting the values. SQL Server 2016 has string_split(). For earlier versions you can find one on the web.
with tc as (
select t.*, s.country1
from t cross apply
(string_split(t.countrylist1, '|') s(country1)
)
select distinct t.country1, t.rnk
from tc
where not exists (select 1
from t t2
where tc.rnk = t2.rnk and
tc.country in (select value from string_split(t2.country_list))
);
This will not be efficient. And with the data structure you have, there is little scope for improving performance.
Here's a nice loop you can use for this:
declare #holding table (country1 varchar(max), country2 varchar(max), rank int)
declare #iterator int=1
declare #countrylistoriginal1 varchar(max)
declare #countrylistoriginal2 varchar(max)
declare #countrylist1 varchar(max)
declare #countrylist2 varchar(max)
declare #rank int
while #iterator<=(select max(rowid) from #temp2)
begin
select #countrylistoriginal1=countrylist1+'|', #rank=[rank]
from yourtable where rowid=#iterator
while #countrylistoriginal1<>''
begin
set #countrylist1=left(#countrylistoriginal1,(charindex('|',#countrylistoriginal1)))
set #countrylistoriginal1=replace(#countrylistoriginal1, #countrylist1,'')
select #countrylistoriginal2=countrylist2+'|'
from yourtable where rowid=#iterator
while #countrylistoriginal2<>''
begin
set #countrylist2=left(#countrylistoriginal2,(charindex('|',#countrylistoriginal2)))
set #countrylistoriginal2=replace(#countrylistoriginal2, #countrylist2,'')
insert #holding
select replace(#countrylist1,'|',''), replace(#countrylist2,'|',''), #rank
end
end
set #iterator=#iterator+1
end
select distinct a.country1, a.rank from #holding a
left join #holding b on a.country1=b.country2 and a.rank=b.rank where b.country2 is null
Try this...
Table Schema and data
CREATE TABLE [tableName](
[CountryList1] [nvarchar](50) NULL,
[CountryList2] [nvarchar](50) NULL,
[RANK] [int] NULL
)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'HN|IN|US', N'GB|CA|CH|CA', 1)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'JP|CH ', N'IN|US|LU', 1)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'HN|SK|KY', N'GB|CA', 1)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'FI', N'IN|MO', 1)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'HN|IN|US', N'HN ', 2)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'JP|CH', N'CH|IN|US', 2)
INSERT [tableName] ([CountryList1], [CountryList2], [RANK]) VALUES (N'HN', N'NO', 2)
SQL Query
;WITH cte AS
( SELECT DISTINCT *
FROM (SELECT [value] AS DistinctCountry1,
[rank],
Rtrim(Ltrim([value])) + Cast([rank] AS NVARCHAR(max)) AS colX
FROM tablename
CROSS apply String_split([countrylist1], '|')) tmp
WHERE colx NOT IN (SELECT Rtrim(Ltrim([value])) + Cast([rank] AS NVARCHAR(max)) AS colX
FROM tablename
CROSS apply String_split([countrylist2], '|'))
)
SELECT [distinctcountry1], [rank]
FROM cte
ORDER BY [rank]
Output
+------------------+------+
| distinctcountry1 | rank |
+------------------+------+
| FI | 1 |
| HN | 1 |
| JP | 1 |
| KY | 1 |
| SK | 1 |
| JP | 2 |
+------------------+------+
Demo: http://www.sqlfiddle.com/#!18/19acb/2/0
Note: As others already suggested, you should really consider fixing your table or you'll have to put extra hours when manipulating data.

sql server sort dynamic pivot on large set of data

I am having trouble sorting a pivot based on a quite large set of data. I have looked at many examples, but none of them seems to address the issue of volume - or perhaps I am just missing something. I have had a very good look here: Sort Columns For Dynamic Pivot and PIVOT in sql 2005 and found much good advise, but I still cannot find the correct way to sort my pivot.
I am using the following sql. It pivots the columns, but the result needs to be sorted for readability:
SELECT a.* INTO #tempA
FROM (SELECT top (5000) id, email, CONVERT(varchar,ROW_NUMBER() OVER
(PARTITION BY email ORDER BY id)) AS PIVOT_CODE FROM Email) a
order by PIVOT_CODE
DECLARE #cols AS NVARCHAR(MAX),
#sql AS NVARCHAR(MAX)
SELECT #cols =STUFF((SELECT DISTINCT ', ' + QUOTENAME(col)
FROM #tempA WITH (NOLOCK)
cross apply
(
SELECT 'id_' + PIVOT_CODE, id
) c (col, so)
group by col, so
--order by col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #sql = 'SELECT email, '
+#cols+
'INTO ##AnotherPivotTest FROM
(
SELECT email,
col,
value
FROM #tempA WITH (NOLOCK)
cross apply
(
values
(''id_'' + PIVOT_CODE, id)
) c (col, value)
) d
pivot
(
max(value)
for col in ('
+ #cols+
')
) piv'
EXEC (#sql)
SELECT * FROM ##AnotherPivotTest
The result is a chaos to look at:
==============================================================================================
| email | id_19 | id_24 | id_2 | id_16 | id_5 | id_9 | id_23 | .... | id_1 | .... | id_10 |
==============================================================================================
| xx#yy.dk | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 1234 | NULL | NULL |
==============================================================================================
I would very much like the Ids to be sorted - beginning with id_1.
As you can see, I have attempted to place an 'order by' in the selection for 'cols', but that gives me the error: "ORDER BY items must appear in the select list if SELECT DISTINCT is specified." And without DISTINCT, I get another error: "The number of elements in the select list exceeds the maximum allowed number of 4096 elements."
I'm stuck, so any help will be greatly appreciated!
Not sure what causes the problem but I've solved my order problem in my pivot table by inserting the data coming from tempA into another temp table and ordering them there
INSERT INTO #tempB
SELECT * FROM #tempA
ORDER BY PIVOT_CODE
Then selecting distinct ones like so:
SELECT #cols = #cols + QUOTENAME(PIVOT_CODE) + ',' FROM (SELECT DISTINCT PIVOT_CODE FROM #tempB ORDER BY PIVOT_CODE)
SELECT #cols = SUBSTRING(#cols, 0, LEN(#cols)) --trims "," at end
You can also just use a cursor to determine your cols and the order them
Cursor with cols ordered
declare #gruppe nvarchar(max)
declare #gruppeSql nvarchar(max)
declare #SQL nvarchar(max)
DECLARE myCustomers CURSOR FOR
select top 10 FirstName from [dbo].[DimCustomer] Order by FirstName
set #gruppeSql = ''
OPEN myCustomers
FETCH NEXT FROM myCustomers INTO #gruppe
IF (##FETCH_STATUS>=0)
BEGIN
SET #gruppeSql = #gruppeSql +'[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
WHILE (##FETCH_STATUS<>-1)
BEGIN
IF (##FETCH_STATUS<>-2)
SET #gruppeSql = #gruppeSql + ',[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
CLOSE myCustomers
DEALLOCATE myCustomers
SET #gruppeSql = replace(#gruppesql,'''','')
/*Select to preview your cols*/
select #gruppeSql
Dynamic pivot
SET #SQL = '
 Select *
from
(
SELECT SalesAmount, FirstName
FROM [AdventureWorksDW2014].[dbo].[FactInternetSales] a inner join dbo.DimCustomer b on a.CustomerKey = b.CustomerKey
) x
pivot
(
sum(SalesAmount)
for FirstName in ('+#gruppesql+')
) p'
print #sql
exec(#sql)

Pivot Header Data in row using sql server

Is there any way to make table pivot in sql server like such a way.
I have data like
| OldItem | NewItem |
---------------------
| HD1 | 365 |
I need output like below.
| Name | Value1 |
---------------------
| OldItem | HD1 |
| NewItem | 365 |
Thanks in advance.
Please try using UNPIVOT. Sample given is for static two rows.
SELECT Name, Value1
FROM
(SELECT *
FROM tbl) p
UNPIVOT
(Value1 FOR Name IN
(OldItem, NewItem)
)AS unpvt;
The following works for me:
Create Table #Values (OldItem char(3),NewItem int);
INSERT INTO #Values (OldItem, NewItem)
VALUES ('HD1',365)
,('HD2',300)
,('HD3',200);
With Values_Ordered AS
(
SELECT OldItem, NewItem, row_number() OVER (ORDER BY OldItem) AS Sequence
FROM #Values
)
SELECT 'OldItem' AS Name,
min(CASE WHEN Sequence = 1 THEN OldItem ELSE NULL END) AS Value1,
min(CASE WHEN Sequence = 2 THEN OldItem ELSE NULL END) AS Value2,
min(CASE WHEN Sequence = 3 THEN OldItem ELSE NULL END) AS Value3
FROM Values_Ordered
UNION ALL
SELECT 'NewItem' AS Name,
min(CASE WHEN Sequence = 1 THEN CAST(NewItem AS CHAR(3)) ELSE NULL END) AS Value1,
min(CASE WHEN Sequence = 2 THEN CAST(NewItem AS CHAR(3)) ELSE NULL END) AS Value2,
min(CASE WHEN Sequence = 3 THEN CAST(NewItem AS CHAR(3)) ELSE NULL END) AS Value3
FROM Values_Ordered
And here is my little code :D
DECLARE #dataTable TABLE (OldItem VARCHAR(10), NewItem INT)
INSERT INTO #dataTable SELECT 'HD1', 365
INSERT INTO #dataTable SELECT 'HD2', 300
INSERT INTO #dataTable SELECT 'HD3', 200
INSERT INTO #dataTable SELECT 'HD4', 200
--first select data what you need and add upcoming new column name
SELECT 'Value' + CAST(ROW_NUMBER() OVER (ORDER BY OldITem) AS VARCHAR) AS NewColumn, 'OldItem' as RowName, OldItem AS Item
INTO #SelectedData
FROM #dataTable
WHERE OldItem IN ('HD1', 'HD2', 'HD3')
UNION ALL
SELECT 'Value' + CAST(ROW_NUMBER() OVER (ORDER BY OldITem) AS VARCHAR) AS NewColumn, 'NewItem' as RowName, CAST(NewItem AS VARCHAR) AS Item
FROM #dataTable
WHERE OldItem IN ('HD1', 'HD2', 'HD3')
--Collect what column names will be
DECLARE #columns NVARCHAR(MAX) = (
SELECT STUFF(
(SELECT DISTINCT ', [' + NewColumn + ']'
FROM #SelectedData
FOR XML PATH ('')),
1, 2, '' )
)
-- create dynamic code for pivot
DECLARE #dynamicSQL AS NVARCHAR(MAX);
SET #dynamicSQL = N'
SELECT RowName, ' + #columns + '
FROM #SelectedData
PIVOT (MIN(Item) FOR NewColumn IN (' + #columns + ')) AS T
';
EXEC sp_executesql #dynamicSQL