For the necessity of my application, I must return the column names of a query as the very first row.
Now I must PIVOT this result in order to UNION it with my result set, but the difficult part is: it must be dynamic, so if I ever add new columns to this table, the SELECT will bring all the names pivoted.
The following SELECT brings me the Column names:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Codes'
ORDER BY INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION
And my result set is:
COLUMN_NAME
Id
CodeName
Country
StartDate
EndDate
What I expect is:
Id CodeName Country StartDate EndDate (... whatever other columns I might have)
Is there any easy way to do that without hardcoding the column names?
Thank you in advance!
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + COLUMN_NAME + ']',
'[' + COLUMN_NAME + ']')
FROM (SELECT DISTINCT COLUMN_NAME,INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION O
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'CODES') PV
ORDER BY O
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT TOP 0 * FROM
(
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''CODES''
) x
PIVOT
(
MIN(COLUMN_NAME)
FOR [COLUMN_NAME] IN (' + #cols + ')
) p
'
EXEC SP_EXECUTESQL #query
Starting with SQL Server 2017, there's a function for this: STRING_AGG.
I used the QUOTENAME function as well here, to make adding the [ ] brackets easier.
DECLARE #ColumnNames NVARCHAR(MAX);
SELECT #ColumnNames = STRING_AGG(QUOTENAME(COLUMN_NAME), ',')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='Codes';
Simple way would be by declaring a variable and assigning the columns with comma separted. Try this.
DECLARE #col_list VARCHAR(max)=''
SELECT #col_list += '['+ COLUMN_NAME + '],'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Codes'
ORDER BY INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION
SELECT #col_list = LEFT(#col_list, Len(#col_list) - 1)
SELECT #col_list
Related
Having a simple query such as:
select * from foo
Where the output has the columns
bar | abc | def
---------------
...............
It is possible to encapsulate this query into another one and give sequential names instead to the columns such as 1|2|3... (mantaining all the rows intact)?
I know this sounds weird and probably it isn't even possible.
Thanks for the help!
Try This
DECLARE #Sql NVARCHAR(max),
#TableName VARCHAR(100) = '<YourTableName>'
;WITH CTE
AS
(
SELECT DENSE_RANK()OVER (ORDER BY COLUMN_NAME) Rno,
TABLE_NAME,
COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_CATALOG='Database'
AND TABLE_NAME = #TableName
)
SELECT 'SELECT '+ STUFF((SELECT ', '+ COLUMN_NAME +' AS '+ QUOTENAME(CAST(Rno AS VARCHAR))
FROM CTE
FOR XML PATH ('')),1,1,'') + ' FROM '+#TableName
PRINT #Sql
EXEC (#Sql)
Try this:
DECLARE #TableName NVARCHAR(100) = 'YOUR TABLENAME HERE',
#Query NVARCHAR(MAX) = 'SELECT '
SELECT #Query = CONCAT(#Query, ' ', COLUMN_NAME, ' AS ', QUOTENAME(ROW_NUMBER() OVER(ORDER BY ORDINAL_POSITION)), ',
')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
SET #Query = SUBSTRING(#Query, 0, LEN(#Query) - 3) + '
FROM ' + QUOTENAME(#TableName)
You use INFORMATION_SCHEMA.COLUMNS to get all our table's columns,
ROW_NUMBER() window function to generate sequential number for column aliases and sp_ExecuteSQL to execute dynamic generated queries.
I have seen multiple questions on how to retrieve every column from every table along with its data type, among many other pieces of information which can be summarised in the shortest way with this query:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
However, is it possible to get all the data from the columns and the rows they belong to get the first row in the table alongside this? I have not found a way to do so thus far. Is it possible to do such, maybe also having a WHERE condition such as checking if the table contains a list of specific columns before returning it e.g.:
SELECT <AllTablesAndColumns+FirstRow>
FROM <WhereTheyCanBeSelectedFrom>
WHERE <TheTableHasTheseSpecificColumns>
Which would return the table name, column name and the data contained within those columns for each row.
If you are looking for more of an EAV structure
Let's say that we're looking for all tables with a column name of ZIPCODE
Example
Declare #S varchar(max) = ''
SELECT #S = #S +'+(Select top 1 SourceTable='''+A.Table_Name+''',* from '+quotename(A.Table_Name)+' for XML RAW)'
FROM INFORMATION_SCHEMA.COLUMNS A
Where COLUMN_NAME in ('ZipCode')
Declare #SQL varchar(max) = '
Declare #XML xml = '+stuff(#S,1,1,'')+'
Select SourceTable = r.value(''#SourceTable'',''varchar(100)'')
,Item = attr.value(''local-name(.)'',''varchar(100)'')
,Value = attr.value(''.'',''varchar(max)'')
From #XML.nodes(''/row'') as A(r)
Cross Apply A.r.nodes(''./#*'') AS B(attr)
Where attr.value(''local-name(.)'',''varchar(100)'') not in (''SourceTable'')
'
Exec(#SQL)
Returns
You could build dynamic query:
DECLARE #sql NVARCHAR(MAX) =
N'SELECT *
FROM (VALUES (1)) AS s(n)
<joins>';
DECLARE #joins NVARCHAR(MAX)= '';
SELECT #joins += FORMATMESSAGE('LEFT JOIN (SELECT TOP 1 * FROM %s ) AS sub%s
ON 1=1' + CHAR(10), table_schema + '.' + table_name,
CAST(ROW_NUMBER() OVER(ORDER BY 1/0) AS VARCHAR(10)))
FROM (SELECT DISTINCT table_schema, table_name
FROM INFORMATION_SCHEMA.COLUMNS
-- WHERE ... -- custom logic based on column type/name/...
) s;
SET #sql = REPLACE(#sql, '<joins>', #joins);
PRINT #sql;
EXEC(#sql);
DBFiddle Demo
The dynamic query has structure:
SELECT *
FROM (VALUES (1)) AS s(n) -- always 1 row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab1 ) AS sub1 ON 1=1 -- get single row
LEFT JOIN (SELECT TOP 1 * FROM dbo.tab2 ) AS sub2 ON 1=1
LEFT JOIN (SELECT TOP 1 * FROM dbo.tabC ) AS sub3 ON 1=1
Please treat it as starting point. You could easily extend it with WHERE condition for each subquery and return specific columns instead of *.
EDIT:
Version with UNION ALL:
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = COALESCE(#sql + ' UNION ALL', '') +
FORMATMESSAGE(' SELECT TOP 1 tab_name=''%s'',col_name=''%s'',col_val=%s FROM %s'+CHAR(10)
,table_name, column_name, column_name, table_schema + '.' + table_name)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE 'colV%';
PRINT #sql;
EXEC(#sql);
DBFiddle Demo2
For example this is my ID: 07E485
I need to find this ID in all tables wherever it is found
All columns, which might carry this value, are sort of string-type...
Something like: select * from **alltables** where **anyColumn**='07E485'
The following query will return all tables in the database yourDBName whose name contains 07E485.
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND
TABLE_CATALOG = 'yourDBName' AND
TABLE_NAME LIKE '%07E485%'
If I misread your requirement, and you instead wanted to find all tables precisely named 07E485 in any database, then you can use the following query:
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND
TABLE_NAME = '07E485'
In each database you have a view called INFORMATION_SCHEMA.COLUMNS, you can use this view to query through all of your tables.
This is the way I'ld do it, if anyone know a better way, feel free.. :)
SET NOCOUNT ON
DECLARE #Table varchar(255), #Schema varchar(255), #SQL varchar(MAX)
DECLARE table_cursor CURSOR FOR
SELECT TABLE_NAME, TABLE_SCHEMA
FROM INFORMATION_SCHEMA.COLUMNS -- This is a system view where you can see all columns of a database.
WHERE UPPER(COLUMN_NAME) = 'ID' -- This makes sure you don't loop through any tables that don't have a Column called 'ID'.
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO #Table, #Schema
WHILE ##FETCH_STATUS = 0 BEGIN
-- This part creates your queries.
SET #SQL = 'SELECT * FROM '+#Schema+'.'+#Table+'
WHERE CAST(ID as varchar) = ''07E485''' -- Casting ID to varchar to avoid data type errors.
-- This executes the query.
EXEC(#SQL)
-- If a result is found, i.e. ID is equal to '07E485' somewhere in the table, Table name is printed on the "Messages" tab.
IF ##ROWCOUNT > 0 PRINT #Table
FETCH NEXT FROM table_cursor INTO #Table, #Schema
END
CLOSE table_cursor
DEALLOCATE table_cursor
To see which tables contain id = '07E485', go to "Messages" and you will have a list of them.
UPDATE My answer completely re-written
Try it like this: This dynamic SQL will check all string-type columns if they are equal to the given search string. You might want to add more data types to the output to get a better look onto the table's row. But one cannot simply put SELECT * as there are data types not allowed in XML without extra effort.
Secondly, by using QUOTENAME, I avoid syntax errors due to column or table names with blanks...
DECLARE #Search VARCHAR(10)='07E485';
DECLARE #cmd VARCHAR(MAX);
WITH TableNames AS
(
SELECT t.*
,t.TABLE_CATALOG + '.' + t.TABLE_SCHEMA + '.' + t.TABLE_NAME AS FullTblName
,QUOTENAME(t.TABLE_CATALOG)+ '.' + QUOTENAME(t.TABLE_SCHEMA) + '.' + QUOTENAME(t.TABLE_NAME) AS FullTblNameQuoted
,
STUFF(
(
SELECT 'OR ' + QUOTENAME(c.COLUMN_NAME) + '=''' + #Search + ''' '
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_CATALOG=t.TABLE_CATALOG AND c.TABLE_SCHEMA=t.TABLE_SCHEMA AND c.TABLE_NAME=t.TABLE_NAME
AND DATA_TYPE LIKE '%char%' --add more types if needed
FOR XML PATH('')
),1,3,'') AS WhereFilter
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE TABLE_TYPE='BASE TABLE'
)
SELECT #cmd = STUFF(
(
SELECT DISTINCT 'UNION ALL SELECT (SELECT ' + (SELECT STUFF((SELECT ',' + QUOTENAME(COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_CATALOG=TableNames.TABLE_CATALOG
AND c.TABLE_NAME =TableNames.TABLE_NAME
AND c.DATA_TYPE LIKE '%char%'
FOR XML PATH('')),1,1,'')) + ' FROM ' + FullTblNameQuoted
+ ' WHERE ' + WhereFilter
+ ' FOR XML PATH(''row''),ROOT(''' + REPLACE(REPLACE(FullTblName,'.','_'),' ','') + '''),TYPE) AS XmlData '
FROM TableNames
WHERE WhereFilter IS NOT NULL
FOR XML PATH('')
),1,10,'')
SET #cmd='SELECT XmlData FROM(' + #cmd + ') AS tbl WHERE XmlData IS NOT NULL;'
PRINT LEN(#cmd)
EXEC(#cmd)
I am using SQL Server 2014 and I have the following T-SQL query which joins 2 tables:
SELECT a.*, b.* FROM TEMP a
INNER JOIN Extras b ON b.ResaID = a.ResaID
I would like to pull ALL the columns from TEMP and all the columns from "Extras" with the exception of the ResaID column as it is already included in a.* in the above query. Basically, I want to pull a.* + b.* (excluding b.ResaID).
I know I can write the query in the form:
Select a.*, b.column2, b.column3,...
but since b.* has got around 40 columns, is there a way to write the query in a more simplified way to exclude b.ResaID, rather than specify each of the columns in the "Extras" table?
Unfortunately, there is no such syntax. You could either use asterisks (*) and just ignore the duplicated column in your code, or explicitly list the columns you need.
You should create a view and select the columns you need from that view. Here is a script that will generate that view for you:
DECLARE #table1 nvarchar(20) = 'temp'
DECLARE #table1key nvarchar(20) = 'ResaID'
DECLARE #table2 nvarchar(20) = 'Extras'
DECLARE #table2key nvarchar(20) = 'ResaID'
DECLARE #viewname varchar(20) = 'v_myview'
DECLARE #sql varchar(max) = ''
SELECT #sql += '], a.[' + column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
SELECT #sql += '], b.[' + column_name
FROM
(
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
EXCEPT
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table1
) x
SELECT
#sql = 'CREATE view ' +#viewname+ ' as SELECT '
+ STUFF(#sql, 1, 3, '') + '] FROM ['
+#table1+ '] a JOIN ['+ #table2
+'] b ON ' + 'a.' + #table1key + '=b.' + #table2key
EXEC(#sql)
You can simply solve this using a dynamic sql query.
DECLARE #V_SQL AS NVARCHAR(2000)='' --variable to store dynamic query
,#V_TAB1 AS NVARCHAR(200)='TEMP' --First Table
,#V_TAB2 AS NVARCHAR(200)='Extras' --Second Table
,#V_CONDITION AS NVARCHAR(2000)='A.ResaID = B.ResaID' --Conditions
SELECT #V_SQL = STUFF(
( SELECT ', '+TCOL_NAME
FROM
( SELECT 'A.'+S.NAME AS TCOL_NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1
UNION ALL
SELECT 'B.'+S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB2
AND S.NAME NOT IN (SELECT S.NAME
FROM SYSCOLUMNS AS S
WHERE OBJECT_NAME(ID) = #V_TAB1)
) D
FOR XML PATH('')
),1,2,'')
EXECUTE ('SELECT '+#V_SQL+'
FROM '+#V_TAB1+' AS A
INNER JOIN '+#V_TAB2+' AS B ON '+#V_CONDITION+' ')
I have a procedure that does a "select * from view" and this output contains an inverted comma in one of the columns. I want to do a replace() to remove the extra inverted comma but I cannot change the "select *" as this is a dynamic query that is used in different scenarios.
As evident, the following query does not work
select replace(*, '"', '') from ReportRegistry
Some help appreciated.
I wanted to know if there is any way I can remove the quotes without knowing the column names. I want them to be removed from all columns. The columns names are different in different cases.
The view is defined within the procedure differently in different cases. The dynamic colum names are added as text and them executed using sp_executesql.
DECLARE #ColNames NVARCHAR(MAX) = ''
--Dynamically build a list of column names for the view, separated by commas.
SELECT #ColNames = #ColNames +
CASE
--Use the REPLACE function for "String" type data types. Did I leave any data types out?
--Change "x" to the character(s) you want to replace.
WHEN c.DATA_TYPE IN ('VARCHAR', 'CHAR', 'NVARCHAR', 'NCHAR') THEN 'REPLACE([' + c.COLUMN_NAME + '], ''x'', '''') AS [' + c.COLUMN_NAME + '],'
--All other data types.
ELSE '[' + c.COLUMN_NAME + '],'
END
--In addition to user tables, views are included in INFORMATION_SCHEMA.COLUMNS (and in INFORMATION_SCHEMA.TABLES).
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = 'ReportRegistry'
--Retain original order of the view's columns.
ORDER BY c.ORDINAL_POSITION
--Remove last comma
SET #ColNames = LEFT(#ColNames, LEN(#ColNames) - 1)
EXEC ('SELECT ' + #ColNames + ' FROM ReportRegistry ')
select replace(TargetColumnName, '"', '')
,Column2
,Column3
,..... and so on.....
from ReportRegistry
declare #tbl varchar(max) = 'table_or_view_name'
declare #cols varchar(max) =
(select case when data_type='varchar' then 'REPLACE('+column_name+',''"'','''')' else column_name end+char(10)+','
from information_schema.columns
where table_name = #tbl for xml path(''))
declare #sql varchar(max)='select '+left(#cols,len(#cols)-1)+' from '+#tbl
print #sql
exec(#sql)