Unpivot all other columns - sql-server

Im new to Stack overflow and SQL and I'm trying to replicate a function in Microsoft Power Query to use in SQL instead.
I know how to Unpivot and keep 1 Column in SQL and then had to reference all the other columns by name to make the unpivot.
Now I need to Keep 3 ID columns and Unpivot the rest of the columns
(This table has 355 columns right now and will change)
Can anyone help me with this?
This is the furthest I got (Thanks to RAV DBLearning on Youtube), but I cant seem to find a way to Convert the columns types to 1 type.
DECLARE
#SQLSTRING NVARCHAR(MAX),
#COLUMNLIST NVARCHAR(1000) = ''
SELECT
#COLUMNLIST = #COLUMNLIST + QUOTENAME(NAME) + ','
FROM
sys.columns
WHERE
OBJECT_ID = OBJECT_ID('xp.XPROPERTYVALUES') AND
--COLUMN_ID NOT IN(1,2,3)
COLUMN_ID IN(452,453,454)
SELECT
#COLUMNLIST = LEFT(#COLUMNLIST,LEN(#COLUMNLIST)-1)
SET
#SQLSTRING =
'
SELECT
upv.id,
upv.item_id,
upv.itemtype_id,
upv.X_Category,
upv.X_Values
FROM
xp.XPROPERTYVALUES
UNPIVOT
(
X_Values FOR X_Category
IN
(' + #COLUMNLIST + ')
) AS upv
'
PRINT
(#SQLSTRING)
EXECUTE
sp_executesql #SQLSTRING

It just needs a source query.
And you can re-use the calculated column list for that.
DECLARE #SQLSTRING NVARCHAR(MAX),
#COLUMNLIST NVARCHAR(MAX);
DECLARE #TABLENAME VARCHAR(30) = 'xp.XPROPERTYVALUES';
SELECT #COLUMNLIST = CONCAT(#COLUMNLIST + ', ', QUOTENAME(NAME))
FROM sys.columns
WHERE OBJECT_ID = OBJECT_ID(#TABLENAME)
AND LOWER(NAME) NOT LIKE '%id';
SET #SQLSTRING = N'SELECT upv.id
, upv.item_id, upv.itemtype_id
, upv.X_Category, upv.X_Values
FROM
(
SELECT id, item_id, itemtype_id,
'+ #COLUMNLIST + N'
FROM '+ #TABLENAME +N'
) src
UNPIVOT
(
X_Values FOR X_Category IN (' + #COLUMNLIST + N')
) upv';
-- SELECT #SQLSTRING;
EXECUTE sp_executesql #SQLSTRING;
db<>fiddle here

Related

Select all unique values from all columns in a table

I need to select all unique values from all columns in a table.
I have tried to implement the query below which I found in the thread How to get unique values from all columns of a table in SQL Server.
declare #Sql_Str varchar(8000)='';
select #Sql_Str=#Sql_Str+' select cast (' +name +' as varchar(500))
from <yourtable> union'
from sys.columns
where [object_id]=object_id('<yourtable>');
set #Sql_Str=SUBSTRING(#Sql_Str,1,len(#Sql_Str)-6);
exec(#Sql_Str)
I cannot get that query to work however. My table has 118 columns. I think that may be more data than the query above may handle.
Try something like this:
DECLARE #Schema VARCHAR(500)='dbo';
DECLARE #tableName VARCHAR(500)='SomeTable';
DECLARE #cmd NVARCHAR(MAX)=
(
SELECT STUFF(
(
SELECT ' UNION ALL SELECT ''' + c.TABLE_SCHEMA + ''' AS TableSchema '
+ ',''' + c.TABLE_NAME + ''' AS TableName '
+ ',''' + c.COLUMN_NAME + ''' AS ColumnName '
+ ',''' + c.DATA_TYPE + ''' AS ColumnType '
+ ',CAST(' + QUOTENAME(c.COLUMN_NAME)+' AS NVARCHAR(MAX)) AS Value '
+ ' FROM ' + QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME)
+ ' WHERE ' + QUOTENAME(c.COLUMN_NAME) + ' IS NOT NULL '
+ ' GROUP BY ' + QUOTENAME(c.COLUMN_NAME) + ' '
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE TABLE_NAME=#TableName
AND TABLE_SCHEMA=#Schema
--exclude not supported types
--AND c.DATA_TYPE NOT IN('xml') --add more types
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)'),1,10,'')
);
--PRINT #cmd
EXEC(#cmd);
This statement will first create a long list of UNION ALL SELECT with GROUP BY (better than DISTINCT) as dynamically created SQL and executes this with EXEC().
You can decomment PRINT to examine the statement created.
This should work in tSQL:
declare #table_name varchar(55)
set #table_name= 'IV00101' ---- <-- Change this to your table name
create table #colcount (
colname varchar(55),
dct int,
tot int
)
create table #colContent (
colname varchar(55),
col_val nvarchar(max),
col_val_count int
)
create table #sqlexecs( s varchar(max))
declare #col_name varchar(max), #sql nvarchar(max), #sql2 nvarchar(max)
declare c cursor for
select name from sys.columns where [object_id]=object_id(#table_name)
open c
fetch next from c into #col_name
while ##FETCH_STATUS = 0
begin
set #sql = 'select cn.name, count(distinct '+#col_name+') as dct_numrow, count('+#col_name+') as tot_numrow from '+#table_name+' join (select name from sys.columns where name = '''+#col_name+''' and [object_id]=object_id('''+#table_name+''')) cn on cn.name = '''+#col_name+''' group by cn.name'
set #sql2 = 'select ' +#col_name+', count('+#col_name+') as colvalcnt from '+#table_name+' group by '+#col_name
--insert into #sqlexecs values (#sql) --uncomment to view sql selects produced by #sql
--insert into #sqlexecs values (#sql2) --uncomment to view sql selects produced by #sql2
insert into #colcount execute sp_executesql #sql
------
declare #d int, #t int
set #d = (select dct from #colcount where colname = #col_name)
set #t = (select tot from #colcount where colname = #col_name)
if (#d <> #t)
begin
insert into #colContent (colname) values (#col_name)
insert into #colContent (col_val,col_val_count) execute sp_executesql #sql2
end
else
begin
insert into #colContent values (#col_name,1,1)
end
fetch next from c into #col_name
end
close c
deallocate c
--select * from #sqlexecs -- uncomment to view sql code produced by #sql and #sql2
select * from #colcount --order by dct desc
select * from #colContent
drop table #colcount
drop table #colContent
drop table #sqlexecs
The first table shows column name, distinct value count, and total value count.
The second table shows column name, distinct values, and the number of times a distinct value appears. If values in column are all distinct (column is a candidate key), colname | 1 | 1 is shown. This should work if copy/pasted, please let me know it doesn't. Dev for use in Dynamics GP.

Select any column with respect to cell containing

I have a table named a. Some cells containing a string 'Empty' in many columns. I want to find this columns. Can you help me?.
Try this dynamic query, it will check all the columns with character data and list the columns which has the word 'Empty'.
DECLARE #SearchText VARCHAR(50) = 'Empty'
DECLARE #sql NVARCHAR(MAX) = 'SELECT '
SELECT #sql = #sql + 'MAX(CASE WHEN ' + c.COLUMN_NAME + ' LIKE ''%'+ #SearchText +'%'' THEN ''' + c.COLUMN_NAME +''' ELSE '''' END) + '','' + '
FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_SCHEMA = 'dbo' and c.TABLE_NAME = 'a'
AND c.DATA_TYPE IN ('varchar','char','nvarchar','nchar','sysname')
SET #sql = #sql + ''''' FROM dbo.a'
EXEC sys.sp_executesql #sql
Hope this helps
Use the LIKE operator:
SELECT a.*
FROM a
WHERE a.col1 LIKE '%Empty%' OR a.col2 LIKE '%Empty%' OR ...
In sql server you can get object id of table then using that object id you can fetch columns. In that case it will be as below:
Step 1: First get Object Id of table
select * from sys.tables order by name
Step 2: Now get columns of your table and search in it:
select * from a where 'Empty' in (select name from sys.columns where object_id =1977058079)
Note: object_id is what you get fetch in first step for you relevant table
You can do it using unpivot with an help of dynamic query , here i have done below an working sample for you , there might be some modification you might have to do to put the below psedo code with your working .
Sample table structure been used :
create table ColTest
(
name1 varchar(10),
name2 varchar(10),
name3 varchar(10),
name4 varchar(10)
)
insert into ColTest values ('sdas','asdasda','ewrewr','erefds')
insert into ColTest values ('sdas','asdasda','EMPTY','erefds')
insert into ColTest values ('EMPTY','asdasda','ewrewr','erefds')
DECLARE #table_name SYSNAME
SELECT #table_name = 'ColTest'
DECLARE #tmpTable SYSNAME
SELECT #tmpTable = 'ColTest2'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = '
SELECT * into
' + #tmpTable + '
FROM ' + #table_name + '
UNPIVOT (
cell_value FOR column_name IN (
' + STUFF((
SELECT ', [' + c.name + ']'
FROM sys.columns c WITH(NOLOCK)
LEFT JOIN (
SELECT i.[object_id], i.column_id
FROM sys.index_columns i WITH(NOLOCK)
WHERE i.index_id = 1
) i ON c.[object_id] = i.[object_id] AND c.column_id = i.column_id
WHERE c.[object_id] = OBJECT_ID(#table_name)
AND i.[object_id] IS NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + '
)
) unpiv'
PRINT #SQL
EXEC sys.sp_executesql #SQL
select * from ColTest2 where cell_value = 'EMPTY'
I'd suggest dynamic SQL
--First you set the variable #TableName to your actual table's name.
DECLARE #TableName VARCHAR(100)='a';
--The following statement will create a list of all columns with a data type containing the word "char" (others should not hold the value Empty)
DECLARE #ColList VARCHAR(MAX)=
STUFF(
(
SELECT ' OR ' + COLUMN_NAME + ' LIKE ''%empty%'''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME=#TableName AND DATA_TYPE LIKE '%char%'
FOR XML PATH('')
),1,4,'');
--This statement builds a command
DECLARE #cmd VARCHAR(MAX)=
(
SELECT 'SELECT * FROM [' + #TableName + '] WHERE ' + #ColList
);
--Here you can see the command
PRINT #cmd;
--And here it is executed
EXEC(#cmd);

How to SELECT * and get all field alphabetical ordered

This could be a strange question.
I have a table with 100+ columns. I would like to SELECT * all columns and get the resulting query with columns ordered alphabetically.
Is it possible in T-SQL?
Thanks
You could build a dynamic SQL statement using the information available in the system catalog view.
The sample code below shows how:
DECLARE #sql AS NVARCHAR(MAX)
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #tbl NVARCHAR(MAX) = N'your_table' -- this is your source table
SELECT #cols= ISNULL(#cols + ',','') + QUOTENAME(c.name)
FROM sys.tables t
join sys.columns c ON c.object_id = t.object_id
WHERE t.name = #tbl
ORDER BY c.name
SET #sql = N'SELECT ' + #cols + ' FROM ' + #tbl
EXEC sp_executesql #sql
DynamicSQL (SQL Server) example:
declare #TABLE varchar(200) set #TABLE='persons'
declare #SQL nvarchar(max)
set #SQL='select '
select #SQL= #SQL + column_name + ','
from information_schema.columns where table_name=#TABLE order by column_name
select #SQL = left(#SQL,len(#SQL)-1) + ' from ' + #TABLE -- trims the trailing comma
--select #SQL -- If you want to see the query
exec sp_executesql #SQL
DECLARE #Table NVARCHAR(MAX)='T'--pass your table name
DECLARE #SQL NVARCHAR(MAX)='SELECT '
SELECT #SQL=#SQL+',' +NAME FROM
(
SELECT TOP 100 QUOTENAME(NAME) AS NAME
FROM sys.columns
WHERE object_id =
(
SELECT OBJECT_ID FROM sys.tables
WHERE NAME =#Table
)
ORDER BY NAME
) AS SS
SELECT #SQL=STUFF(#SQL,8,1,'')+' FROM '+#Table
EXEC sp_executesql #sql

Find blank cells in entire table without specifying all column names in OR condition

There are 190 columns in my table and I trying to list out empty or blank cells. Already i know to check this condition in where clause by using OR, but for no. of 190 columns it seems very long work....So I Tried following query in order to list out null values but when i alter the query according to my criteria by changing in the place of 'IS NULL' as '' to get empty cells it doesn't works
DECLARE #tb NVARCHAR(255) = N'dbo.[Sales_Five_Years]';
DECLARE #sql NVARCHAR(MAX) = N'SELECT * FROM ' + #tb
+ ' WHERE 1 = 0';
SELECT #sql += N' OR ' + QUOTENAME(name) + ' IS NULL'
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#tb);
EXEC sp_executesql #sql;
Please guide me, Thanks in advance
Try this
SELECT #sql += N' OR convert(varchar(50),' + Quotename(name) + ')='''''
FROM sys.columns
WHERE [object_id] = Object_id(#tb);
check this example
i didn't get any error.
CREATE TABLE pr1
(
col VARCHAR(50),
col1 VARCHAR(50)
)
INSERT INTO pr1
SELECT '',NULL
UNION
SELECT NULL,'a'
UNION
SELECT 'b','a'
SELECT #sql += N' OR convert(varchar(50),' + Quotename(name) + ')='''''
FROM sys.columns
WHERE [object_id] = Object_id(#tb);
PRINT #sql
EXEC Sp_executesql
#sql;

Does anybody know of a proc to turn a row into an INSERT statement?

Does anybody know of a proc or script which will generate any row into an insert statement into the same table?
Basically, I'd like to call something like
exec RowToInsertStatement 'dbo.user', 45;
And the following code would be output
insert into dbo.MyTable( FirstName, LastName, Position)
values( 'John', 'MacIntyre', 'Software Consultant');
I realize I could
insert into dbo.MyTable
select * from dbo.MyTable where id=45;
But this obviously won't work, because the ID column will complain (I hope it complains) and there's no way to just override that one column without listing all columns, and in some tables there could be hundreds.
So, does anybody know of a proc that will write this simple insert for me?
EDIT 3:04: The purpose of this is so I can make a copy of the row, so after the INSERT is generated, I can modify it into something like
insert into dbo.MyTable( FirstName, LastName, Position)
values( 'Dave', 'Smith', 'Software Consultant');
.. no obviously this contrived example is so simple it doesn't make sense, but if you have a table with 60 columns, and all you need is to change 3 or 4 values, then it starts to be a hassle.
Does that make sense?
Update
I believe the following dynamic query is what you want:
declare #tableName varchar(100), #id int, #columns varchar(max), #pk varchar(20)
set #tableName = 'MyTable'
set #pk = 'id'
set #id = 45
set #columns = stuff((select ',['+c.name+']' [text()] from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
print 'insert into [' + #tableName + '] (' + #columns + ')
select ' + #columns + '
from [' + #tableName + ']
where ' + #pk + ' = ' + cast(#id as varchar)
Update 2
The actual thing that you wanted:
declare #tableName varchar(100), #id int, #columns nvarchar(max), #pk nvarchar(20), #columnValues nvarchar(max)
set #tableName = 'MyTable'
set #pk = 'id'
set #id = 45
set #columns = stuff((select ',['+c.name+']' [text()] from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
set #columnValues = 'set #actualColumnValues = (select' +
stuff((select ','','''''' + cast(['+c.name+'] as varchar(max)) + '''''''' [text()]' [text()]
from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
+ 'from [' + #tableName + ']
where ' + #pk + ' = ' + cast(#id as varchar)
+ 'for xml path(''''))'
--select #columnValues
declare #actualColumnValues nvarchar(max), #columnValuesParams nvarchar(500)
SET #columnValuesParams = N'#actualColumnValues nvarchar(max) OUTPUT';
EXECUTE sp_executesql #columnValues, #columnValuesParams, #actualColumnValues OUTPUT;
--SELECT stuff(#actualColumnValues, 1,1, '')
declare #statement nvarchar(max)
set #statement =
'insert into [' + #tableName + '] (' + #columns + ')
select ' + stuff(#actualColumnValues,1,1,'')
print #statement
What it does is this:
It generates the insert statement and then it queries the actual data from the table and generates the select statement with that data. May not work correctly for some really complex datatypes but for varchars, datetimes and ints should work like a charm.
This stored proc works great for me:
http://vyaskn.tripod.com/code.htm#inserts
Did you know that in Enterprise Manager and SQL Server Management Studio that you can, from the object browser, drag the list of columns into the text window and it will drop the names of all the columns into the text, separated by commas?

Resources