Find a specific column entry in an unknown table in a database? - sql

I'm aware of this topic ( Find a specific column in an unknown table in a database? ) and my problem is quite similar. The query I need is quite similar to this one (I think):
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name LIKE '%watcher%'
But what I need is a query where the column name is unknown, but I know what the content will be and I want to find out what the name of table/column is. (I know this sounds strange :-/ ).
I this possible?

Try using ApexSQL Search – it searches for both objects and data and it’s a free tool similar to SQL Search from Red Gate.
Another option if you don’t want to deal with third party tools is query that will use cursors to iterate through all columns in all tables but I’m afraid that it would turn out too complex and performance intensive.

Ok, I think that for your problem, you are gonna need dynamic sql, so first take a look at this link. If that weren't enough, the only solution that came to mind involves cursors, so I advise you to keep looking for others implementation of your problem. That said, you can try the following code (but you should test it on small tables first).
DECLARE #Query NVARCHAR(MAX), #Column NVARCHAR(100), #Table NVARCHAR(100)
DECLARE #Search NVARCHAR(100)
SET #Search = 'Your string'
CREATE TABLE #Results(Table_Name VARCHAR(100), Column_Name VARCHAR(100))
DECLARE Col CURSOR FOR
SELECT Table_Name, Column_Name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLLATION_NAME IS NOT NULL
ORDER BY TABLE_NAME, ORDINAL_POSITION
OPEN Col
FETCH NEXT FROM Col INTO #Table, #Column
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query = 'IF EXISTS (SELECT * FROM '+QUOTENAME(#Table)+' WHERE '+QUOTENAME(#Column)+'='''+#Search+''')
SELECT '''+#Table+''','''+#Column+''''
INSERT INTO #Results
EXEC sp_executesql #Query
FETCH NEXT FROM Col INTO #Table, #Column
END
CLOSE Col
DEALLOCATE Col
SELECT * FROM #Results

Have a look at the FREE Red-Gate tool called SQL Search which does this - it searches your entire database for any kind of string(s).
It's a great must-have tool for any DBA or database developer - did I already mention it's absolutely FREE to use for any kind of use??

Using SQL Workbench/J you can run the following statement:
WbGrepData -searchValue=watcher
it will search through all columns in all (accessible) tables and return all rows where the search term is found in at least one column.

USE DBName
GO
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 '%ColumName start from%'
ORDER BY schema_name, table_name;

It could help too
DECLARE #columnName as varchar(100)
SET #columnName = 'ColumnName'
SELECT t.name AS Table, c.name AS Column,
ty.name AS Tipo, c.max_length AS Length
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id
WHERE c.name LIKE #columnName
ORDER BY t.name, c.name

Related

SQL Column Name wildcard

I have a table with 30+ fields and I want to quickly narrow my selection down to all fields where column name start with 'Flag'.
select * Like Flag% from Table1
You will want to build a dynamic query as explained here: https://stackoverflow.com/a/4797728/9553919
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Foods'
AND table_schema = 'YourDB'
AND column_name LIKE 'Vegetable%'
This SQL Statement should be useful. You may be able to simplify it but it does work.
Edit2: I just now saw your pervasive-sql tag. Unfortunately I've never worked with that and don't know if the syntax is compatible with MS SQL Server. I'll let my answer here in case it helps others, but wanted to share that I tested this using SQL Server.
Edit: SCHEMA_NAME function isn't necessary. You can replace SCHEMA_NAME(schema_id) with the name of your schema in single quotes if you want, but either will work.
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
t.name = 'Table1' AND
c.name Like 'Flag%'
ORDER BY
c.name
or
SELECT t.name AS table_name,
'MySchema' 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
t.name = 'Table1' AND
c.name Like 'Flag%'
ORDER BY
c.name
To do this, you will need to query the system tables for the columns associated to the table and filter them to what you want. From there, place them into a variable table and create a CSV of columns. From there, you can dynamically construct your query as needed. The below example should help you get started.
DECLARE #tableName VARCHAR(100) = 'dbo.SomeTable'
DECLARE #columnNames TABLE
(
Id INT IDENTITY PRIMARY KEY,
ColumnName VARCHAR(100)
)
--Grabs all of the columns into a variable table
INSERT INTO #columnNames (ColumnName)
SELECT
[name]
FROM sys.columns
WHERE
[object_id] = OBJECT_ID(#tableName)
AND
[name] LIKE '%Flag'
DECLARE #columns VARCHAR(1000)
--Creates a CSV of columns
SET #columns =
STUFF(
(
SELECT
',' + ColumnName
FROM #columnNames
FOR XML PATH(''))
,1,1,'')
DECLARE #selectStatement NVARCHAR(4000) = CONCAT('SELECT ', #columns, ' FROM ', #tableName)
PRINT #selectStatement
EXEC #selectStatement

How do I select from tables with a specifically named column when that column contains a specific value?

I am trying to select all the tables that has the same column, and there is an instance in that column matches a string format that I specify.
For example,
Table1:
FirstName, LastName, ID
Table2:
ID, Value
Table3:
FirstName, Value
I want my result to show the tables that contains ID in which the ID begin with character 'a'
So far what I have is
SELECT SYS.TABLES.NAME FROM SYS.TABLES
INNER JOIN SYS.COLUMNS
ON SYS.TABLES.OBJECT_ID = SYS.COLUMS.OBJECT_ID
WHERE SYS.COLUMS.NAME = 'ID'
but then I have no clue how to continue. Any help is appreciated :)
This code is tested and it works:
Create a cursor that selects all of the table/column names where column = ID (we didn't need a column variable since you are only targeting 'ID', but I included it in case you or someone else needs more functionality).
Once you have that information stored, you can loop through the cursor and execute the dynamic SQL. Note that this is susceptible to SQL-injection, as is the case with practically all dynamic SQL.
This approach will give you a separate datatable for each table that has a column ID - even if there are no ID columns that meet your conditions (you'll just get a blank datatable for those).
Also, you can change the size of the variables as needed.
Please let me know if you need any clarification or modifications.
DECLARE #TableName varchar(255), #ColumnName varchar(255), #SQL varchar(1000)
DECLARE TempCursor CURSOR FOR
SELECT T.[name] AS [TableName], C.[name] AS [ColumnName]
FROM sys.tables T
JOIN sys.columns C
ON T.object_id = C.object_id
WHERE C.[name] = 'ID'
OPEN TempCursor
FETCH NEXT FROM TempCursor INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'SELECT * FROM ' + #TableName
SET #SQL = #SQL + ' WHERE ' + #ColumnName + ' like ''a%'''
EXECUTE (#SQL)
FETCH NEXT FROM TempCursor INTO #TableName, #ColumnName
END
CLOSE TempCursor
DEALLOCATE TempCursor
I still don't know if i got your question properly - but let's try!
Use the following query to get all tables with a column ID
SELECT SYS.TABLES.NAME , SYS.COLUMNS.NAME
FROM SYS.TABLES INNER JOIN SYS.COLUMNS
ON SYS.TABLES.OBJECT_ID = SYS.COLUMNS.OBJECT_ID
WHERE SYS.COLUMNS.NAME = 'ID';
You will have to iterate through the table names (think cursor, while etc.)
Inside the loop, try something like this
declare #resultcount int;
declare #QueryMain as nvarchar(4000)
set #QueryMain = N'SELECT * FROM <TABLE_NAME> WHERE ID LIKE ''a%'''
exec sp_executesql #QueryMain
set #resultcount = ##rowcount;
Inspect the value of #resultcount to see if the current table name qualifies and use something like a temporary table to collect it.
You may be use other options in sp_executesql to tweak this query
You can use "LIKE" to select once beginning with 'a':
SELECT SYS.TABLES.NAME AS t
FROM SYS.TABLES INNER JOIN SYS.COLUMNS
ON SYS.TABLES.OBJECT_ID = SYS.COLUMS.OBJECT_ID
WHERE SYS.COLUMNS.NAME = 'ID' AND EXISTS (SELECT * FROM t WHERE t.ID LIKE 'a%');
The symbol "%" indicates that any combination of characters can be present after the letter "a"

Microsoft SQL Server: Query to see list of all databases, and their corresponding tables, and columns

What I have found seems to only show this for one database at a time (e.g., select * from information_schema.columns). Is there something like select * from sys.databases which includes not just all of the database names, but also their own table and column names?
Would like to see all of these for the server: database names, table names, column names.
I'm trying to get familiar with a server at a new company, and want to just explore by browsing the table names and columns in every database. Also, I do not have write access, and am doing this in Tableau custom SQL.
Are you looking something like this? You can try msforeachdb
EXEC sp_MSforeachdb N'
BEGIN
use ?
select * from information_schema.tables
select * from information_schema.columns
END';
One I've used before:
EXEC master.sys.sp_MSForEachDB N'
IF ''?'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'')
BEGIN
USE ?
SELECT ''?'' AS theDatabase
, OBJECT_SCHEMA_NAME([tables].[object_id],DB_ID()) AS theSchema
, [tables].name AS theTable
, [columns].[name] AS theColumn
, [types].[name] AS theDatatype
, [columns].max_length AS columnSize
, [columns].[precision] AS columnPrecision
, [columns].scale AS columnScale
, [columns].is_nullable AS isNullable
FROM sys.tables AS [tables]
INNER JOIN sys.all_columns [columns] ON [tables].[object_id] = [columns].[object_id]
INNER JOIN sys.types [types] ON [columns].system_type_id = [types].system_type_id
AND [columns].user_type_id = [types].user_type_id
WHERE [tables].is_ms_shipped = 0
ORDER BY [tables].name, [columns].column_id
END
'
I usually dump it into a temp table so I can better manipulate the resulting data.
I haven't run into the issue with sp_MSforEachDB missing tables, but I'll check out Aaron's article about the issue. He's definitely a name I trust. :-)
I am not a big fan of undocumented procedures. And sp_msforeachdb will sometimes skip databases and nobody really seem to know why. Aaron Bertrand wrote alternatives here:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I also really don't care for cursors which is all that undocumented procedure is. If you look at the code it is one big ugly cursor. For something as simple as you desire I prefer to use some dynamic sql and sys tables to create it.
Here is how I would go about this task. It really isn't much more code than undocumented procedure. The big advantage is that is a LOT simpler to debug and there is no cursor. :)
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select ''' + d.name + ''' as DatabaseName
, t.name collate SQL_Latin1_General_CP1_CI_AS as TableName
, c.name collate SQL_Latin1_General_CP1_CI_AS as ColumnName
from ' + quotename(d.name) + '.sys.tables t
join ' + quotename(d.name) + '.sys.columns c on c.object_id = t.object_id UNION ALL '
from sys.databases d
set #SQL = LEFT(#SQL, len(#SQL) - 10) + ' order by DatabaseName, TableName, ColumnName'
select #SQL --once you are satisfied the sql is correct just uncomment the line below
--exec sp_executesql #SQL
Yo, maybe try this?
--Database Names
SELECT name
FROM sys.databases
--Table Names
SELECT name
FROM sys.tables
--Column Names
SELECT name
FROM sys.columns
That help?

Can I join sys.columns to their data?

Pinal Dave has this neat query where he finds all the columns named EmployeeID across all the tables in his database:
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 '%EmployeeID%'
order by schema_name, table_name;
Is there a way to join that data to the data in the matched columns?
I've tried adding an additional join and looking through the other sys.* items to see if one of them sounded like it might be the data.
I have also tried constructing an outer select around this one, but that requires the joins to be in a format that (clearly) doesn't work:
select ( /* above */ ) as foo
inner join foo.table_name bar on bar.something = foo.something --???
Is there something else I can be doing? My ultimate aim is to update EmployeeID for all of the rows in all of the tables.
As mentioned, this can't be done as you are imagining, as data is stored in tables, and the catalog views (sys.) contain information about database objects.
You can achieve what you're after with a cursor and dynamic sql:
DECLARE #col VARCHAR(MAX)
,#table VARCHAR(MAX)
,#schema VARCHAR(MAX)
,#strSQL VARCHAR(MAX)
DECLARE xyz CURSOR
FOR
SELECT c.name
,t.name
,s.name
FROM sys.tables t
JOIN sys.columns c
ON t.object_ID = c.object_ID
JOIN sys.schemas s
ON t.schema_id = s.schema_id
WHERE c.name LIKE '%EmployeeID%';
OPEN xyz
FETCH NEXT FROM xyz
INTO #col,#table,#schema
WHILE ##FETCH_STATUS = 0
BEGIN
SET #strSQL =
'UPDATE '+#schema+'.'+#table+'
SET '+#col+' = ''Something''
'
PRINT (#strSQL)
FETCH NEXT FROM xyz
INTO #col,#table,#schema
END
CLOSE xyz
DEALLOCATE xyz
GO
It's a good idea to test dynamic sql with the PRINT command before actually running it via EXEC.
No, you can't in the way you seem to want.
Try writing code that generates T-SQL in something like LINQPad. Or write code that generates dynamic SQL in T-SQL itself.

Associate Database Name with Table List

It is no problem to list all tables with schemas on a server
SELECT SCHEMA_NAME(schema_id), name FROM sys.tables
How can I determine which database the tables reside in ?
sys.tables exists in all databases so I am not following the fact that you don't know the db you are in. you can run DB_NAME(DB_ID()) to get the db name
SELECT DB_NAME(DB_ID()),SCHEMA_NAME(schema_id), name FROM sys.tables
but in this case DB_NAME(DB_ID()) will return the same value for every row
to do it for all database, you can do this
EXEC sp_msforeachdb 'use [?] SELECT ''?'',SCHEMA_NAME(schema_id), name
FROM sys.tables'
You can of course dump it into a table as well
CREATE TABLE #output (DatabaseName VARCHAR(1000),
SchemaName VARCHAR(1000),
TableName VARCHAR(1000))
INSERT #output
EXEC sp_msforeachdb 'use [?] SELECT ''?'',SCHEMA_NAME(schema_id), name
FROM sys.tables'
SELECT * FROM #output
Just as a FYI, the sp_msforeachdb proc is undocumented and you should not use it for production code, to quickly find something is fine, for production code roll your own version of this proc
See also Aaron Bertrand's posts here:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I ran into this problem when I was creating query that I wanted to be able to run against a different database on my server, and include the other database's name name without hard-coding it into the query.
The query essentially looked like this:
SELECT DB_NAME() db_name
, SCHEMA_NAME(schema_id) schema_name
, name table_name
FROM OtherDB.sys.tables --The OtherDB is to specify that I am running
--this for a different database than the one
--I'm logged in to for my current session.
The problem was that even though I specify OtherDB.sys.tables in the from clause, DB_NAME() always returned the current database I was in. Yes, I could put a USE OtherDB at the beginning, but it seemed like there should be another way. I looked through every sys view I could find, but could never find anything that would link sys.databases and sys.tables.
What I eventually found was SQL Server's INFORMATION_SCHEMA.TABLES This view includes the Database name as the first column (referred to as TABLE_CATAOLG).
SELECT TABLE_CATALOG
, TABLE_SCHEMA
, TABLE_NAME
, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
With these views, you can easily compare the tables in two databases:
SELECT a.TABLE_CATALOG
, a.TABLE_SCHEMA
, a.TABLE_NAME
, a.TABLE_TYPE
, b.TABLE_CATALOG
, b.TABLE_SCHEMA
, b.TABLE_NAME
, b.TABLE_TYPE
FROM OneDatabase.INFORMATION_SCHEMA.TABLES a
FULL OUTER JOIN TwoDatabase.INFORMATION_SCHEMA.TABLES b
ON a.TABLE_SCHEMA = b.TABLE_SCHEMA
AND a.TABLE_NAME = b.TABLE_NAME
If the databases are on separate servers that are linked you should be able to use this query by using all four parts of the Fully Qualified Table Name.
If your intention is just to include the current database name, why not just:
SELECT DB_NAME(), SCHEMA_NAME(schema_id), name FROM sys.tables;
If your intention is to pull all names from all databases, I personally prefer dynamic SQL like this instead of sp_msforeachdb:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + 'UNION ALL
SELECT ''' + name + ''', s.name, t.name
FROM ' + QUOTENAME(name) + '.sys.tables AS t
INNER JOIN ' + QUOTENAME(name) + '.sys.schemas AS s
ON t.schema_id = s.schema_id'
FROM sys.databases
WHERE database_id > 4;
SET #sql = STUFF(#sql, 1, 13, '');
PRINT #sql;
-- EXEC sp_executesql #sql;