Find all unique values of a column name in a SQL database - sql

We are building a large database using SQL. Every table in the database has many columns but one of the columns in the tables tells who added the row of data. That value "Name of person" is tied to a variable in SSIS. Again, the variable tells who added the row. How can I create a query to pull back all the names in that column, no matter where it is used in the database. The value of the column is different depending on the day.

RE: Every table has the same <Column_Name> ... a query to pull back all the values of that column, no matter where it is used in the database.
IF you have a large database and every table has the same column <Column_Name>, then you will be pulling a value from every row in every table ... in a very large database. Not sure that is what you want to do, but it can be easily done. The following will work even if <column_name> is only in a few tables.
Grab a list of every schema.table that contains <column_name>, then loop over it to get the the value for <column_name>. The following should work.
DECLARE #colname sysname = '<Column_name>' -- just in case it is not in every table
-- capture name of every table here
CREATE TABLE #tablename ( schema_name sysname, table_name sysname, Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED)
INSERT INTO #tablename (schema_name, table_name)
SELECT s.name schemaname, t.name tablename FROM sys.columns c
INNER JOIN sys.tables t ON t.object_id = c.object_id
INNER JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE c.name = #colname
-- capture result of query here
CREATE TABLE #result ( schema_name sysname, table_name sysname, column_value VARCHAR(100) )
DECLARE #i INT = 1, #imax INT
SELECT #imax = MAX(Id) FROM #tablename
-- loop over tablename
DECLARE #query NVARCHAR(255)
WHILE #i <= #imax
BEGIN
SELECT #query = N'SELECT ' + schema_name + '.' + table_name + ',' + ' <Column_Name> FROM ' + schema_name + '.' + table_name
FROM #tablename WHERE Id = #i
INSERT INTO #result ( schema_name, table_name, column_value)
EXEC sp_executesql #query
SET #i += 1
END

Related

How to use loop for like or ilike function?

I am currently asked to validate the result of a global search which to find all records containing some keywords cross whole database. To do so, I need to check all the rows in each table that have keywords. The result for this global search is ready and partially of the result looks like the one below:
table_name
column_name
keyword
cnt
Wf_Process
Name_EN
FEC
11
Wf_Process
FTABLENAME
GB
14
ICCClass
Name_EN
GB
4
What I am trying to do is using the 'like' operator to extract all the data in each table where columns containing keywords. That is, I will use query such as:
select distinct Name_EN, FTABLENAME from Wf_Process where Name_EN like '%FEC%' or FTABLENAME like '%GB%'
and
select distinct Name_EN from ICCClass where Name_EN like '%GB%'
to pull out all data I need in whole database. If I only have three records, then it is not a problem. However my result returned over thousands tables have more than 10K rows containing keywords in total. Therefore, I was trying to use a loop to do this mission but I failed.
My question therefore is, does anyone have any idea to write a loop to do the 'like' search for all the tables in one time? Or is there another way rather than loop can do this in SQL Server?
Thank you very much!
You need dynamic SQL for this. You can generate a big UNION ALL query of all the tables together.
DECLARE #sql nvarchar(max) = (
SELECT STRING_AGG(CAST('
SELECT
' + QUOTENAME(con.table_name, '''') + ' table_name,
' + QUOTENAME(con.column_name, '''') + ' column_name,
' + QUOTENAME(con.keyword, '''') + ' keyword,
COUNT(*) cnt
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' t
WHERE t.' + QUOTENAME(c.name) + ' LIKE ' + QUOTENAME('%' + con.keyword + '%', '''')
AS nvarchar(max)), '
UNION ALL')
FROM YourConditions con
JOIN sys.tables t ON t.table_name
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
WHERE t.name = con.table_name
AND c.name = con.column_name
);
PRINT #sql; -- your friend
EXEC sp_executesql #sql;
There are more efficient ways to do this if there are many columns or keywords per table, but this should get you started.
The question looks odd, thou can be done via dynamic sql, but not as a regular solution more like a one off or POC/test.
use tempdb
GO
drop table if exists search_result
go
create table search_result (
table_name sysname
,column_name sysname
,keyword varchar(100))
go
insert into search_result values
('Wf_Process', 'Name_EN', 'FEC')
,('Wf_Process' , 'FTABLENAME', 'GB')
,('ICCClass', 'Name_EN', 'GB')
GO
drop table if exists result
create table result (val varchar(500))
go
declare #col sysname
declare #tab sysname
declare #kw varchar(100)
declare #sql varchar(1000)
while exists (select * from search_result)
begin
select top 1
#tab = table_name
, #col = column_name
, #kw = keyword
from search_result
set #sql = concat('insert into result select ', #col, ' from ', #tab, ' where ', #col, ' like ''%', #kw, '%''' )
print(#sql)
exec (#sql)
delete from search_result
where table_name = #tab and column_name = #col and keyword = #kw
end
GO
select * from result
--initially trying to get all the column name with its respective table name, finding out if there is that particular keyword in all table,
--if yes, insert into temp table, if there aint keyword, it will insert the record too but the count of record will be zero.
--trying to make it dynamic and running it inside the loop by assigning row number to each table with different column.
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
CREATE TABLE #TEMP (TABLE_NAME NVARCHAR(200), COLUMN_NAME NVARCHAR(200), KEYWORD NVARCHAR(200), CNT INT)
DECLARE #TABLE NVARCHAR(200)
DECLARE #TABLE_ID INT
SET #TABLE_ID = 1
DECLARE #TABLE_NAME NVARCHAR (200)
DECLARE #FOR_LOOP INT
DECLARE #COLUMN NVARCHAR(200)
DECLARE #COLUMN_NAME NVARCHAR(200)
--TOTAL NO OF RECORDS FOR LOOP
SET #FOR_LOOP = (SELECT COUNT(*)
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)
DECLARE #STRINGS NVARCHAR(200)
SET #STRINGS = '%FEC%' --------->ENTER YOUR KEYWORD HERE, TRY ONE AT A TIME
DECLARE #COUNT INT
WHILE #TABLE_ID <= #FOR_LOOP
BEGIN
SET #TABLE = (SELECT CAST(TABLE_NAME AS NVARCHAR(200))
FROM
(SELECT ROW_NUMBER()OVER(ORDER BY T.NAME) TABLE_ID
,S.TABLE_SCHEMA SCHEMA_NAME ,T.NAME TABLE_NAME, C.name COLUMN_NAME
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)A
WHERE TABLE_ID = #TABLE_ID)
SET #TABLE_NAME = '['+#TABLE+']'
SET #COLUMN = (SELECT CAST(COLUMN_NAME AS NVARCHAR(200))
FROM
(SELECT ROW_NUMBER()OVER(ORDER BY T.NAME) TABLE_ID
,S.TABLE_SCHEMA SCHEMA_NAME ,T.NAME TABLE_NAME, C.name COLUMN_NAME
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)A
WHERE TABLE_ID = #TABLE_ID)
SET #COLUMN_NAME = '['+#COLUMN+']'
DECLARE #EXEC NVARCHAR(200) = 'SELECT ' + #COLUMN_NAME +' FROM ' + #TABLE_NAME + ' WHERE ' + #COLUMN_NAME + ' LIKE ' + ''''+#STRINGS+''''
--THERE MUST BE ANOTHER WAY TO REPLACE SP_EXECUTESQL FOR BETTER PERFORMANCE, AS IT WILL BE EXECUTED AS MANY TIMES AS THERE ARE RECORDS IN #FOR_LOOP
EXEC SP_EXECUTESQL #EXEC
SET #COUNT = (SELECT ##ROWCOUNT)
IF (SELECT ##ROWCOUNT) >=1
BEGIN
INSERT INTO #TEMP VALUES
(#TABLE_NAME, #COLUMN_NAME, #STRINGS, #COUNT)
SET #TABLE_ID = #TABLE_ID + 1
END
ELSE
--DONT KNOW WHY THIS ELSE PART IS NOT TOUCHED, WHEREAS I THINK IT SHOULD HAVE
BEGIN
PRINT 'RECORD NOT FOUND'
END
END
GO
--AFTER THE ABOVE BLOCK IS EXECUTED THEN TRY RUNNING BELOW ONE
SELECT *FROM #TEMP WHERE CNT>0
--THERE MAY BE MULTIPLE ERROS, MUST BE EDITED AND CAN MAKE IT AS A PROCEDURE TOO.

Dynamic SQL Server Query loop through schema find primary key duplicate

EDIT: There seems to be some confusion around the create table statements. These are there solely as a demonstration of what tables *might come in to our synapse instance, not as actual code that will run. The important part of the question is contained in the latter half.
I am trying to create a stored procedure that loops through every table in a supplied schema and outputs the count of duplicate primary key rows for each table. Assume that the data is being supplied from elsewhere and the primary keys are not being enforced. For example I may have three tables in the stack schema:
CREATE TABLE stack.table1(
id int,
name NVARCHAR(MAX),
color NVARCHAR(20)
PRIMARY KEY (id))
INSERT INTO stack.table1 VALUES(1,'item1','yellow')
(2,'item2','blue')
(2,'item2','blue')
CREATE TABLE stack.table2(
id int,
name NVARCHAR(MAX),
size NVARCHAR(1)
PRIMARY KEY (id,size))
INSERT INTO stack.table2 VALUES(1,'item1','L')
(2,'item2','M')
(3,'item2','S')
CREATE TABLE stack.table3(
id int,
name NVARCHAR(MAX),
weight NVARCHAR(20)
PRIMARY KEY (id))
INSERT INTO stack.table1 VALUES(1,'item1','200lb')
(2,'item2','150lb')
(3,'item2','125lb')
I want to supply a variable to a stored procedure to indicate the schema (in this case 'stack') and have that procedure spit out a table with the names of the tables in the schema and the counts of duplicate primary key rows. So in this example a stored procedure called 'loopcheck' would look like this:
Query:
EXEC loopcheck #schema = 'stack'
Output:
table
duplicate_count
table1
1
table2
0
table3
0
I am using an Azure Synapse instance so there are several functions that are not available (such as FOR XML PATH and others.) Since each table may have a single column primary key or a composite primary key I need to join to the system provided tables to get primary key info. My general idea was like so:
CREATE procedure loopcheck #schema= NVARCHAR(MAX)
AS
BEGIN
create table #primarykey(
SCHEMA_NAME nvarchar(400),
TABLE_NAME nvarchar(500),
COLUMN_NAME nvarchar(500)
)
insert into #primarykey
select l.TABLE_SCHEMA,
l.TABLE_NAME,
l.COLUMN_NAME
from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE l
inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS t on l.constraint_Name = t.CONSTRAINT_NAME
where
l.table_schema = #schema
CREATE TABLE #groupBy2(
TABLE_NAME nvarchar(50),
groupby nvarchar(200)
)
INSERT INTO #groupBy2
SELECT TABLE_NAME, STRING_AGG(CONVERT(NVARCHAR(max), COLUMN_NAME), ',') as groupby
FROM #primarykey
GROUP BY TABLE_NAME
DECLARE #currentTable NVARCHAR(MAX)=''
DECLARE #currentGroup NVARCHAR(MAX)=''
create table #work4(
TABLE_NAME nvarchar(400),
COUNT int)
DECLARE #final NVARCHAR(MAX)=
'INSERT INTO #work4
SELECT '+#currentTable+', COUNT(*) FROM '+#currentTable+'GROUP BY'+#currentGroup
WHILE (SELECT COUNT(*) FROM #groupby2)>0
BEGIN
SET #currentTable =(SELECT TOP 1 TABLE_NAME FROM #groupby2 ORDER BY TABLE_NAME)
SET #currentGroup =(SELECT TOP 1 groupby FROM #groupby2 ORDER BY TABLE_NAME)
exec #final
DELETE #groupby2 where TABLE_NAME =#currentTable
END
END
This code gives me an error:
Incorrect syntax near 'SELECT'
but doesn't give me the actual line it has the error on.
Your primary issue was syntax errors: parameter declaration should not have = between name and type name, and missing spaces in the dynamic SQL.
Also
A schema name (or any object) can be up to nvarchar(128), you can use the alias sysname
You don't need to do any loops or use temp tables, you can build one big dynamic statement to execute
CREATE procedure loopcheck
#schema sysname
AS
DECLARE #sql nvarchar(max) = (
SELECT STRING_AGG(CAST('
SELECT
TableName = ' + QUOTENAME(t.name, '''') + ',
IndexName = ' + QUOTENAME(i.name, '''') + ',
Duplicates = COUNT(*)
FROM (
SELECT 1 n
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' t
GROUP BY ' + cols.agg + '
HAVING COUNT(*) > 1
) t
'
AS nvarchar(max)), 'UNION ALL')
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
AND s.name = #schema
JOIN sys.indexes i ON i.object_id = t.object_id
AND i.is_unique = 1
CROSS APPLY (
SELECT STRING_AGG('t.' + QUOTENAME(c.name), ', ')
FROM sys.index_columns ic
JOIN sys.columns c ON c.column_id = ic.column_id AND c.object_id = t.object_id
WHERE ic.index_id = i.index_id
AND ic.object_id = i.object_id
AND ic.is_included_column = 0
) cols(agg)
);
PRINT #sql; -- for testing
EXEC sp_executesql #sql;
db<>fiddle
I feel like there might be a slightly more efficient method using GROUPING SETS in cases where there are multiple unique indexes/constraints on a single table, but I'll leave that to you.

Incorrect placement of SELECT in a cursor causes countless amount of tables pop up

I've been busy with coding a stored procedure which contains two temporary tables and a cursor. It's been two days since I've received this assignment and it's giving me a hard time, for it's the first time I'm busy with coding an SP of such complexity.
The cursor is supposed to derive data from DATABASE_X, and lets that data being fetched for making comparisons with DATABASE_Y.
The TableInfo table in DATABASE_Y is to contain all, or most of the schemas and tables that are in DATABASE_X. The similar thing goes for the ColumnInfo table as well, with the only difference from the TableInfo table being able to contain the column data as well.
The temporary tables, which respectively contain the data of the non-existing tables in DATABASE_Y.TableInfo and the non-existing columns in DATABASE_Y.ColumnInfo are meant to be filled after the comparisons. (non-existing means that the table A exists in DBX but not in the rows of DBY.TableInfo, and vice versa for the case of columns)
The NotInUse column that exists in DATABASE_Y.TableInfo is a determinant for whether a table in DBX should be considered to be examined.
CREATE TABLE #NONEXISTENT_TABLES(
SCHEMA_NAME VARCHAR(100),
TABLE_NAME VARCHAR(100)
)
CREATE TABLE #NONEXISTENT_COLUMNS(
SCHEMA_NAME VARCHAR(100),
TABLE_NAME VARCHAR(100),
COLUMN_NAME VARCHAR(100)
)
DECLARE #SchemaName VARCHAR(100)
DECLARE #TableName VARCHAR(100)
DECLARE #ColumnName VARCHAR(100)
USE DATABASE_X;
DECLARE CRS_GET_NONEXISTENT_STUFF CURSOR FOR
select s.name as 'sname', t.name as 'tname', c.name as 'cname'
from sys.schemas (nolock) s
join sys.tables (nolock) t
on s.schema_id = t.schema_id
join sys.columns (nolock) c
on c.object_id = t.object_id
order by 1,2,3
OPEN CRS_GET_NONEXISTENT_STUFF
FETCH NEXT FROM CRS_GET_NONEXISTENT_STUFF INTO #SchemaName,
#TableName,
#ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
select #SchemaName, #TableName
from DATABASE_Y..TableInfo (nolock) ti
print #SchemaName + '-' + #TableName
IF ##ROWCOUNT = 1
BEGIN
declare #NotInUse varchar(100)
select #NotInUse = ti.[NotInUse]
from DATABASE_Y..TableInfo (nolock) ti
where ti.[Schema] = #SchemaName
and ti.[Name] = #TableName
print #SchemaName + '-' + #TableName
IF #NotInUse = '0'
DECLARE #colname varchar(100)
BEGIN
select #colname = ci.[Name]
from DATABASE_Y..ColumnInfo (nolock) ci
where ci.[TableSchema] = #SchemaName
and ci.[TableName] = #TableName
and ci.[Name] = #ColumnName
print #SchemaName + '-' + #TableName + '-' + #ColumnName
IF #colname IS NULL
BEGIN
INSERT INTO #NONEXISTENT_COLUMNS(SEMA_ADI, TABLO_ADI, KOLON_ADI)
VALUES(#SchemaName, #TableName, #colname)
END
END
END
ELSE
INSERT INTO #NONEXISTENT_TABLES(SCHEMA_NAME, TABLE_NAME)
VALUES (#SchemaName, #TableName)
FETCH NEXT FROM CRS_GET_NONEXISTENT_STUFF INTO #SchemaName,
#TableName,
#ColumnName
END
CLOSE CRS_GET_NONEXISTENT_STUFF
DEALLOCATE CRS_GET_NONEXISTENT_STUFF
SELECT * FROM #NONEXISTENT_COLUMNS
SELECT * FROM #NONEXISTENT_TABLES
DROP TABLE #NONEXISTENT_COLUMNS
DROP TABLE #NONEXISTENT_TABLES
Assuming that:
*The first schema on DBX is AAA and the first table on DBX of AAA is BBBBB
*The second table of AAA is CCCCC;
I receive a countless amount of tables as the result of a hardly-looks-like-it-would-end-quickly query, having only AAA-BBBBB displayed in 5-6 tables with around 5000 rows, then moving onto AAA-CCCCC doing what happened above, and it goes on and on.
I believe that my mistake was to put the SELECT commands under that WHILE loop, but I also believe that it wasn't my only mistake...
I'd appreciate having the advices of all of you about this issue.
I apologize for the mere wall of text.
First thing I would suggest is to read this article - Bad habits : Putting NOLOCK everywhere.
The next thing is that for each loop you run this:
select #SchemaName, #TableName
from DATABASE_Y..TableInfo (nolock) ti;
print #SchemaName + '-' + #TableName;
IF ##ROWCOUNT = 1
....
I have added line breaks and statement terminators for clarity, but the first select is why you are getting loads of results sets. This does nothing and will run for every column in your database. Also where you have IF ##ROWCOUNT = 1 it will always return 0 because it follows a print command, which returns no rows. So you will never enter the "true" section of this IF/ELSE block.
I am quite certain that you don't need a cursor at all, and as a general rule unless you absolutely have to use a cursor you should not use one. So I think you can simply replace all your loops with two set based inserts:
CREATE TABLE #NONEXISTENT_TABLES(
SCHEMA_NAME VARCHAR(100),
TABLE_NAME VARCHAR(100),
object_id INT NOT NULL
);
CREATE TABLE #NONEXISTENT_COLUMNS(
SCHEMA_NAME VARCHAR(100),
TABLE_NAME VARCHAR(100),
COLUMN_NAME VARCHAR(100)
);
INSERT #NONEXISTENT_TABLES (SCHEMA_NAME, TABLE_NAME, objecobject_idt_ID)
SELECT s.name, t.Name, object_id
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
WHERE NOT EXISTS
( SELECT 1
FROM DATABASE_Y..TableInfo AS ti
WHERE ti.TableSchema = s.Name
AND ti.name = t.name
--AND ti.NotInUse = 0
);
INSERT #NONEXISTENT_COLUMNS (SCHEMA_NAME, TABLE_NAME, COLUMN_NAME)
SELECT nt.SCHEMA_NAME, nt.TABLE_NAME, c.name
FROM sys.columns AS c
INNER JOIN #NONEXISTENT_TABLES AS nt
ON nt.object_id = c.object_id);

How do I create a select statement to return distinct values, column name and table name?

I would like to create a SQL Statement that will return the distinct values of the Code fields in my database, along with the name of the column for the codes and the name of the table on which the column occurs.
I had something like this:
select c.name as 'Col Name', t.name as "Table Name'
from sys.columns c, sys tables t
where c.object_id = t.object_id
and c.name like 'CD_%'
It generates the list of columns and tables I want, but obviously doesn't return any of the values for each of the codes in the list.
There are over 100 tables in my database. I could use the above result set and write the query for each one like this:
Select distinct CD_RACE from PERSON
and it will return the values, but it won't return the column and table name, plus I have to do each one individually. Is there any way I can get the value, column name and table name for EACH code in my database?
Any ideas? THanks...
Just generate your selects and bring in the column and table names as static values. Here's an Oracle version:
select 'select distinct '''||c.column_name||''' as "Col Name", '''||t.table_name||''' as "Table Name", '||c.column_name||' from '||t.table_name||';'
from all_tab_columns c, all_tables t
where c.table_name = t.table_name;
This will give you a bunch of separate statements, you can modify the query a bit to put a union between each select if you really want one uber query you can execute to get all your code values at once.
Here's an approach for SQL Server since someone else covered Oracle (and specific DBMS not mentioned. The following steps are completed:
Setup table to receive the schema, table, column name, and column value (in example below only table variable is used)
Build the list of SQL commands to execute (accounting for various schemas and names with spaces and such)
Run each command dynamically inserting values into the setup table from #1 above
Output results from table
Here is the example:
-- Store the values and source of the values
DECLARE #Values TABLE (
SchemaName VARCHAR(500),
TableName VARCHAR(500),
ColumnName VARCHAR(500),
ColumnValue VARCHAR(MAX)
)
-- Build list of SQL Commands to run
DECLARE #Commands TABLE (
Id INT PRIMARY KEY NOT NULL IDENTITY(1,1),
SchemaName VARCHAR(500),
TableName VARCHAR(500),
ColumnName VARCHAR(500),
SqlCommand VARCHAR(1000)
)
INSERT #Commands
SELECT
[TABLE_SCHEMA],
[TABLE_NAME],
[COLUMN_NAME],
'SELECT DISTINCT '
+ '''' + [TABLE_SCHEMA] + ''', '
+ '''' + [TABLE_NAME] + ''', '
+ '''' + [COLUMN_NAME] + ''', '
+ '[' + [COLUMN_NAME] + '] '
+ 'FROM [' + [TABLE_SCHEMA] + '].[' + [TABLE_NAME] + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE 'CD_%'
-- Loop through commands
DECLARE
#Sql VARCHAR(1000),
#Id INT,
#SchemaName VARCHAR(500),
#TableName VARCHAR(500),
#ColumnName VARCHAR(500)
WHILE EXISTS (SELECT * FROM #Commands) BEGIN
-- Get next set of records
SELECT TOP 1
#Id = Id,
#Sql = SqlCommand,
#SchemaName = SchemaName,
#TableName = TableName,
#ColumnName = ColumnName
FROM #Commands
-- Add values for that command
INSERT #Values
EXEC (#Sql)
-- Remove command record
DELETE #Commands WHERE Id = #Id
END
-- Return the values and sources
SELECT * FROM #Values

Search SQL DB's For A Specific Word

I am completely new to SQL and have no experience what so ever in it so please bear with me with this question.
I need to know if it is possible to search a SQL database for a specific word and if so how?
We are currently going through a rebranding project and I need to look in our CMS (Content Management System) database for all reference to an email address. All I need to search for is:
.co.uk
Below is a screenshot of the database in question with all the containing tables, I just cant get me head around SQL and I have had no joy on Google trying to find the answer.
I need to search everything in this database but I don't know what tables, views, column names etc the content sits in as it's all spread across them all.
There are other tables I need to search but hopefully an answer will be provided which I can modify to search these.
DB's aren't really meant for such vague search descriptions, you should have some definition or model or requirement specs to describe where values like that could exist.
But of course, you could opt for an insanely slow method of doing it by using dynamic SQL.
I made this right fast and just tested it fast, but it should work:
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#SEARCHTABLE') IS NOT NULL
DROP TABLE #SEARCHTABLE
IF OBJECT_ID('tempdb..#RESULTS') IS NOT NULL
DROP TABLE #RESULTS
CREATE TABLE #SEARCHTABLE (ROWNUM INT IDENTITY(1,1), SEARCHCLAUSE VARCHAR(2000) COLLATE DATABASE_DEFAULT)
INSERT INTO #SEARCHTABLE (SEARCHCLAUSE)
SELECT 'SELECT TOP 1 '''+TAB.name+''', '''+C.name+'''
FROM ['+S.name+'].['+TAB.name+']
WHERE '
+CASE WHEN T.name <> 'xml'
THEN '['+C.name+'] LIKE ''%.co.uk%'' AND ['+C.name+'] LIKE ''%#%'''
ELSE 'CAST(['+C.name+'] AS VARCHAR(MAX)) LIKE ''%.co.uk%'' AND CAST(['+C.name+'] AS VARCHAR(MAX)) LIKE ''%#%'''
END AS SEARCHCLAUSE
FROM sys.tables TAB
JOIN sys.schemas S on S.schema_id = TAB.schema_id
JOIN sys.columns C on C.object_id = TAB.object_id
JOIN sys.types T on T.user_type_id = C.user_type_id
WHERE TAB.type_desc = 'USER_TABLE'
AND (T.name LIKE '%char%' OR
T.name LIKE '%xml%')
AND CASE WHEN C.max_length = -1 THEN 10 ELSE C.max_length END >= 6 -- To only search through sufficiently long column
CREATE TABLE #RESULTS (ROWNUM INT IDENTITY(1,1), TABLENAME VARCHAR(256) COLLATE DATABASE_DEFAULT, COLNAME VARCHAR(256) COLLATE DATABASE_DEFAULT)
DECLARE #ROWNUM_NOW INT, #ROWNUM_MAX INT, #SQLCMD VARCHAR(2000), #STATUSSTRING VARCHAR(256)
SELECT #ROWNUM_NOW = MIN(ROWNUM), #ROWNUM_MAX = MAX(ROWNUM) FROM #SEARCHTABLE
WHILE #ROWNUM_NOW <= #ROWNUM_MAX
BEGIN
SELECT #SQLCMD = SEARCHCLAUSE FROM #SEARCHTABLE WHERE ROWNUM = #ROWNUM_NOW
INSERT INTO #RESULTS
EXEC(#SQLCMD)
SET #STATUSSTRING = CAST(#ROWNUM_NOW AS VARCHAR(25))+'/'+CAST(#ROWNUM_MAX AS VARCHAR(25))+', time: '+CONVERT(VARCHAR, GETDATE(), 120)
RAISERROR(#STATUSSTRING, 10, 1) WITH NOWAIT
SELECT #ROWNUM_NOW = #ROWNUM_NOW + 1
END
SET NOCOUNT ON
SELECT 'This table and column contains strings ".co.uk" and a "#"' INFORMATION, TABLENAME, COLNAME FROM #RESULTS
-- Uncomment to drop the created temp tables
--IF OBJECT_ID('tempdb..#SEARCHTABLE') IS NOT NULL
-- DROP TABLE #TABLECOLS
--IF OBJECT_ID('tempdb..#RESULTS') IS NOT NULL
-- DROP TABLE #RESULTS
What it does, it search the DB for all user-created tables with their schemas, which have (n)char/(n)varchar/xml columns of a sufficient length, and search each of them one by one until at least one match is found, then it moves to the next one on the list. Match is defined as any string or XML cast as string, which contains the text ".co.uk" and an "#"-sign somewhere in there.
It will show the progress of the script (how many searchable TABLE.COLUMN combinations are have been found and which one on that list is currently running, as well as the current timestamps down to seconds) on the messages tab. When ready, it will show you all the tables and column names that contained at least one match.
So from that list, you'll have to search through the tables and columns manually to find exactly how many and what kinds of matches there are, and what it is you actually want to do.
Edit: Again I disregarded using sysnames for sysobjects, but I'll modify later if needed.
I threw together a quick query that seems to work for me:
--Search for a word in the current database
SET NOCOUNT ON;
--First make a hit list of possible tables/ columns
DECLARE #HitList TABLE (
Id INT IDENTITY(1,1) PRIMARY KEY,
TableName VARCHAR(255),
SchemaName VARCHAR(255),
ColumnName VARCHAR(255));
INSERT INTO
#HitList (
TableName,
SchemaName,
ColumnName)
SELECT
t.name,
s.name,
c.name
FROM
sys.tables t
INNER JOIN sys.columns c ON c.object_id = t.object_id
INNER JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE
c.system_type_id = 167;
--Construct Dynamic SQL
DECLARE #Id INT = 1;
DECLARE #Count INT;
SELECT #Count = COUNT(*) FROM #HitList;
DECLARE #DynamicSQL VARCHAR(1024);
WHILE #Id <= #Count
BEGIN
DECLARE #TableName VARCHAR(255);
DECLARE #SchemaName VARCHAR(255);
DECLARE #ColumnName VARCHAR(255);
SELECT #TableName = TableName FROM #HitList WHERE Id = #Id;
SELECT #SchemaName = SchemaName FROM #HitList WHERE Id = #Id;
SELECT #ColumnName = ColumnName FROM #HitList WHERE Id = #Id;
SELECT #DynamicSQL = 'SELECT * FROM [' + #SchemaName + '].[' + #TableName + '] WHERE [' + #ColumnName + '] LIKE ''%co.uk%''';
--PRINT #DynamicSQL;
EXECUTE (#DynamicSQL);
IF ##ROWCOUNT != 0
BEGIN
PRINT 'We have a hit in ' + #TableName + '.' + #ColumnName + '!!';
END;
SELECT #Id = #Id + 1;
END;
Basically it makes a list of any VARCHAR columns (you might need to change this to include NVARCHARs if you have Unicode text columns - just change the test for system type id from 167 to 231) then performs a search for each one. When you run this from management studio switch to the messages pane to see the hits and just ignore the results.
It will be slow if your database is any sort of size... but then that is to be expected?