This is sample data table.
I want to select values in any rows or any column (equals) = 200 with column names.But we don't know column names.
If you know the table name it's possible to interrogate the INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS, with SQLServer 2005 or better, or sysobjects and syscolumns with SQLServer 2000 to retrieve the table columns, after that you can create a fully referenced query for your needs
I think the below T-SQL will get you what you want. It was written against AdventureWorks2012LT. In the future, you can get more specific help by including the SQL create statements with your question (so the responder doesn't have to recreate the tables)
(BTW, My example is looking for any field that contains the letter 'S')
DECLARE #column_name nvarchar(200);
DECLARE #statement nvarchar(max);
DECLARE #results TABLE(
id int,
colname nvarchar(200),
value nvarchar(max)
)
DECLARE col_cursor CURSOR FOR
SELECT C.COLUMN_NAME AS col
FROM INFORMATION_SCHEMA.COLUMNS C WHERE C.TABLE_NAME LIKE 'Address'
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #column_name
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #column_name
SELECT #statement = N'SELECT AddressID, ''' + #column_name + ''' AS ColName, ' + #column_name + ' AS value FROM SalesLT.[Address] WHERE ' + #column_name + ' LIKE ''%S%''';
INSERT INTO #results
EXEC(#statement);
FETCH NEXT FROM col_cursor INTO #column_name
END
CLOSE col_cursor
DEALLOCATE col_cursor
SELECT * FROM #results
Related
I need to find all columns that have 5 or more distinct values. Now my query is like:
SELECT TABLE_NAME,COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'MY_SCHEMA'
AND TABLE_NAME IN ('TABLE_1', 'TABLE_2', 'TABLE_3')
I thought it could be done like simple subquery. Something like:
*code above*
AND (select count(distinct COLUMN_NAME) FROM TABLE_SCHEMA + TABLE_NAME) > 5
I just recently started to learn SQL and thought this kind of thing is easy, but still I can't figure out right query.
With help of Stu's answer and this answer I was able to make workable solution.
declare #RowsToProcess int
declare #CurrentRow int
declare #SelectCol nvarchar(max)
declare #SelectTable nvarchar(max)
declare #tablesAndColumns table(RowID int not null primary key identity(1,1), table_name nvarchar(max), column_name nvarchar(max)
insert into #tablesAndColumns
select TABLE_NAME,COLUMN_NAME,DATA_TYPE
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'my schema'
and TABLE_NAME in ('myTable', 'myTable2' ,'myTable3')
set #RowsToProcess=##ROWCOUNT
set #CurrentRow=0
while #CurrentRow<#RowsToProcess
begin
set #CurrentRow=#CurrentRow+1
select
#SelectCol=column_name,
#SelectTable=table_name
from #tablesAndColumns
where RowID=#CurrentRow
declare #QRY NVARCHAR(MAX)
set #QRY = ' insert into [my_schema].[result_table] (table_name,column_name,distinct_values)
SELECT ' + '''' +#SelectTable+ '''' + ', ' + '''' +#SelectCol+ '''' + ', count(*) as cnt
FROM (SELECT DISTINCT ' +#SelectCol+ ' FROM my_schema.'+ #SelectTable+') as a'
exec SP_EXECUTESQL #QRY
end
I'd like to propose another way. You can run through all the column and table names by using a CURSOR. That way you don't need to store them beforehand and can directly access them in your loop while also having a while condition.
Also I went with sys.tables and sys.columns since I noticed that INFORMATION_SCHEMA also contains views and sys.tables can be filtered for the table's type.
I added a "HAVING COUNT(*) >= 5" into the dynamic SQL so I don't save those informations in the first place rather than filtering them later.
Finally I went with "(NOLOCK)" because you only try to acces the tables for reading and that way you don't lock them for other users / interactions.
(The #i and #max are just for tracking the progress since I ran the query on ~10k columns and just wanted to see how far it is.)
Hopefully might be helpful aswell although you seem to have solved your problem.
DECLARE #columnName nvarchar(100),
#tableName nvarchar(100),
#sql nvarchar(MAX),
#i int = 0,
#max int = (SELECT COUNT(*)
FROM sys.tables T
INNER JOIN sys.columns C ON T.object_id = C.object_id
WHERE T.[type] = 'U')
DROP TABLE IF EXISTS #resultTable
CREATE TABLE #resultTable (ColumnName nvarchar(100), TableName nvarchar(100), ResultCount int)
DECLARE db_cursor CURSOR FOR
SELECT C.[name], T.[name]
FROM sys.tables T
INNER JOIN sys.columns C ON T.object_id = C.object_id
WHERE T.[type] = 'U'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #columnName, #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = CONCAT(' INSERT INTO #resultTable (ColumnName, TableName, ResultCount)
SELECT ''', #columnName, ''', ''', #tableName, ''', COUNT(*)
FROM (
SELECT DISTINCT [', #columnName, ']
FROM [', #tableName, '] (NOLOCK)
WHERE [', #columnName, '] IS NOT NULL
) t
HAVING COUNT(*) >= 5')
EXEC sp_executesql #sql
SET #i = #i + 1
PRINT CONCAT(#i, ' / ', #max)
FETCH NEXT FROM db_cursor INTO #columnName, #tableName
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT *
FROM #resultTable
I have multiple databases with the same table (an Eventlog with different values). The names of these databases are subject to change. I am trying to display the Eventlog tables in one consolidated table with the corresponding database name.
I tried to using cursor and dynamic SQL statement to achieve this with no luck. As well, I'm not sure if that is the best approach. Would love some help!
-- Create a new table variable to record all the database name
DECLARE #Database_Table table ([TimeStamp] nvarchar(500)
,[EventIDNo] nvarchar(100)
,[EventDesc] nvarchar(1000))
--Create variable for database name and query variable
DECLARE #DB_Name VARCHAR(100) -- database name
DECLARE #query NVARCHAR(1000) -- query variable
--Declare the cursor
DECLARE db_cursor CURSOR FOR
-- Populate the cursor with the selected database name
SELECT name
FROM sys.databases
WHERE name NOT IN ('master','model','msdb','tempdb')
--Open the cursor
OPEN db_cursor
--Moves the cursor to the first point and put that in variable name
FETCH NEXT FROM db_cursor INTO #DB_Name
-- while loop to go through all the DB selected
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = N'INSERT INTO #Database_Table
SELECT #DB_Name, *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'
EXEC (#query)
--Fetch the next record from the cursor
FETCH NEXT FROM db_cursor INTO #DB_Name
END
--Close and deallocate cursor
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT *
FROM #Database_Table
If you need a single resultset and all tables have the same layout, this should work:
DECLARE #sql nvarchar(4000) =
(SELECT STRING_AGG(CONCAT(
'SELECT ''',
QUOTENAME(name),
''',
* FROM ',
QUOTENAME(name),
'..Table ',
CHAR(10)
), ' UNION ALL ' + CHAR(10))
FROM sys.databases);
SELECT #sql; -- for checking
EXEC(#sql);
If you're on compatibility level 130 or lower, you will have to use XML PATH(TYPE, '') to aggregate. I will leave that to you.
I notice a bug in this code:
SET #query = N'INSERT INTO #Database_Table
SELECT #DB_Name, *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'
In the SELECT clause you reference #DB_Name inside the string itself without concatenating the value as a variable. You did do this correctly in the FROM clause.
Something like:
SET #query = N'INSERT INTO #Database_Table
SELECT ''' + #DB_Name + ''', *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'
All database tables have a UserId field of [uniqueidentifier] type.
I need to query the entire database and get the list of tables that have UserId set to a specific value.
Right now I achieved this by using cursor and the results are horrible and are difficult to read. How can I improve this query to retrieve back a clear list with tables and count of record that have UserId set to a specific value, instead of using this:
DECLARE #TableName VARCHAR(127);
DECLARE #Value VARCHAR(512);
DECLARE #SqlCommand varchar(1000)
--Use cursor to loop through database tables that contain UserId column
DECLARE db_cursor CURSOR FOR
SELECT t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'UserId';
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
--Check if the next table has any UserId matching the where clause
EXEC('SELECT COUNT(UserId) , ''' + #TableName + ''' FROM ' + #TableName + ' WHERE UserId = ''FF13ACCA-022C-4296-AB3D-A35700E35BB3''');
FETCH NEXT FROM db_cursor INTO #TableName;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
You made all the difficult part, just put the value in a temp table and select them once you've finished.
DECLARE #TableName VARCHAR(127);
DECLARE #Value VARCHAR(512);
DECLARE #SqlCommand varchar(1000)
--Creta temp table
CREATE TABLE #Results (Number int, Tablename sysname)
--Use cursor to loop through database tables that contain UserId column
DECLARE db_cursor CURSOR FOR
SELECT t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'UserId';
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
--Check if the next table has any UserId matching the where clause
EXEC('INSERT INTO #Results (Number, ''' + #TableName + ''') SELECT COUNT(UserId) , ''' + #TableName + ''' FROM ' + #TableName + ' WHERE UserId = ''FF13ACCA-022C-4296-AB3D-A35700E35BB3''');
FETCH NEXT FROM db_cursor INTO #TableName;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
SELECT * FROM #Results
DROP TABLE #Results
I cannot test it but this should be the way
I have the sql server table with 51 columns like below
id
remarks1
remarks2
.
.
.
remarks50
I need to search if particular string is present in atleast one remarks field like in the example below
id remarks1 remarks2 remarks3 remarks4
1 key nonkey grabaze jjjjj
2 uuu 888 8888 kkk
3 888 key hjhj kjkj
suppose i need to search key which is present in either remarks1,2,3.....or 50
I can have sql like
select id from tbl where remarks1 ='key' or remarks2='key' and so on ..
writing or query upto 50 columns is really unpractical.. do we have any quick method?
You can try using below stored procedure .
CREATE PROCEDURE sp_FindStringInTable #stringToFind VARCHAR(100), #schema sysname, #table sysname
AS
DECLARE #sqlCommand VARCHAR(8000)
DECLARE #where VARCHAR(8000)
DECLARE #columnName sysname
DECLARE #cursor VARCHAR(8000)
BEGIN TRY
SET #sqlCommand = 'SELECT * FROM [' + #schema + '].[' + #table + '] WHERE'
SET #where = ''
SET #cursor = 'DECLARE col_cursor CURSOR FOR SELECT COLUMN_NAME
FROM ' + DB_NAME() + '.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ''' + #schema + '''
AND TABLE_NAME = ''' + #table + '''
AND DATA_TYPE IN (''char'',''nchar'',''ntext'',''nvarchar'',''text'',''varchar'')'
EXEC (#cursor)
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
IF #where <> ''
SET #where = #where + ' OR'
SET #where = #where + ' [' + #columnName + '] LIKE ''' + #stringToFind + ''''
FETCH NEXT FROM col_cursor INTO #columnName
END
CLOSE col_cursor
DEALLOCATE col_cursor
SET #sqlCommand = #sqlCommand + #where
--PRINT #sqlCommand
EXEC (#sqlCommand)
END TRY
BEGIN CATCH
PRINT 'There was an error. Check to make sure object exists.'
IF CURSOR_STATUS('variable', 'col_cursor') <> -3
BEGIN
CLOSE col_cursor
DEALLOCATE col_cursor
END
END CATCH
The stored procedure gets created in the master database so you can use it in any of your databases and it takes three parameters:
stringToFind - this is the string you are looking for. This could be a simple value as 'test' or you can also use the % wildcard such as '%test%', '%test' or 'test%'.
schema - this is the schema owner of the object
table - this is the table name you want to search, the procedure will search all char, nchar, ntext, nvarchar, text and varchar columns in the table
Source
You can use an unpivot to transpose the remarks* columns as rows with a common column name, which you can then filter on. (You'll need to repeat all 51 columns).
Distinct will be needed to eliminate cases where more than one column matches (i.e. to mimic the original or)
SELECT DISTINCT ID, Rmk
FROM
(SELECT ID, Remarks1, Remarks2, Remarks3, Remarks4
FROM Remarks) r
UNPIVOT
(Rmk FOR RmkCol IN (Remarks1, Remarks2, Remarks3, Remarks4))AS unpvt
WHERE rmk = 'key';
Sql Fiddle here
However I would advise you to reconsider normalising this into a 1 to many Remarks table - if your table is large, you will need a large number of indexes on the Remark* columns.
SELECT remarks1, remarks2, remarks3, remarks4 from tabl_name
WHERE CONTAINS (( remarks1, remarks2, remarks3, remarks4),'888') ORDER BY id;
BackDrop: We are researching why a number of accounts were missed in a process. We have went back to as far as we have data. We now have a rather large list of accounts that for whatever reason were missed. Now this process without going into too much detail is VERY VERY complex and we need to know why these accounts and only these accounts were missed. As any DataBase we have many many automated procedures that run all the time, so there is really at this point no telling what on earth happened to cause these accounts to get missed. My only bet I think at solving this is to find similarities between these accounts. Obviously we have tried looking at the more common places and have since found nothing.
Issue: I want to use SQL to return all the tablenames and columnnames in our database Where these list of accounts have the same value in a column or columns of a table. I have created a query to find tablenames, columns, and so forth but dont know how to bring it all together to create one query that will give me all the results I want. I am certain that a cursor will need to be used and lots of inner joining but I am just not sure how this should be done.
Again:
Lets say I have account numbers 123456 and 654321 and I know our DataBase has 3,000 tables with a column reference to account number with a name of either AccountNumber, AccountNum, or Account. I want to search and find all tables that have a column with the name AccountNumber, AccountNum, or Account that has a value of 123456 or 654321. Then with these tables, for each table I want to take the rows Where the column whether the name be AccountNumber, AccountNum, or Account has a value of either 123456 and 654321 and then for each of those rows I want to check each column of each row to see if the columns on a row for account number 123456 is eqaul to a column on a row for account number 654321 , if so then I want it to return the column name and the tablename. This way I can see what these accounts have in common.
ADVANCED PORTION:
IF some poor soul is able to do the above then I'd also like to create a query that will return
The tablename and when it was updated. I would get the updated value by checking each column in each table and if the column has a type of "timestamp" or a default value of "GetDate()" then that column would be used as updated. In final result set that shows were all changes have happened for those account nubmers it will order by updated.
A first approach, rustic (I'm not that used to T-SQL, I did more PL/SQL), but which may help you going further, AND TESTED IN SQL SERVER 2008. Hope it works in 2005...)
So, we create two procedures, one calling the other
The provided code can only check, in one time
- for 2 differents IDs
- for all concerned fields (Account, AccountNum, AccountNumber)
The idea (checking for AccountNumber column)
Find the tables (in table INFORMATION_SCHEMA.columns, which lists your database tables) which have a column with one of the 3 names provided
For every table found :
create a dynamic query :
select count(*) from <THE_TABLE> where <Account_column_name> IN (123456 654321);
If we have 2 results (mean that our Ids are both present in table), we launch the second procedure, with parameters : #TableName = <THE_TABLE>, #FieldName = <Account_column_name>, #FirstId = 123456, #SecondId = 654321
We find the column names for <THE_TABLE> (again in INFORMATION_SCHEMA.columns).
For every column found :
create a dynamic query
select count(*) from <THE_TABLE> as T1
inner join <THE_TABLE> as T2 on T1.<COLUMN_NAME> = T2.<COLUMN_NAME>
where T1.<Account_column_name>= 123456
and T2.<Account_column_name>= 654321
if count(*) = 1, it means that the same value exists in the same column of the same table for the given ids.
In that case, we print <THE_TABLE> and <THE_COLUMN>
To launch search, in sql management studio, just make
EXEC GetSimilarValuesForFieldAndValue 123456, 654321
and in the "Messages" part, you should have a list of "results".
CREATE procedure [dbo].[GetSimilarValuesForFieldAndValue](#FirstId int, #SecondId int)
AS
DECLARE #sql nvarchar(MAX);
DECLARE #params NVARCHAR(MAX);
DECLARE #Count int;
DECLARE #Name NVARCHAR(MAX);
DECLARE #FieldName NVARCHAR(MAX);
DECLARE db_cursor CURSOR for
select TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.columns
where COLUMN_NAME IN('Account', 'AccountNumber', 'AccountNum');
OPEN db_cursor
FETCH next from db_cursor into #Name, #FieldName
while ##FETCH_STATUS = 0
begin
select #sql =
N' SELECT #Count=Count(*) FROM ' + #Name +
N' WHERE ' +#FieldName+' IN (#FirstId,#SecondId)'
SELECT #params = N'#FieldName NVARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
EXEC sp_executesql #sql, #params, #FieldName, #FirstId, #SecondId, #Count OUT
if (#Count = 2)
begin
exec dbo.CompareFields #Name, #FieldName, #FirstId, #SecondId
end
FETCH NEXT FROM db_cursor INTO #Name, #FieldName
end
close db_cursor
DEALLOCATE db_cursor
GO
The second one :
CREATE procedure [dbo].[CompareFields](#TableName NVARCHAR(MAX), #FieldName NVARCHAR(MAX), #FirstId int, #SecondId int)
as
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Params NVARCHAR(MAX)
DECLARE #Count int
DECLARE cfCursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = ''+#TableName+' '
AND COLUMN_NAME <> ' '+#FieldName+''
OPEN cfCursor
FETCH next from cfCursor into #ColumnName
while ##FETCH_STATUS = 0
begin
select #Sql =
N' SELECT #Count = count(*) from ' +#TableName + ' T1 '+
N' INNER JOIN ' + #TableName + ' T2 ON T1.' +#ColumnName + ' = T2.' + #ColumnName +
N' WHERE T1.' +#FieldName + ' = '+ CAST(#FirstId as varchar) +
N' AND T2.' + #FieldName + ' = '+CAST(#SecondId as varchar)
SELECT #Params =
N'#TableName VARCHAR(MAX), #ColumnName VARCHAR(MAX), '+
N'#FieldName VARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
exec sp_executesql #sql, #Params, #TableName, #ColumnName, #FieldName, #FirstId, #SecondId, #Count OUT
if #Count = 1
begin
--print tableName and column Name if value is identic
print 'Table : ' + #TableName + ' : same value for ' + #ColumnName
end
FETCH NEXT FROM cfCursor INTO #ColumnName
end
close cfCursor
DEALLOCATE cfCursor
GO
I actually had to do this for Guids at one point. Here is the script for doing with Guids. One sec and I'll work on modifying it to suit your needs:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value INT
SET #value = '06B8BD6C-A8EC-4EB3-9562-6666EE86952D'
DECLARE table_cursor CURSOR
FOR select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where system_type_id = 36
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' +
#column + ''' AS COL FROM [' + #table + ']
WITH(NOLOCK) WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
Updated to handle for searching on a field name:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value UNIQUEIDENTIFIER
SET #value = --ENTER YOUR ACCOUNT NUMBER HERE
DECLARE table_cursor CURSOR
select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where cols.Name = 'AccountNumber'
OR cols.Name = 'AccountNum' OR cols.Name = 'Account'
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' + #column +
''' AS COL FROM [' + #table + '] WITH(NOLOCK)
WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor