search a string in a table without knowing the column - sql-server

Note: this is NOT asking
how to select a string where the column name is known.
how to select a string in ALL tables (all google results relate to this one)
This is asking search in only ONE table.
SQL returns error info conversion failed when converting the nvarchar value S3N2V5.
I want to locate the column name where S3N2V5 exists.
No manual methods please. There are 1000000 columns.
Input S3N2V5
Output columnname1ofthistable

Assuming I understand the question, here is one way to get a list of all columns from a single table that contain the search value, using CASE:
Create and populate sample table (Please save us this step in your future questions)
CREATE TABLE T
(
COL1 char(3),
COL2 char(3),
COL3 char(3),
COL4 int
)
INSERT INTO T VALUES
('abc', 'def', 'nop', 1),
('klm', 'nop', 'qrs', 2),
('tuv', 'wzy', 'zab', 3)
Build your dynamic sql:
DECLARE #Search nvarchar(5) = 'nop'
DECLARE #SQL nvarchar(max) = 'SELECT CASE #Search'
SELECT #SQL = #SQL +' WHEN '+ COLUMN_NAME + ' THEN '''+ COLUMN_NAME +''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'T'
AND LOWER(DATA_TYPE) LIKE '%char%' -- only search char, varchar, nchar and nvarchar columns
SELECT #SQL = 'SELECT ColumnName FROM (' +
#SQL + ' END As ColumnName FROM T) x WHERE ColumnName IS NOT NULL'
Execute: (Note that using sp_executeSQL is SQL Injection safe, since we do not concatenate the search parameter into the query, but using it as a parameter)
EXEC sp_executeSQL #SQL, N'#Search nvarchar(5)', #Search
Results:
ColumnName
COL3
COL2

DECLARE #MyValue NVarChar(4000) = 'searchstring';
SELECT S.name SchemaName, T.name TableName
INTO #T
FROM sys.schemas S INNER JOIN
sys.tables T ON S.schema_id = T.schema_id;
WHILE (EXISTS (SELECT * FROM #T)) BEGIN
DECLARE #SQL NVarChar(4000) = 'SELECT * FROM $$TableName WHERE (0 = 1) ';
DECLARE #TableName NVarChar(1000) = (
SELECT TOP 1 SchemaName + '.' + TableName FROM #T
);
SELECT #SQL = REPLACE(#SQL, '$$TableName', #TableName);
DECLARE #Cols NVarChar(4000) = '';
SELECT
#Cols = COALESCE(#Cols + 'OR CONVERT(NVarChar(4000), ', '') + C.name + ') = CONVERT(NVarChar(4000), ''$$MyValue'') '
FROM sys.columns C
WHERE C.object_id = OBJECT_ID(#TableName);
SELECT #Cols = REPLACE(#Cols, '$$MyValue', #MyValue);
SELECT #SQL = #SQL + #Cols;
select substring(#SQL,charindex('.',#SQL)+1,charindex('(',#SQL)-charindex('.',#SQL)-8) as 'TableName'
EXECUTE(#SQL);
DELETE FROM #T
WHERE SchemaName + '.' + TableName = #TableName;
END;
DROP TABLE #T;
This will give you table Name and the entire row from the table which contains the searchstring.

Apart from anwswers mentioned in post : Older Post
1) (using column name) SELECT table_name,table_schema FROM INFORMATION_SCHEMA.COLUMNS WHERE column_name='sort_method';
I hope better you can take dump ( in.sql format ) and you can easily search the content using IDEs like N++.

Related

How to search for a value in a common column across multiple tables in a SQL Server database?

I have almost 1000 tables and most of them have a common column ItemNumber. How do I search across all the tables in the database for a value or list of values that exist in this common column, such as 350 or (350, 465)? The tables have different schemas.
Table A100
ItemNumber
Detail
230
Car
245
Plane
Table A1000
ItemNumber
ProductDescription
350
Pie
465
Cherry
This does not perform type checking, so you can get conversion errors if the target column is not the correct type. Also, this script uses LIKE, you would probably need to change that to a direct comparison.
SET NOCOUNT ON
DECLARE #ID NVARCHAR(100) = '2'
DECLARE #ColumnName NVARCHAR(100) ='UserID'
DECLARE #Sql NVARCHAR(MAX)=N'CREATE TABLE #TempResults(TableName NVARCHAR(50), ColumnName NVARCHAR(50), ItemCount INT)'
SELECT
#Sql = #Sql + N'INSERT INTO #TempResults SELECT * FROM (SELECT '''+ST.Name+''' AS TableName, '''+C.Name+''' AS ColumnName, COUNT(*) AS ItemCount FROM '+ST.Name+' WHERE '+C.Name+'='+#ID+') AS X WHERE ItemCount > 0 '
FROM
sys.columns C
INNER JOIN sys.tables ST ON C.object_id = ST.object_id
WHERE
C.Name LIKE '%'+#ColumnName+'%'
SET #Sql = #Sql + N'SELECT * FROM #TempResults'
exec sp_executesql #sql
You need to do this with dynamic SQL. You will need to query all 1000 tables, and make sure you are converting the values correctly if the columsn are different types.
You don't need a temp table for this, you can just script one giant UNION ALL query. You must make sure to quote all dynamic names correctly using QUOTENAME.
To be able to return data for multiple items, you should create a Table Valued Parameter, which you can pass in using sp_executesql.
First create a table type
CREATE TYPE dbo.IntList (Id int PRIMARY KEY);
Then you create a table variable containing them, and pass it in. You can also do this in a client application and pass in a TVP.
SET NOCOUNT ON;
DECLARE #Items dbo.IntList;
INSERT #Items (Id) VALUES(350),(465);
DECLARE #Sql nvarchar(max);
SELECT
#Sql = STRING_AGG(CONVERT(nvarchar(max), N'
SELECT
' + QUOTENAME(t.name, '''') + ' AS TableName,
t.ItemNumber,
COUNT(*) AS ItemCount
FROM ' + QUOTENAME(t.Name) + ' t
JOIN #items i ON i.Id = t.ItemNumber
GROUP BY
t.ItemNumber
HAVING COUNT(*) > 0
' ),
N'
UNION ALL
' )
FROM
sys.tables t
WHERE t.object_id IN (
SELECT c.object_id
FROM sys.columns c
WHERE
c.Name = 'ItemNumber'
);
PRINT #sql; -- your friend
EXEC sp_executesql
#sql,
N'#items dbo.IntList',
#items = #items READONLY;
If you don't need to know the count, and only want to know if a value exists, you can change the dynamic SQL to an EXISTS
....
SELECT
#Sql = STRING_AGG(CONVERT(nvarchar(max), N'
SELECT
' + QUOTENAME(t.name, '''') + ' AS TableName,
t.ItemNumber
FROM #items i
WHERE i.Id IN (
SELECT t.ItemNumber
FROM ' + QUOTENAME(t.Name) + ' t
)
' ),
N'
UNION ALL
' )
....

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.

How do I validate a variable against multple database tables

Does anyone know how to check a a variable against all database table with columns storing the same type of information? I have a poorly designed database that stores ssn in over 60 tables within one database. some of the variations of columns in the various tables include:
app_ssn
ca_ssn
cand_ssn
crl_ssn
cu_ssn
emtaddr_ssn
re_ssn
sfcart_ssn
sfordr_ssn
socsecno
ssn
Ssn
SSN
I want to create a stored procedure that will accept a value and check it against every table that has 'ssn' in the name.Does anyone have idea as to how to do this?
-- I assume that table/column names don't need to be surrounded by square braces. You may want to save matches in a table - I just select them. I also assume ssn is a char.
alter proc proc1
#search1 varchar(500)
as
begin
set nocount on
declare #strsql varchar(500)
declare #curtable sysname
declare #prevtable sysname
declare #column sysname
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
order by table_schema+'.'+table_name +column_name
-- make sure that at least one column has ssn in the column name
if #curtable is not null
begin
while (1=1)
begin
set #strsql = 'select * from ' +#curtable +' where '+''''+#search1+''''+ ' = '+#column
print #strsql
-- any matches for passed in ssn will match here...
exec (#strsql)
set #prevtable = #curtable+#column
select top 1 #curtable= table_schema+'.'+table_name, #column=column_name
from INFORMATION_SCHEMA.COLUMNS
where CHARINDEX('ssn',column_name) > 0
and table_schema+'.'+table_name +column_name> #prevtable
order by table_schema+'.'+table_name +column_name
-- when we run out of columns that contain ssn we are done...
if ##ROWCOUNT = 0
break
end
end
end
What you will need to do is some research. But here is where you can start;
SELECT tbl.NAME AS TableName
,cl.NAME AS ColumnName
,IDENTITY(INT, 1, 1) AS ID
INTO #ColumnsToLoop
FROM sys.tables tbl
JOIN sys.columns cl ON cl.object_id = tbl.object_id
This will give you the table / column relation then you can simply build a dynamic SQL string based on each row in the query above (basically loop it) and use EXEC or sp_execsql. So basically;
DECLARE #Loop int = (select min(ID) From #ColumnsToLoop),#MX int = (Select MAX(ID) From #ColumnsToLoop)
WHILE(#Loop<=#MX)
BEGIN
DECLARE #SQL nvarchar(MAX) = 'SQL String'
//Construct the dynamic SQL String
EXEC(#SQL);
SET #Loop += 1
END
Perhaps I went a little too crazy with this one, but let me know. I thought it would best the primary key of the search results with the table name so you could join it to your tables. I also managed to do it without a single cursor or loop.
DECLARE #SSN VARCHAR(25) = '%99%',
#SQL VARCHAR(MAX);
WITH CTE_PrimaryKeys
AS
(
SELECT TABLE_CATALOG,
TABLE_SCHEMA,
TABLE_NAME,
column_name
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE D
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
),
CTE_Columns
AS
(
SELECT A.*,
CONCAT(A.TABLE_CATALOG,'.',A.TABLE_SCHEMA,'.',A.TABLE_NAME) AS FullTableName,
CASE WHEN B.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IsPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS A
LEFT JOIN CTE_PrimaryKeys B
ON A.TABLE_CATALOG = B.TABLE_CATALOG
AND A.TABLE_SCHEMA = B.TABLE_SCHEMA
AND A.TABLE_NAME = B.TABLE_NAME
AND A.COLUMN_NAME = B.COLUMN_NAME
),
CTE_Select
AS
(
SELECT
'SELECT ' +
--This returns the pk_col casted as Varchar and the table name in another columns
STUFF((SELECT ',CAST(' + COLUMN_NAME + ' AS VARCHAR(MAX)) AS pk_col,''' + B.TABLE_NAME + ''' AS Table_Name'
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
AND B.IsPrimaryKey = 1
FOR XML PATH ('')),1,1,'')
+ ' FROM ' + fullTableName +
--This is where I list the columns where LIKE desired SSN
' WHERE ' +
STUFF((SELECT COLUMN_NAME + ' LIKE ''' + #SSN + ''' OR '
FROM CTE_Columns B
WHERE A.Table_Name = B.TABLE_NAME
--This is where I filter so I only get desired columns
AND (
--Uncomment the Collate if your database is case sensitive
COLUMN_NAME /*COLLATE SQL_Latin1_General_CP1_CI_AS*/ LIKE '%ssn%'
--list your column Names that don't have ssn in them
--OR COLUMN_NAME IN ('col1','col2')
)
FOR XML PATH ('')),1,0,'') AS Selects
FROM CTE_Columns A
GROUP BY A.FullTableName,A.TABLE_NAME
)
--Unioning them all together and getting rid of last trailing "OR "
SELECT #SQL = COALESCE(#sql,'') + SUBSTRING(selects,1,LEN(selects) - 3) + ' UNION ALL ' + CHAR(13) --new line for easier debugging
FROM CTE_Select
WHERE selects IS NOT NULL
--Look at your code
SELECT SUBSTRING(#sql,1,LEN(#sql) - 11)

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