Count non-empty rows with unknown column names - sql-server

Given a table that has unknown column names, how can I list all column names and the amount of rows that have a non-empty (NULL or empty string) value in that column?
Small example:
Col1 | Col2 | Col3 | dkasldk | dD? d3# !(
1 | | 2 | |
| 2 | d | ddd939 |
f | f | 84 | |
Should yield:
Column Name | Values
Col1 | 2
Col2 | 2
Col3 | 3
dkasldk | 1
dD? d3# !( | 0
I already tried around with using INFORMATION_SCHEMA.COLUMNS and creating dynamic SQL queries, but to no avail:
select N'select c.COLUMN_NAME,
(select sum(case when p.' + QUOTENAME(c.COLUMN_NAME) + ' != '''' then 1 else 0 end) from [Produkte] p) [Produkte]
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = ''Produkte'' and c.COLUMN_NAME = ''' + c.COLUMN_NAME + '''' select_statement
from INFORMATION_SCHEMA.COLUMNS c
where c.TABLE_NAME = 'Produkte'
I cannot quite wrap my head around how to combine the two problems (for which I did find solution in their own, but not the combination) of Dynamic Column Names and Count empty values...

You can do something like this using UNPIVOT..
DECLARE #TableName NVARCHAR(MAX) = N'Produkte',
#CountColumns NVARCHAR(MAX),
#Columns NVARCHAR(MAX),
#Sql NVARCHAR(MAX)
SELECT #CountColumns = COALESCE(#CountColumns + ',', '') + 'COUNT(NULLIF(' + QUOTENAME(c.COLUMN_NAME) + ','''')) AS ' + QUOTENAME(c.COLUMN_NAME),
#Columns = COALESCE(#Columns + ',', '') + QUOTENAME(c.COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = #TableName
SET #SQL = 'SELECT [Column Name], [Values]
FROM (
SELECT '
+ #CountColumns + '
FROM ' + #TableName + '
) t
UNPIVOT (
[Values]
FOR [Column Name] IN (' + #Columns + ')
) up
'
EXEC(#SQL)
SQL Fiddle Demo

You can use a dynamic UNPIVOT.
Using these variables:
DECLARE #qry NVARCHAR(MAX)
DECLARE #cols NVARCHAR(MAX) = ''
you can select columns names in a format suitable for the UNPIVOT operation with the following dynamic sql:
SELECT #cols = STUFF((SELECT ',[' + c.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = 'Produkte'
FOR XML PATH('')), 1, 1, '')
Finally use #cols to build the query:
SET #qry = 'SELECT t.Col, COUNT(*)
FROM (SELECT Val, Col
FROM Produkte
UNPIVOT (
Val FOR Col IN (' + #cols + ')) unpvt
) AS t
WHERE t.Val <> '''' OR t.Val IS NOT NULL
GROUP BY t.Col'
... and execute it to get desired result:
EXEC(#qry)

Ultimately the SQL you are after is something like this:
SELECT ColumnName, NonEmpty
FROM ( SELECT A = 1,
[Col1] = COUNT(CASE WHEN [Col1] <> '' THEN 1 END),
[Col2] = COUNT(CASE WHEN [Col2] <> '' THEN 1 END),
[Col3] = COUNT(CASE WHEN [Col3] <> '' THEN 1 END),
[dkasldk] = COUNT(CASE WHEN [dkasldk] <> '' THEN 1 END),
[dD? d3# !(] = COUNT(CASE WHEN [dD? d3# !(] <> '' THEN 1 END)
FROM #T
) AS t
UNPIVOT
( NonEmpty
FOR ColumnName IN ([Col1],[Col2],[Col3],[dkasldk],[dD? d3# !(])
) AS upvt;
UNPIVOT Will convert your data from
Col1 Col2 Col3
-------------------------
1 3 2
To the format you require:
ColumnName NonEmpty
-------------------------
Col1 1
Col2 3
Col3 2
You can build up dynamically using something like this:
-- SAMPLE DATA
USE TempDB;
CREATE TABLE #T
(
[Col1] VARCHAR(1),
[Col2] VARCHAR(1),
[Col3] VARCHAR(2),
[dkasldk] VARCHAR(6),
[dD? d3# !(] VARCHAR(1)
);
INSERT #T ([Col1], [Col2], [Col3], [dkasldk], [dD? d3# !(])
VALUES
('1', NULL, '2', NULL, ''),
('', '2', 'd', 'ddd939', NULL),
('f', 'f', '84', NULL, '');
DECLARE #TableName SYSNAME = '#T';
-- VALID INPUT TABLE
IF OBJECT_ID(#TableName, 'U') IS NULL
AND OBJECT_ID(#TableName, 'V') IS NULL
BEGIN
PRINT 'Invalid table or View';
RETURN
END
-- BUILD DYNAMIC SQL
DECLARE #SQL NVARCHAR(MAX) =
CONCAT('SELECT ColumnName, NonEmpty FROM (SELECT A = 1, ' ,
STUFF(( SELECT CONCAT(',',
QUOTENAME(name),
' = COUNT(CASE WHEN ',
QUOTENAME(Name),
' <> '''' THEN 1 END)')
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
' FROM ',
#TableName,
') AS t UNPIVOT (NonEmpty FOR ColumnName IN (',
STUFF(( SELECT CONCAT(',', QUOTENAME(name))
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
')) AS upvt');
-- EXECUTE DYNAMIC SQL
EXECUTE sp_executesql #SQL;

Related

Validate decimal and integer values by looping through list of dynamic columns

I was tasked to validate the decimal and integer values of the columns from a list of tables. I have around 10-12 tables having different column names.
I created a lookup table which has the table name and the column names of decimal and integer as shown below. for example 'Pricedetails' and 'Itemdetails' tables have many columns of which only the ones mentioned in the Lookup table are required.
lkpTable
TableName
requiredcolumns
Pricedetails
sellingPrice,RetailPrice,Wholesaleprice
Itemdetails
ItemID,Itemprice
Pricedetails
Priceid
Mafdate
MafName
sellingPrice
RetailPrice
Wholesaleprice
01
2020-01-01
Americas
25.00
43.33
33.66
02
2020-01-01
Americas
43.45
22.55
11.11
03
2021-01-01
Asia
-23.00
-34.00
23.00
Itemdetails
ItemID
ItemPrice
Itemlocation
ItemManuf
01
45.11
Americas
SA
02
25.00
Americas
SA
03
35.67
Americas
SA
I have created a stored procedure with table name as input parameter, and able to pull the required column names of the tables (input parameter) from the lookup table and store that resultset into a table variable, below is the code.
declare #resultset Table
(
id INT identity(1,1),
tablename varchar(200) ,
ColumnNames varchar(max)
)
declare #tblname varchar(200),#sql varchar(max),#cols varchar(max),
INSERT INTO #resultset
select tablename,ColumnNames
from lkptable where tablename ='itemdetails'
select #cols = ColumnNames from #resultset;
select #tblname = TableName from #resultset;
----- Split the comma separated columnnames
Create table ##splitcols
(
ID int identity(1,1),
Name varchar(50)
)
Insert into ##splitcols
select value from string_split(#cols,',')
set #sql = 'select ' +#cols + ' from ' +#tblname
--print (#cols)
exec (#sql)
select * from ##splitcols
On executing the above code i get the below result sets, similarly what ever table name i provide i can get the required columns and its relevant data, now i am stuck at this point on how to validate whether the columns are decimal or int. I tried using while loop and cursor to pass the Name value from Resultset2, to the new dynamic query, somehow i don't find any way on how to validate.
ItemID
ItemPrice
01
45.11
02
25.00
03
35.67
Resultset2
ID
Name
01
ItemID
02
ItemPrice
you can validate in this way
insert into #splitcols values (1.1),(1.11)
select case when (t * 100)%10 = 0 then 1 else 0 end as valid from #splitcols
Similarly for number/integer
You can generate one giant UNION ALL query using dynamic SQL, then run it.
Each query would be of the form:
SELECT
TableName = 'SomeTable',
ColumnName,
IsInt
FROM (
SELECT
[Column1] = CASE WHEN COUNT(CASE WHEN ROUND([Column1], 0) <> [Column1] THEN 1 END) = 0 THEN 'All ints' ELSE 'Not All ints' END,
[Column2] = CASE WHEN COUNT(CASE WHEN ROUND([Column2], 0) <> [Column2] THEN 1 END) = 0 THEN 'All ints' ELSE 'Not All ints' END
FROM SomeTable
) t
UNPIVOT (
ColumnName FOR IsInt IN (
[Column1], [Column2]
)
) u
The script is as follows
DECLARE #sql nvarchar(max);
SELECT #sql = STRING_AGG('
SELECT
TableName = ' + QUOTENAME(t.name, '''') + ',
ColumnName,
IsInt
FROM (
SELECT ' + c.BeforePivotCoumns + '
FROM ' + QUOTENAME(t.name) + '
) t
UNPIVOT (
ColumnName FOR IsInt IN (
' + c.UnpivotColumns + '
)
) u
', '
UNION ALL '
)
FROM sys.tables t
JOIN lkpTable lkp ON lkp.TableName = t.name
CROSS APPLY (
SELECT
BeforePivotCoumns = STRING_AGG(CAST('
' + QUOTENAME(c.name) + ' = CASE WHEN COUNT(CASE WHEN ROUND(' + QUOTENAME(c.name) + ', 0) <> ' + QUOTENAME(c.name) + ' THEN 1 END) = 0 THEN ''All ints'' ELSE ''Not All ints'' END'
AS nvarchar(max)), ','),
UnpivotColumns = STRING_AGG(QUOTENAME(c.name), ', ')
FROM sys.columns c
JOIN STRING_SPLIT(lkp.requiredcolumns, ',') req ON req.value = c.name
WHERE c.object_id = t.object_id
) c;
PRINT #sql;
EXEC sp_executesql #sql;
db<>fiddle
If you are on an older version of SQL Server then you can't use STRING_AGG and instead you need to hack it with FOR XML and STUFF.
DECLARE #unionall nvarchar(100) = '
UNION ALL ';
DECLARE #sql nvarchar(max);
SET #sql = STUFF(
(SELECT #unionall + '
SELECT
TableName = ' + QUOTENAME(t.name, '''') + ',
ColumnName,
IsInt
FROM (
SELECT ' + STUFF(c1.BeforePivotCoumns.value('text()[1]','nvarchar(max)'), 1, 1, '') + '
FROM ' + QUOTENAME(t.name) + '
) t
UNPIVOT (
ColumnName FOR IsInt IN (
' + STUFF(c2.UnpivotColumns.value('text()[1]','nvarchar(max)'), 1, 1, '') + '
)
) u'
FROM sys.tables t
JOIN (
SELECT DISTINCT
lkp.TableName
FROM lkpTable lkp
) lkp ON lkp.TableName = t.name
CROSS APPLY (
SELECT
',
' + QUOTENAME(c.name) + ' = CASE WHEN COUNT(CASE WHEN ROUND(' + QUOTENAME(c.name) + ', 0) <> ' + QUOTENAME(c.name) + ' THEN 1 END) = 0 THEN ''All ints'' ELSE ''Not All ints'' END'
FROM lkpTable lkp2
CROSS APPLY STRING_SPLIT(lkp2.requiredcolumns, ',') req
JOIN sys.columns c ON req.value = c.name
WHERE c.object_id = t.object_id
AND lkp2.TableName = lkp.TableName
FOR XML PATH(''), TYPE
) c1(BeforePivotCoumns)
CROSS APPLY (
SELECT
', ' + QUOTENAME(c.name)
FROM lkpTable lkp2
CROSS APPLY STRING_SPLIT(lkp2.requiredcolumns, ',') req
JOIN sys.columns c ON req.value = c.name
WHERE c.object_id = t.object_id
AND lkp2.TableName = lkp.TableName
FOR XML PATH(''), TYPE
) c2(UnpivotColumns)
FOR XML PATH(''), TYPE
).value('text()[1]','nvarchar(max)'), 1, LEN(#unionall), '');
PRINT #sql;
EXEC sp_executesql #sql;
db<>fiddle

How to combine rows dynamically

I have a table with many columns (even number of columns). Now I need to combine the second column and third column, the forth and fifth column, sixth and seventh column....etc. How to achieve this?
I tried static one, but what about dynamic one. Assume that there are 100 or more columns.
create table tb11 ( [id] int,[A] varchar(20),[B] varchar(20),
[C] varchar(20),[D] varchar(20))
insert into tb11 values
(1,'a','b','c','d'),
(2,'e','f','g','h'),
(3,'i','j','k','l')
select * from tb11
/*
id A B C D
---- --- ---- --- ----
1 a b c d
2 e f g h
3 i j k l
*/
select id,
[A] + [B] as '1' ,
[C] + [D] as '2'
from tb11
/*output with 3 columns
id 1 2
---- ----- ------
1 ab cd
2 ef gh
3 ij kl
*/
Try this
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = (
SELECT '
' +
STUFF((
SELECT ', ' + c.name + ' + ' + c2.name + ' AS [' + c.name + c2.name +']'
FROM sys.columns c
INNER JOIN sys.columns c2 ON c2.object_id = c.object_id
AND c2.column_id = c.column_id + 1
WHERE c.[object_id] = o.[object_id]
AND c.column_id > 1
AND c.column_id % 2 = 0
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, 'SELECT id, ') + '
FROM [' + SCHEMA_NAME(o.[schema_id]) + '].[' + o.name + ']' -- select *
FROM sys.objects o
WHERE o.[type] = 'U'
AND o.is_ms_shipped = 0
AND [name] = 'tb11'
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
EXEC sys.sp_executesql #SQL
You may use schema of tables to get the list of columns and create dynamic columns name from them.
Similarly by using that schema only create you table script for new structure.
Try this:
GO
;with cte as (
select ORDINAL_POSITION as slno, COLUMN_NAME from information_schema.columns where table_name = 'tb11'
)
select * into #tab_col from cte
declare #max int
set #max = (select Count(*) -1 from #tab_col)
declare #loop int
set #loop = 0
create table newtab (
id int )
declare #columns nvarchar(max) = ''
declare #values nvarchar(max) = ''
while(#Loop <= (#Max/2))
Begin
declare #Col1 varchar(100)
declare #Col2 varchar(100)
set #Col1 = (select Column_name from #tab_col where slno = #Loop+2)
set #Col2 = (select Column_name from #tab_col where slno = #Loop+3)
declare #alter nvarchar(max)
set #alter = ' Alter table newtab add [' + cast(((#Loop/2)+1) as varchar(100)) + '] nvarchar(max) '
set #columns = #columns + ',[' + (select cast(((#Loop/2)+1) as varchar(100))) + ']'
set #values = #values + ',''' + #Col1 + #Col2 + ''''
exec sp_executesql #alter
set #loop = #loop + 2
End
set #values =( select substring( #values, 2, len(#values)))
select #values
set #columns =( select substring( #columns, 2, len(#columns)))
select #columns
declare #altertab nvarchar(max)
set #altertab = ' insert into newtab ( id, ' + #columns + ' ) values ( 1, ' + #values + ' )'
exec sp_executesql #altertab
drop table newtab
Drop table #tab_col
GO
This will not exactly give you your answer but you'll get some idea.

How to get column names from a table and count not null values of them in SQL Server?

I tried something like this, which did not work:
SELECT COLUMN_NAME, COUNT(COLUMN_NAME) AS COUNT_NOT_NULL FROM myDB.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable';
Obviously, the "information-meta-table" doesn't have the data from myTable to count. So do I need some sort of JOIN? Do I need to declare some variable and use some iteration?
The weird part is that this solution works for some tables, but not for others (it counts NULLs): https://stackoverflow.com/a/24411738/8055476
First I created a table with some sample data
IF OBJECT_ID('COuntNotNull') IS NOT NULL
Drop Table COuntNotNull
;With cte(Column1 , Column2 , Column3 , Column4 )
AS
(
SELECT 'X' , ' X' , NULL , 'X' Union all
SELECT NULL , NULL , 'X' , 'X' Union all
SELECT NULL , NULL , NULL , NULL
)
SELECT * INTO COUNTNOTNULL FROM cte
The below code get the column names dynamically For given table and get count of non null values
DECLARE #DynamicColms NVARCHAR(max)
,#CaseDynamicColms NVARCHAR(max)
,#Sql NVARCHAR(max)
,#TableName VARCHAR(100) = 'COuntNotNull'--Here give Your TableName
SELECT #DynamicColms = STUFF((
SELECT ', ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
FOR XML PATH('')
), 1, 1, '')
--SELECT #DynamicColms
SELECT #CaseDynamicColms = STUFF((
SELECT '+ ' + 'CASE WHEN ' + COLUMN_NAME + ' IS NOT NULL THEN 1 ELSE 0 END'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
FOR XML PATH('')
), 1, 1, '')
--SELECT #CaseDynamicColms
SET #Sql = 'SELECT ' + #DynamicColms + ',' + #CaseDynamicColms + CHAR(13) + CHAR(10) + ' AS COUNT_NOT_NULL FROM ' + #TableName
PRINT #Sql
EXECUTE (#Sql)
Result
Column1 Column2 Column3 Column4 COUNT_NOT_NULL
-------------------------------------------------
X X NULL X 3
NULL NULL X X 2
NULL NULL NULL NULL 0

Pivot in sql server returns error "incorrect syntax near the keyword 'for': The DUPLICATE has not the answer [duplicate]

I'm looking for an efficient way to convert rows to columns in SQL server, I heard that PIVOT is not very fast, and I need to deal with lot of records.
This is my example:
-------------------------------
| Id | Value | ColumnName |
-------------------------------
| 1 | John | FirstName |
| 2 | 2.4 | Amount |
| 3 | ZH1E4A | PostalCode |
| 4 | Fork | LastName |
| 5 | 857685 | AccountNumber |
-------------------------------
This is my result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 2.4 | ZH1E4A | Fork | 857685 |
---------------------------------------------------------------------
How can I build the result?
There are several ways that you can transform data from multiple rows into columns.
Using PIVOT
In SQL Server you can use the PIVOT function to transform the data from rows to columns:
select Firstname, Amount, PostalCode, LastName, AccountNumber
from
(
select value, columnname
from yourtable
) d
pivot
(
max(value)
for columnname in (Firstname, Amount, PostalCode, LastName, AccountNumber)
) piv;
See Demo.
Pivot with unknown number of columnnames
If you have an unknown number of columnnames that you want to transpose, then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from yourtable
group by ColumnName, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName
from yourtable
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
See Demo.
Using an aggregate function
If you do not want to use the PIVOT function, then you can use an aggregate function with a CASE expression:
select
max(case when columnname = 'FirstName' then value end) Firstname,
max(case when columnname = 'Amount' then value end) Amount,
max(case when columnname = 'PostalCode' then value end) PostalCode,
max(case when columnname = 'LastName' then value end) LastName,
max(case when columnname = 'AccountNumber' then value end) AccountNumber
from yourtable
See Demo.
Using multiple joins
This could also be completed using multiple joins, but you will need some column to associate each of the rows which you do not have in your sample data. But the basic syntax would be:
select fn.value as FirstName,
a.value as Amount,
pc.value as PostalCode,
ln.value as LastName,
an.value as AccountNumber
from yourtable fn
left join yourtable a
on fn.somecol = a.somecol
and a.columnname = 'Amount'
left join yourtable pc
on fn.somecol = pc.somecol
and pc.columnname = 'PostalCode'
left join yourtable ln
on fn.somecol = ln.somecol
and ln.columnname = 'LastName'
left join yourtable an
on fn.somecol = an.somecol
and an.columnname = 'AccountNumber'
where fn.columnname = 'Firstname'
This is rather a method than just a single script but gives you much more flexibility.
First of all There are 3 objects:
User defined TABLE type [ColumnActionList] -> holds data as
parameter
SP [proc_PivotPrepare] -> prepares our data
SP [proc_PivotExecute] -> execute the script
CREATE TYPE [dbo].[ColumnActionList] AS TABLE
(
[ID] [smallint] NOT NULL,
[ColumnName] nvarchar NOT NULL,
[Action] nchar NOT NULL
);
GO
CREATE PROCEDURE [dbo].[proc_PivotPrepare]
(
#DB_Name nvarchar(128),
#TableName nvarchar(128)
)
AS
SELECT #DB_Name = ISNULL(#DB_Name,db_name())
DECLARE #SQL_Code nvarchar(max)
DECLARE #MyTab TABLE (ID smallint identity(1,1), [Column_Name] nvarchar(128), [Type] nchar(1), [Set Action SQL] nvarchar(max));
SELECT #SQL_Code = 'SELECT [<| SQL_Code |>] = '' '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Declare user defined type [ID] / [ColumnName] / [PivotAction] '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''DECLARE #ColumnListWithActions ColumnActionList;'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Set [PivotAction] (''''S'''' as default) to select dimentions and values '' '
+ 'UNION ALL '
+ 'SELECT ''-----|'''
+ 'UNION ALL '
+ 'SELECT ''-----| ''''S'''' = Stable column || ''''D'''' = Dimention column || ''''V'''' = Value column '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''INSERT INTO #ColumnListWithActions VALUES ('' + CAST( ROW_NUMBER() OVER (ORDER BY [NAME]) as nvarchar(10)) + '', '' + '''''''' + [NAME] + ''''''''+ '', ''''S'''');'''
+ 'FROM [' + #DB_Name + '].sys.columns '
+ 'WHERE object_id = object_id(''[' + #DB_Name + ']..[' + #TableName + ']'') '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Execute sp_PivotExecute with parameters: columns and dimentions and main table name'' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''EXEC [dbo].[sp_PivotExecute] #ColumnListWithActions, ' + '''''' + #TableName + '''''' + ';'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
EXECUTE SP_EXECUTESQL #SQL_Code;
GO
CREATE PROCEDURE [dbo].[sp_PivotExecute]
(
#ColumnListWithActions ColumnActionList ReadOnly
,#TableName nvarchar(128)
)
AS
--#######################################################################################################################
--###| Step 1 - Select our user-defined-table-variable into temp table
--#######################################################################################################################
IF OBJECT_ID('tempdb.dbo.#ColumnListWithActions', 'U') IS NOT NULL DROP TABLE #ColumnListWithActions;
SELECT * INTO #ColumnListWithActions FROM #ColumnListWithActions;
--#######################################################################################################################
--###| Step 2 - Preparing lists of column groups as strings:
--#######################################################################################################################
DECLARE #ColumnName nvarchar(128)
DECLARE #Destiny nchar(1)
DECLARE #ListOfColumns_Stable nvarchar(max)
DECLARE #ListOfColumns_Dimension nvarchar(max)
DECLARE #ListOfColumns_Variable nvarchar(max)
--############################
--###| Cursor for List of Stable Columns
--############################
DECLARE ColumnListStringCreator_S CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'S'
OPEN ColumnListStringCreator_S;
FETCH NEXT FROM ColumnListStringCreator_S
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Stable = ISNULL(#ListOfColumns_Stable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_S INTO #ColumnName
END
CLOSE ColumnListStringCreator_S;
DEALLOCATE ColumnListStringCreator_S;
--############################
--###| Cursor for List of Dimension Columns
--############################
DECLARE ColumnListStringCreator_D CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'D'
OPEN ColumnListStringCreator_D;
FETCH NEXT FROM ColumnListStringCreator_D
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Dimension = ISNULL(#ListOfColumns_Dimension, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_D INTO #ColumnName
END
CLOSE ColumnListStringCreator_D;
DEALLOCATE ColumnListStringCreator_D;
--############################
--###| Cursor for List of Variable Columns
--############################
DECLARE ColumnListStringCreator_V CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'V'
OPEN ColumnListStringCreator_V;
FETCH NEXT FROM ColumnListStringCreator_V
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Variable = ISNULL(#ListOfColumns_Variable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_V INTO #ColumnName
END
CLOSE ColumnListStringCreator_V;
DEALLOCATE ColumnListStringCreator_V;
SELECT #ListOfColumns_Variable = LEFT(#ListOfColumns_Variable, LEN(#ListOfColumns_Variable) - 1);
SELECT #ListOfColumns_Dimension = LEFT(#ListOfColumns_Dimension, LEN(#ListOfColumns_Dimension) - 1);
SELECT #ListOfColumns_Stable = LEFT(#ListOfColumns_Stable, LEN(#ListOfColumns_Stable) - 1);
--#######################################################################################################################
--###| Step 3 - Preparing table with all possible connections between Dimension columns excluding NULLs
--#######################################################################################################################
DECLARE #DIM_TAB TABLE ([DIM_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #DIM_TAB
SELECT [DIM_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'D';
DECLARE #DIM_ID smallint;
SELECT #DIM_ID = 1;
DECLARE #SQL_Dimentions nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_Dimentions', 'U') IS NOT NULL DROP TABLE ##ALL_Dimentions;
SELECT #SQL_Dimentions = 'SELECT [xxx_ID_xxx] = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Dimension + '), ' + #ListOfColumns_Dimension
+ ' INTO ##ALL_Dimentions '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Dimension + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
WHILE #DIM_ID <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #SQL_Dimentions = #SQL_Dimentions + 'AND ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
END
SELECT #SQL_Dimentions = #SQL_Dimentions + ' )x';
EXECUTE SP_EXECUTESQL #SQL_Dimentions;
--#######################################################################################################################
--###| Step 4 - Preparing table with all possible connections between Stable columns excluding NULLs
--#######################################################################################################################
DECLARE #StabPos_TAB TABLE ([StabPos_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #StabPos_TAB
SELECT [StabPos_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #StabPos_ID smallint;
SELECT #StabPos_ID = 1;
DECLARE #SQL_MainStableColumnTable nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_StableColumns', 'U') IS NOT NULL DROP TABLE ##ALL_StableColumns;
SELECT #SQL_MainStableColumnTable = 'SELECT xxx_ID_xxx = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Stable + '), ' + #ListOfColumns_Stable
+ ' INTO ##ALL_StableColumns '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Stable + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
WHILE #StabPos_ID <= (SELECT MAX([StabPos_ID]) FROM #StabPos_TAB)
BEGIN
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + 'AND ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
END
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + ' )x';
EXECUTE SP_EXECUTESQL #SQL_MainStableColumnTable;
--#######################################################################################################################
--###| Step 5 - Preparing table with all options ID
--#######################################################################################################################
DECLARE #FULL_SQL_1 NVARCHAR(MAX)
SELECT #FULL_SQL_1 = ''
DECLARE #i smallint
IF OBJECT_ID('tempdb.dbo.##FinalTab', 'U') IS NOT NULL DROP TABLE ##FinalTab;
SELECT #FULL_SQL_1 = 'SELECT t.*, dim.[xxx_ID_xxx] '
+ ' INTO ##FinalTab '
+ 'FROM ' + #TableName + ' t '
+ 'JOIN ##ALL_Dimentions dim '
+ 'ON t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1);
SELECT #i = 2
WHILE #i <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #FULL_SQL_1 = #FULL_SQL_1 + ' AND t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i)
SELECT #i = #i +1
END
EXECUTE SP_EXECUTESQL #FULL_SQL_1
--#######################################################################################################################
--###| Step 6 - Selecting final data
--#######################################################################################################################
DECLARE #STAB_TAB TABLE ([STAB_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #STAB_TAB
SELECT [STAB_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #VAR_TAB TABLE ([VAR_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #VAR_TAB
SELECT [VAR_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'V';
DECLARE #y smallint;
DECLARE #x smallint;
DECLARE #z smallint;
DECLARE #FinalCode nvarchar(max)
SELECT #FinalCode = ' SELECT ID1.*'
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #z = 1
WHILE #z <= (SELECT MAX([VAR_ID]) FROM #VAR_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ', [ID' + CAST((#y) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z) + '] = ID' + CAST((#y + 1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z)
SELECT #z = #z + 1
END
SELECT #y = #y + 1
END
SELECT #FinalCode = #FinalCode +
' FROM ( SELECT * FROM ##ALL_StableColumns)ID1';
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #x = 1
SELECT #FinalCode = #FinalCode
+ ' LEFT JOIN (SELECT ' + #ListOfColumns_Stable + ' , ' + #ListOfColumns_Variable
+ ' FROM ##FinalTab WHERE [xxx_ID_xxx] = '
+ CAST(#y as varchar(10)) + ' )ID' + CAST((#y + 1) as varchar(10))
+ ' ON 1 = 1'
WHILE #x <= (SELECT MAX([STAB_ID]) FROM #STAB_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ' AND ID1.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x) + ' = ID' + CAST((#y+1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x)
SELECT #x = #x +1
END
SELECT #y = #y + 1
END
SELECT * FROM ##ALL_Dimentions;
EXECUTE SP_EXECUTESQL #FinalCode;
From executing the first query (by passing source DB and table name) you will get a pre-created execution query for the second SP, all you have to do is define is the column from your source:
+ Stable
+ Value (will be used to concentrate values based on that)
+ Dim (column you want to use to pivot by)
Names and datatypes will be defined automatically!
I cant recommend it for any production environments but does the job for adhoc BI requests.
I modified Taryn's answer ("Pivot with unknown number of columnnames" version) to show more than 1 row in the result. This requires to have an additional "Group" column
DROP TABLE #yourtable
CREATE table #yourtable
([Id] int,[Group] int, [Value] varchar(6), [ColumnName] varchar(13))
;
INSERT INTO #yourtable
([Id],[Group], [Value], [ColumnName])
VALUES
(1,1, 'John', 'FirstName'),
(2,1, '2.4', 'Amount'),
(3,1, 'ZH1E4A', 'PostalCode'),
(4,1, 'Fork', 'LastName'),
(5,1, '857685', 'AccountNumber'),
(6,2, 'Pedro', 'FirstName'),
(7,2, '5.1', 'Amount'),
(8,2, '123456', 'PostalCode'),
(9,2, 'Torres', 'LastName'),
(10,2, '857686', 'AccountNumber')
;
;
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from #yourtable
group by [Group], ColumnName, id
having [group] = (SELECT TOP 1 MIN([Group])FROM #yourtable)
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName,[Group]
from #yourtable
GROUP BY [Group],ColumnName,Value
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
Please try
CREATE TABLE pvt (Present int, [Absent] int);
GO
INSERT INTO pvt VALUES (10,40);
GO
--Unpivot the table.
SELECT Code, Value
FROM
(SELECT Present, Absent
FROM pvt) p
UNPIVOT
(Value FOR Code IN
(Present, [Absent])
)AS unpvt;
GO
DROP TABLE pvt
One more option which could be very useful is using CROSS APPLY
-- Original data
SELECT * FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
-- row to columns using CROSS APPLY
SELECT Stage.id,v.idd, v.colc
FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
CROSS APPLY (VALUES ('col1', col1),('col2', col2),('col3', col3)) AS v(idd,colc)
GO

TSQL transform row values into one column table

Source Table
Col1 |Col2 |Col3 |Col4 | Col5
----------------------------------
hi | this | is | a | test
Destination Table
RowValues|
----------
hi
this
is
a
test
I am using Dynamic SQL.
Any help ?
This is my code , just change table name and the Id in the where clause to what suits you
DECLARE #sql nVARCHAR(max), #TableName nvarchar(100), #where nvarchar(max)
set #TableName = 'stockItems'
set #where= ' where id = 2'
select #sql ='Select '
select #sql = #sql+ + ' '''+ [name] +' = ''+ cast(' + [name] + ' as nvarchar(10)) as '+[name]+', '
from sys.columns where object_name (object_id) = #TableName
set #sql = stuff(#sql, len(#sql), 1, '') + ' From '+#TableName+ #where
print #sql
set #sql = REPLACE(#sql,', From',' From')
set #sql = #sql + ' '
print #sql
exec(#sql)
Now I need to create a new table that has one column that hold holds each value as a row
Thanks to #Mahmoud-Gamal
The solution should be something like below
declare #cols nvarchar(max)
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name = 'vehicles'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
declare #statement nvarchar(max)
set #statement ='
SELECT
ColumnName, Value
FROM
Vehicles
UNPIVOT
(
Value
FOR ColumnName
IN
(
'+#cols+'
)
)
AS A'
execute(#statement)
Please change the "vehicle" table name to any table on your database that has columns from different types (datetime, int and nvarchar) and the below error is shown
Any help ?
The type of column "Description" conflicts with the type of other columns specified in the UNPIVOT list.
Use the UNPIVOT table operator:
SELECT col AS RowValues
FROM table1 AS t
UNPIVOT
(
col
FOR value IN([col1],
[col2],
[col3],
[col4],
[col5])
) AS u;
SQL Fiddle Demo
This will give you:
| ROWVALUES |
|-----------|
| hi |
| this |
| is |
| a |
| test |
Update:
In case you don't know the names of the columns, and you want to do this dynamically, you have to do this using dynamic SQL.
But the problem is how to get the columns names?
You can get the columns names from the information_schema.columns, then concatenate them in one sql, then replace the columns' names in the UNPIVOT with this string, and execute that statement dynamically like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(column_name)
FROM information_schema.columns
WHERE table_name = 'Table1'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = ' SELECT col AS RowValues
FROM table1 AS t
UNPIVOT
(
val
FOR col IN ( ' + #cols + ' )
) AS u;';
EXECUTE(#query);
Updated SQL Fiddle Demo
I believe you want this
Select Col1 + Col2 + Col3 + Col4 + Col5 From Table
Or may be following
Select Col1 From Table1
union Select Col2 From Table1
union Select Col3 From Table1
union Select Col4 From Table1
union Select Col5 From Table1 ;

Resources