Find table information where part of column matches char variable - sql

I'm trying to find any tables with columns containing the word date somewhere in the column name.
All of my queries are either all or nothing: they return all tables in the DB, or no results at all.
When I run a query without the variable, it works, as seen here.
select *
from MyDB.INFORMATION_SCHEMA.COLUMNS
where column_name like '%date%'
However, I can't get it to work by using a variable.
declare #temp varchar = 'date'
select*
from MyDB.INFORMATION_SCHEMA.COLUMNS
where column_name like '%' + #temp + '%'
The reason I'd like to do this is because I need to run this on more than one DB (such as below), and I have to perform this several times (for more than just date), and I'd like the process to go more smoothly.
select *
from MyDB1.INFORMATION_SCHEMA.COLUMNS
where column_name like '%date%'
union all
select *
from MyDB2.INFORMATION_SCHEMA.COLUMNS
where column_name like '%date%'
union all
select *
from MyDB3.INFORMATION_SCHEMA.COLUMNS
where column_name like '%date%'

One query with cursor to loop through all the database on your server or you can explicitly loop through certain database
Query
DECLARE #DB_Name SYSNAME;
DECLARE #Sql NVARCHAR(MAX)= '';
DECLARE #cur CURSOR;
SET #Cur = CURSOR FOR
SELECT name
FROM sys.sysdatabases
--WHERE name IN ('DBName1', 'DBName2', 'DBName3'); --<-- uncomment this line and
-- specify the database names
OPEN #cur
FETCH NEXT FROM #Cur INTO #DB_Name
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #Sql = N'
SELECT t.name
,c.name
FROM '+ QUOTENAME(#DB_Name) + '.sys.tables t
INNER JOIN ' + QUOTENAME(#DB_Name) + '.sys.columns c ON c.object_id = t.object_id
WHERE t.name LIKE ''%test%'''
EXEC(#Sql)
FETCH NEXT FROM #Cur INTO #DB_Name
END
CLOSE #cur
DEALLOCATE #cur

You can use what ever source of column info you prefer but the issue you are having is because of your variable declaration.
declare #temp varchar = 'date' is equivalent to
declare #temp varchar(1) = 'date' so
select #temp returns 'd' so you are getting any columns containing 'd'
when you declare your variable make sure it has a length that is sufficient to store the longest of the strings you will be searching for. Jason's answer will work too but it is because of the variable declaration not the source of the data.

I guess problem is with your variable declaration part.
By default any variable declared with datatype VARCHAR will be considered as VARCHAR(1) in SQLServer.
declare #temp varchar = 'date'
print #temp --d
So try declaring your variable as VARCHAR(4).
declare #temp varchar(4) = 'date'
print #temp --date

Related

Retrieve Max loaded date across all tables on a DB

Output I'm trying to get to;
(Database name = ATT)
Table Name
Column name
MAX loaded date = MAX(loaded_date) for this column only
loaded_date is a column in around 50 tables in a database with the same name and datatype (Datetime)
select * FROM sys.tables
select * FROM syscolumns
I've been exploring the system tables without much luck, looking at some posts it may be done dynamic SQL which I've never done.
You can write an sql that writes an sql..
SELECT REPLACE(
'select ''{tn}'' as table_name, max(loaded_date) as ld from {tn} union all'
,'{tn}',table_name)
FROM
information_schema.columns
WHERE
column_name = 'loaded_date'
Run that, then copy all but the final UNION ALL out of the results window and into the query window, and run again
If you wanted to get all this into a single string for dynamic exec, i guess it'd look like (untested) a procedure that contained:
DECLARE #x NVARCHAR(MAX);
SELECT #x =
STRING_AGG(
REPLACE(
'select ''{tn}'' as table_name, max(loaded_date) as ld from {tn}'
,'{tn}',table_name)
,' union all ')
FROM
information_schema.columns
WHERE
column_name = 'loaded_date';
EXECUTE sp_executesql #x;
If your SQLS is old and doesnt have string_agg it's a bit more awkward - but there are many examples of "turn rows into CSV" in sql server that look like STUFF..FOR XML PATH - https://duckduckgo.com/?t=ffab&q=rows+to+CSV+SQLS&ia=web
I wrote up a more permanent type of script that does this. It returns a result set of the list of tables in the current database with a column named loaded_date along with the MAX(loaded_date) result from each table. This script individually queries each table by looping through and running the query on each table individually and keeping track of the max value for each table in a table variable. It also has a #Debug variable that allows you to see the text of the queries that would be run instead of actually running them and implements custom error message to troubleshoot any issues.
/*disable row count messages*/
SET NOCOUNT ON;
/*set to 1 to debug (aka just print queries instead of running)*/
DECLARE #Debug bit = 0;
/*get list of tables to query and assign a unique index to row to assist in looping*/
DECLARE #TableList TABLE(
SchemaAndTableName nvarchar(257) NOT NULL
,OrderToQuery bigint NOT NULL
,MaxLoadedDate datetime NULL
,PRIMARY KEY (OrderToQuery)
);
INSERT INTO #TableList (SchemaAndTableName,OrderToQuery)
SELECT
CONCAT(QUOTENAME(s.name),N'.', QUOTENAME(t.name)) AS SchemaAndTableName
,ROW_NUMBER() OVER(ORDER BY s.name, t.name) AS OrderToQuery
FROM
sys.columns AS c
INNER JOIN sys.tables AS t ON c.object_id = t.object_id
INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
WHERE
c.name = N'loaded_date';
/*declare and set some variables for loop*/
DECLARE #NumTables int = (SELECT TOP (1) OrderToQuery FROM #TableList ORDER BY OrderToQuery DESC);
DECLARE #I int = 1;
DECLARE #CurMaxDate datetime;
DECLARE #CurTable nvarchar(257);
DECLARE #CurQuery nvarchar(max);
/*start loop*/
WHILE #I <= #NumTables
BEGIN
/*build text of current query*/
SET #CurTable = (SELECT SchemaAndTableName FROM #TableList WHERE OrderToQuery = #I);
SET #CurQuery = CONCAT(N'SELECT #MaxDateOut = MAX(loaded_date) FROM ', #CurTable, N';');
/*check debugging status*/
IF #Debug = 0
BEGIN
BEGIN TRY
EXEC sys.sp_executesql #stmt = #CurQuery
,#params = N'#MaxDateOut datetime OUTPUT'
,#MaxDateOut = #CurMaxDate OUTPUT;
END TRY
BEGIN CATCH
DECLARE #ErrorMessage nvarchar(max) = CONCAT(
N'Error querying table ', #CurTable, N'.', NCHAR(13), NCHAR(10)
,N'Errored query: ', NCHAR(13), NCHAR(10), #CurQuery, NCHAR(13), NCHAR(10)
,N'Error message: ', ERROR_MESSAGE()
);
RAISERROR(#ErrorMessage,16,1) WITH NOWAIT;
/*on error end loop so error can be investigated*/
SET #I = #NumTables + 1;
END CATCH;
END;
ELSE /*currently debugging*/
BEGIN
PRINT(CONCAT(N'Debug output: ', #CurQuery));
END;
/*update value in our table variable*/
UPDATE #TableList
SET MaxLoadedDate = #CurMaxDate
WHERE
OrderToQuery = #I;
/*increment loop*/
SET #I = #I + 1;
END;
SELECT
SchemaAndTableName AS TableName
,MaxLoadedDate AS Max_Loaded_date
FROM
#TableList;
I like this solution better as querying each table one at a time would be much less system impact than attempting one large UNION ALL query. Querying a large set of a tables all at once could cause some serious resource semaphore or locking contention (depending on usage of your db).
It is fairly well commented, but let me know if something is not clear.
Also, just a note, dynamic SQL should be used as a last resort. I provided this script to answer your question, but you should explore better options than something like this.
You can go for undocumented stored procedure sp_MSforeachtable. But, don't use in production code, as this stored procedure might not be available in future versions.
Read more on sp_MSforeachtable
EXEC sp_MSforeachtable 'SELECT ''?'' as tablename, max(loaded_Date) FROM ?'

Dynamic query inside if exists is not working

I am writing Dynamic SQL inside If exists. I have a query on sys.columns to check that department Id column exists or not. If it exists I have to check row count and go inside if statement. I am not able to get mistake in my query. Someone please point out.
DECLARE #TableName VARCHAR(100) = '[Student]'
DECLARE #ColName VARCHAR(10) = 'DeptId'
DECLARE #Query NVARCHAR(1000) = '
SELECT *
FROM sys.columns
WHERE Name =' + #ColName + 'AND Object_ID = Object_ID(' + #TableName + ')'
DECLARE #rowcnt INT
EXEC sp_executesql #query
SELECT #rowcnt = ##ROWCOUNT
IF (#rowcnt > 0)
BEGIN
PRINT 'row present'
END
ERROR - Incorrect syntax near 'Object_ID'.
And it always returns row count as 1.
I'm going to tell what's wrong, but I'll let you fix the query because there are a lot of options. The query returns an error. I'm not sure why ##ROWCOUNT is returned as 1, but you are missing single quotes for the Name. So, in all likelihood, you are going to get an error that DEPTID is not a valid column.
You can naively fix this by putting in the single quotes. But, you should be using that arguments to sp_execute_sql to pass arguments in.
And, I'll add that this is probably unnecessary, because normally one would do:
if (exists (select 1
from information_schema.columns
where table_name = #table_name and column_name = #column_name
)
)
begin
. . .
end;
Of course, this doesn't do the object resolution from the name, but that is rarely needed, especially if you are not putting strange characters in table and column names.
Fortunately dynamic SQL isn't needed in this case. You can use a parameterized query.
SELECT #rowcnt = COUNT(*)
FROM sys.columns
WHERE [name] = #ColName
AND [object_id] = OBJECT_ID(#TableName)
DECLARE #TableName SYSNAME = 'dbo.test',
#ColName SYSNAME = 'col1'
IF COL_LENGTH(#TableName, #ColName) IS NOT NULL
PRINT 'column present'

Looping through a column in SQL table that contains names of other tables

I have fairly new to using SQL, currently I have a table that has a column that contains the names of all the tables I want to use for one query, so what I want to do is to loop through that column and go to every single one of these tables and then search one of their columns for a value (there could be multiple values), so whenever a table contains the value, I will list the name of the table. Could someone give me a hint of how this is done? Is cursor needed for this?
I don't have enough reputation to comment but is the table with the column that contain the table names all in one column, meaning that all the table names are comma separated or marked with some sort of separator? This would cause the query to be a little more complicated as you would have to take care of that before you start looping through your table.
However, this would require a cursor, as well as some dynamic sql.
I will give a basic example of how you can go about this.
declare #value varchar(50)
declare #tableName varchar(50)
declare #sqlstring nvarchar(100)
set #value = 'whateveryouwant'
declare #getTableName = cursor for
select tableName from TablewithTableNames
OPEN #getTableName
fetch NEXT
from #getTableName into #tableName
while ##FETCH_STATUS = 0
BEGIN
set #sqlstring = 'Select Count(*) from ' + #tableName + 'where ColumnNameYouwant = ' + #value
exec #sqlstring
If ##ROWcount > 0
insert into #temptable values (#tableName)
fetch next
from #getTableName into #tableName
END
select * from #temptable
drop table #temptable
close #getTableName
deallocate #getTableName
I'm currently not able to test this out as for time constraint reasons, but this is how I would go about doing this.
You could try something like this:
--Generate dynamic SQL
DECLARE #TablesToSearch TABLE (
TableName VARCHAR(50));
INSERT INTO #TablesToSearch VALUES ('invoiceTbl');
DECLARE #SQL TABLE (
RowNum INT,
SQLText VARCHAR(500));
INSERT INTO
#SQL
SELECT
ROW_NUMBER() OVER (ORDER BY ts.TableName) AS RowNum,
'SELECT * FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
FROM
#TablesToSearch ts
INNER JOIN sys.tables t ON t.name = ts.TableName
INNER JOIN sys.columns c ON c.object_id = t.object_id;
--Now run the queries
DECLARE #Count INT;
SELECT #Count = COUNT(*) FROM #SQL;
WHILE #Count > 0
BEGIN
DECLARE #RowNum INT;
DECLARE #SQLText VARCHAR(500);
SELECT TOP 1 #RowNum = RowNum, #SQLText = SQLText FROM #SQL;
EXEC (#SQLText);
DELETE FROM #SQL WHERE RowNum = #RowNum;
SELECT #Count = COUNT(*) FROM #SQL;
END;
You would need to change the "1" I am using as an example to the value you are looking for and probably add a CONVERT/ CAST to make sure the column is the right data type?
You actually said that you wanted the name of the table, so you would need to change the SQL to:
'SELECT ''' + ts.TableName + ''' FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
Another thought, it would probably be best to insert the results from this into a temporary table so you can dump out the results in one go at the end?

Selecting column names that have specified value

We are receiving rather large files, of which we have no control over the format of, that are being bulk-loaded into a SQL Server table via SSIS to be later imported into our internal structure. These files can contain over 800 columns, and often the column names are not immediately recognizable.
As a result, we have a large table that represents the contents of the file with over 800 Varchar columns.
The problem is: I know what specific values I'm looking for in this data, but I do not know what column contains it. And eyeballing the data to find said column is neither efficient nor ideal.
My question is: is it at all possible to search a table by some value N and return the column names that have that value? I'd post some code that I've tried, but I really don't know where to start on this one... or if it's even possible.
For example:
A B C D E F G H I J K L M N ...
------------------------------------------------------------
'a' 'a' 'a' 'a' 'a' 'b' 'a' 'a' 'a' 'b' 'b' 'a' 'a' 'c' ...
If I were to search this table for the value 'b', I would want to get back the following results:
Columns
---------
F
J
K
Is something like this possible to do?
This script will search all tables and all string columns for a specific string. You might be able to adapt this for your needs:
DECLARE #tableName sysname
DECLARE #columnName sysname
DECLARE #value varchar(100)
DECLARE #sql varchar(2000)
DECLARE #sqlPreamble varchar(100)
SET #value = 'EDUQ4' -- *** Set this to the value you're searching for *** --
SET #sqlPreamble = 'IF EXISTS (SELECT 1 FROM '
DECLARE theTableCursor CURSOR FAST_FORWARD FOR
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo' AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME NOT LIKE '%temp%' AND TABLE_NAME != 'dtproperties' AND TABLE_NAME != 'sysdiagrams'
ORDER BY TABLE_NAME
OPEN theTableCursor
FETCH NEXT FROM theTableCursor INTO #tableName
WHILE ##FETCH_STATUS = 0 -- spin through Table entries
BEGIN
DECLARE theColumnCursor CURSOR FAST_FORWARD FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tableName AND (DATA_TYPE = 'nvarchar' OR DATA_TYPE = 'varchar')
ORDER BY ORDINAL_POSITION
OPEN theColumnCursor
FETCH NEXT FROM theColumnCursor INTO #columnName
WHILE ##FETCH_STATUS = 0 -- spin through Column entries
BEGIN
SET #sql = #tableName + ' WHERE ' + #columnName + ' LIKE ''' + #value +
''') PRINT ''Value found in Table: ' + #tableName + ', Column: ' + #columnName + ''''
EXEC (#sqlPreamble + #sql)
FETCH NEXT FROM theColumnCursor INTO #columnName
END
CLOSE theColumnCursor
DEALLOCATE theColumnCursor
FETCH NEXT FROM theTableCursor INTO #tableName
END
CLOSE theTableCursor
DEALLOCATE theTableCursor
One option you have is to be a little creative using XML in SQL Server.
Turn a row at a time into XML using cross apply and query for the nodes that has a certain value in a second cross apply.
Finally you output the distinct list of node names.
declare #Value nvarchar(max)
set #Value= 'b'
select distinct T3.X.value('local-name(.)', 'nvarchar(128)') as ColName
from YourTable as T1
cross apply (select T1.* for xml path(''), type) as T2(X)
cross apply T2.X.nodes('*[text() = sql:variable("#Value")]') as T3(X)
SQL Fiddle
If you have access to the files are RegEx will be must faster than performing a generic search in SQL.
If you are forced to use SQL #pmbAustin's answer is the way to go. Be warned, it won't run quickly.

Search sql database for a column name, then search for a value within the retuned columns

This query will search a database for a specific column name. I would like to go one step further and search the returned columns for a specific value.
SELECT t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name,
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE '%Example%'
Any ideas?
Many thanks
For example, I have a database named Organisation. I have more than one table where tax_id column is present.
Most of the time, we have to find such a column from the whole database.
The solution is provided below:
select table_name,column_name from information_schema.columns
where column_name like '%tax%'
There is no matter in query to database name which ever you just need to change willing Column Name and will found required result
Search any value Like computer in whole database in which column and in which tables value computer exists
For it first we need to write a store procedure then we reuse it for our search i got it from http://vyaskn.tripod.com/search_all_columns_in_all_tables.htm very perfect result.
after executing store procedure we got required result as in given below image.
Image showing complete search result of keyword computer from whole database.
Above was concept to solve it.Exact Query fullfilling above requirment is below
Select tax_id from (select table_name from information_schema.columns
where column_name = 'tax_id') as temp
There is not such system table present for this kind of searching. Whereas you can try this for your purpose
DECLARE #ValueToSearch NVARCHAR(500)
DECLARE #SearchColumn NVARCHAR(100)
DECLARE #TableName NVARCHAR(200)
DECLARE #ColumnName NVARCHAR(200)
SET #ValueToSearch ='YOUR VALUE TP SEARCH'
SET #SearchColumn = 'YOUR COLUMN'
DECLARE #getResult CURSOR
SET #getResult = CURSOR FOR
SELECT t.name AS table_name,c.name AS column_name FROM sys.tables AS t INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID WHERE c.name = #SearchColumn
OPEN #getResult
FETCH NEXT FROM #getResult INTO #TableName,#ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET NOCOUNT ON ;
DECLARE #RESULT INT;
DECLARE #TYPE INT
DECLARE #QUERY NVARCHAR(1000)
SET #QUERY = 'select #RESULT=count(*) from ' + ISNULL(#TableName,'') +' WHERE '+ ISNULL(#ColumnName,'')+'='''+ ISNULL(#ValueToSearch,'') +''''
EXEC sp_executesql #QUERY,
N'#result int OUTPUT, #type int OUTPUT',
#RESULT OUTPUT,
#TYPE OUTPUT
IF(ISNULL(#RESULT,0)>0)
BEGIN
SET NOCOUNT ON;
SELECT ' COLUMN '+ #ColumnName + ' OF TABLE ' +#TableName+ ' HAS THIS VALUE.'
END
FETCH NEXT FROM #getResult INTO #TableName,#ColumnName
END
CLOSE #getResult
DEALLOCATE #getResult
Thanks
Manoj