Can I join sys.columns to their data? - sql

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.

Related

Dynamically stating FROM which table name to SELECT from

Would it be possible to use a system query to retrieve TABLE name and then SELECT * FROM that TABLE name. Along the lines of:
SELECT * FROM CAST (( SELECT TOP 1 t.Name
FROM sys.tables t
JOIN sys.columns c ON c.OBJECT_ID = t.OBJECT_ID
WHERE c.NAME = 'SomeColumnID' ) AS sys.tables )
The current issue is that the SELECT TOP 1 t.Name will return a string and could it be then cast into a valid Tables.Name.
You need dynamic sql for this: that is, build a query string from a query, then execute it with sp_executesql.
For your use case, that would look like:
declare #q nvarchar(max);
select top (1) #q = N'select * from ' + t.name
from sys.tables t
join sys.columns c on c.object_id = t.object_id
where c.name = 'SomeColumnID'
-- debuug the query
select #q sql;
-- execute the query
execute sp_executesql #q;

Query multiple tables using one join

I need to query multiple tables that contain the column name 'idClient' for a specific condition.
So far I acquire the tables that I need to query using the following query:
SELECT c.name AS 'ColumnName',
t.name AS 'TableName'
FROM sys.columns c (nolock)
JOIN sys.tables t (nolock) ON c.object_id = t.object_id
WHERE c.name LIKE '%idClient%'
ORDER BY TableName,
ColumnName;
This gives me the following result (in reality there are roughly 100 tables returned):
+------------+-----------------+
| ColumnName | TableName |
+------------+-----------------+
| idClient | tbClient |
| idClient | tbClientContact |
| idClient | tbInvoice |
+------------+-----------------+
In order for me to find all client records in each of the tables I am currently running 3 separate queries for each table name. For example:
SELECT * FROM tbClientContact (nolock)
JOIN tbClient (nolock)
ON tbClientContact.idClient = tbClient.idClient
WHERE tbClient.vcSurname = 'Smith'
Instead of running the above query 3 times for each table, is there an easier way to run the same query on all results that are returned as TableName?
GOAL: I have been tasked with - in the example above - removing any client records from a database where the client surname is 'Smith'. I am running the above SELECT query to find whether the idClient of all clients with the surname of 'Smith' will leave orphan records in tables where there is a link of 'idClient'. I am joining tbClient as the column vcSurname does not exist in any other table than tbClient.
You could try
SELECT idClient FROM tbClient WHERE vcSurname = 'Smith'
UNION ALL
SELECT idClient FROM tbClientContact WHERE vcSurname = 'Smith'
UNION ALL
SELECT idClient FROM tbInvoice WHERE vcSurname = 'Smith'
If you need more columns output, all three queries must have the same number of ouput columns and all of the same type
Edit
As others have suggested, it would be helpful to know what you are doing as the method you are attempting is never the best way of doing something. However, the cursor solution below should build a dynamic query to do what you want
DECLARE #table AS NVARCHAR(128)
DECLARE #sql AS NVARCHAR(4000)
DECLARE c CURSOR FOR
-- hey all the tables with the columns LIKE '%idClient%'
SELECT t.name AS 'TableName'
FROM sys.columns c (nolock)
JOIN sys.tables t (nolock) ON c.object_id = t.object_id
WHERE c.name LIKE '%idClient%'
ORDER BY TableName
-- loop through the results of the query line by line
OPEN c
FETCH NEXT FROM c INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
-- build the query dynamically
SET #sql =CONCAT(#sql,'SELECT idClient FROM ' + #table + ' WHERE vcSurname = ''Smith'' UNION ALL ')
FETCH NEXT FROM c INTO #table
END
-- remove last "UNION ALL" text
SET #sql = STUFF(#sql,LEN(#sql)-9,11,'')
EXEC sp_executesql #stmt = #sql
CLOSE c
DEALLOCATE c
EDIT EDIT
Just seen your edit. Does your table have foreign / primary key pairs, do the foreign keys have ON DELETE CASCADE? If so, it should just be a case of
DELETE from tbClient WHERE vcSurname = 'Smith'
For automated query:
After question Edit:
SELECT 'SELECT '+c.name+' FROM '+t.name+' T WITH(nolock)
JOIN tbClient (NOLOCK)
ON T.idClient = tbClient.idClient
WHERE vcSurname = ''Smith''
UNION ALL
'
FROM sys.columns c (nolock)
JOIN sys.tables t (nolock) ON c.object_id = t.object_id
WHERE c.name LIKE '%idClient%'
PRINT the result in text format (ctrl+T).
Copy the complete result and then remove last UNION ALL
1 Determine involved tables by querying systables (sysforeignkeys or syscolumns if you don't have FKs).
2 Manually write a comprehensive stored proc that is meant to do everything right
create proc dbo.Client_Del
#client_id int
as
begin try
if not exists(select 1 from dbo.Client c where c.id = #client_id)
raiserror("Client %d not found", 16, 1, #client_id)
begin tran
delete ct
from dbo.ClientContacts ct
where ct.client_id = #client_id
delete idt
from dbo.InvoiceDetail idt
inner join dbo.Invoice i
on i.invoice_id = idt.invoice_id
where i.client_id = #client_id
delete i
from dbo.Invoice i
where i.client_id = #client_id
delete c
from dbo.Client c
where c.client_id = #client_id
commit tran
end try
begin catch
if ##trancount > 0
rollback tran
throw
end catch
GO
3 Invoke your stored proc with an argument
declare #id int
set #id = (select c.client_id from dbo.Client c where c.LastName = 'Smith')
exec dbo.Client_Del
#client_id = #id
I am not sure on what is your need. And this post is enhanced code of #Luv. Use of this, you just run it.
declare #select nvarchar(max) = N''
set #select =
N'
declare #sql nvarchar(max) = N''''
SELECT #sql += '' UNION ALL SELECT ''+c.name+'' FROM ''+t.name+'' T WITH(nolock)
JOIN tbClient (NOLOCK)
ON T.idClient = tbClient.idClient
WHERE vcSurname = ''''Smith''''''
FROM sys.columns c (nolock)
JOIN sys.tables t (nolock) ON c.object_id = t.object_id
WHERE c.name LIKE ''%yourColumnName%''
set #sql = STUFF(#sql, 1, 10, '''')
print #sql
exec sp_executesql #sql
'
exec sp_executesql #select

How to loop through the columns in a table using SQL SELECT in SQL Server?

I have a table 'ABC' that has columns 'prod1_name', 'prod1_percentage',
'prod2_name', 'prod2_percentage' and so on until 'prod15_name', 'prod15_percentage'. Each column (from 1-15) may or may not have value.
Question - how can I use a loop to get the non-null values from the columns instead of listing each column names in the SQL SELECT?
I'm not sure I understand the question, but here's a few options:
You can get a list of schemas, tables, and columns for any table by querying the system views.
SELECT s.name, o.name, c.name
FROM sys.schemas s
INNER JOIN sys.objects o
ON o.schema_id = s.schema_id
INNER JOIN sys.columns c
ON c.object_id = o.object_id
WHERE s.name = #schemaName
AND o.name = #tableName
You can throw this into a cursor if you need to do something dynamic with them by prefixing the above with something similar to:
DECLARE schemaRunner CURSOR FAST_FORWARD FOR
From there, measure ##FETCH_STATUS in a while loop to grab your variables and do something to them:
--cursor definition
OPEN schemaRunner
FETCH NEXT FROM shcemaRunner into #sName, #tName, #cName
WHILE ##FETCH_STATUS = 0
BEGIN
--...DO SOMETHING TO THE VARIABLES HERE
FETCH NEXT FROM shcemaRunner into #sName, #tName, #cName
END
CLOSE schemaRunner
DEALLOCATE schemarunner
Now, for your question - Not selecting columns if they're null - so if all rows have them null? So, you could measure that in the DO SOMETHING portion of the above, and add or flag the columns if they come back true, so...
DECLARE #SQL NVARCHAR(MAX) = N'' -- THIS LINE SHOULD BE OUTSIDE, BEFORE THE LOOP.
SET #SQL = 'IF EXISTS (SELECT TOP 1 1 FROM ABC WHERE ' + #cName + ' IS NOT NULL) INSERT INTO <someTrackingTable> VALUES (''' + #cName + ''')'
EXEC SP_EXECUTESQL #SQL
Then, after the loop runs, you can construct your column list for your statement:
SELECT STUFF((
SELECT ', ' + Name
FROM <someTrackingTable>
FOR XML PATH('')
), 1, 2,'') AS Columns
This is a bit involved, but you are asking for a complex item.
This is an unpivot operation. I would use cross apply:
select t.productid, v.mixcomponent, v.mixpercentage
from t cross apply
(values (mix1component, mix1percentage),
(mix2component, mix2percentage),
(mix3component, mix3percentage),
(mix4component, mix4percentage),
. . .
) v(mixcomponent, mixpercentage)
where mixcomponent is not null;

Search column name having specific value in tables in certain database

Is there any way I can search a column having specific value I am trying to find through all tables in a database?
For example I have
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 '%VersionID%'
ORDER BY schema_name, table_name;
Which I found from searching. It gives me the table names that "versionID" column exists, but how can I search to return table names that for example have a value of "35" for that column.
Thanks in advance,
I apologize, maybe I am not being clear in my request. Maybe this cannot be done in SQL because I have not found anything through my research. But let me clarify.
Running this script
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 '%factor%'
--and table_name in (Select name from sysobjects where ratingVersionID = 35)
ORDER BY schema_name, table_name;
Will return this for example:
But let say, the "factor" column in "AutoAge_Factor" table does NOT HAVE any records matching to "35". How can I eliminate that table from returning in the results. But I would really prefer the format of the results be this way because I would like to use this return as a loop and do some other stuff within the loop.
Thanks again!
This modification to your sql will generate more SQL that you can run
SELECT
'select '''+SCHEMA_NAME(schema_id) +'.'+t.name + ''', '+c.name + ' from '
+ SCHEMA_NAME(schema_id) +'.' + t.name
+ ' where ' + c.name + ' = 35'
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE '%ID%'
ORDER BY schema_name(schema_id), t.name
You could make the SQL run automatically if necessary, but the syntax for that depends on your platform. Alternatively, copy the results into your query tool and run them, if that suffices
select s.name, c.name from sys.columns c inner join sys.tables s
on s.object_id = c.object_id where c.name like'%35%'
This will give you the columns with 35 and the associated table.
declare #tableName varchar(100), #colName varchar(100) declare
#sqlStatement nvarchar(100)
declare tablelist cursor for select s.name, c.name from sys.columns
c inner join sys.tables s on s.object_id = c.object_id where c.name
like'%YourSearchCondtion%' OPEN tablelist FETCH NEXT FROM tablelist
into #tableName, #colName
while ##FETCH_STATUS = 0 BEGIN
SELECT #sqlStatement = 'SELECT ' + #colName + ', * FROM ' + #tableName
+ ' WHERE ' + #colName + ' NOT LIKE ''%35%'''
exec sp_executesql #sqlStatement
-- Here you can get the table that you dont want and add to a temp table.. PRINT CAST(##ROWCOUNT AS VARCHAR)
FETCH NEXT FROM tablelist INTO #tableName, #colName END
CLOSE tablelist DEALLOCATE tablelist GO

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

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