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;
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.
If I have these tables below:
PLAYERS
ID Name
== ===========
1 Mick
2 Matt
COACHES
ID Name
== ===========
1 Bill
2 Don
And I have a script below to find all tables that has a column called "Name":
SELECT t.name AS table_name FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE 'Name'
Which returns the following:
table_name
===========
PLAYERS
COACHES
How can I select all the rows from both tables returned by the query above?
You will have to use dynamic sql, try something like this:
declare #t table( tablename varchar(50))
declare #sql varchar(max)
set #sql = ''
insert into #t
SELECT t.name AS table_name FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE 'Name'
select #sql = #sql + 'Select * From ' + tablename + ' union ' from #t
--remove the trailing 'union'
Select #sql = substring(#sql, 1, len(#sql) - 6)
exec (#sql)
The above script creates and executes the following sql
select * from coaches
union
select * from players
Since we are using union here, it is important that all your tables that have name as column is of same structure.
See more about dynamic sql from http://msdn.microsoft.com/en-us/library/ms188001.aspx
SELECT p.Id,p.Name,c.Id,c.Name
FROM Players p JOIN Coaches c
ON p.Id=c.Id
May be this can help you.
This question already has answers here:
Query to list number of records in each table in a database
(23 answers)
Closed 8 years ago.
I am searching for a SQL Script that can be used to determine if there is any data (i.e. row count) in any of the tables of a given database.
The idea is to re-incarnate the database in case there are any rows existing (in any of the database).
The database being spoken of is Microsoft SQL SERVER.
Could someone suggest a sample script?
The following SQL will get you the row count of all tables in a database:
CREATE TABLE #counts
(
table_name varchar(255),
row_count int
)
EXEC sp_MSForEachTable #command1='INSERT #counts (table_name, row_count) SELECT ''?'', COUNT(*) FROM ?'
SELECT table_name, row_count FROM #counts ORDER BY table_name, row_count DESC
DROP TABLE #counts
The output will be a list of tables and their row counts.
If you just want the total row count across the whole database, appending:
SELECT SUM(row_count) AS total_row_count FROM #counts
will get you a single value for the total number of rows in the whole database.
If you want to by pass the time and resources it takes to count(*) your 3million row tables. Try this per SQL SERVER Central by Kendal Van Dyke.
Row Counts Using sysindexes
If you're using SQL 2000 you'll need to use sysindexes like so:
-- Shows all user tables and row counts for the current database
-- Remove OBJECTPROPERTY function call to include system objects
SELECT o.NAME,
i.rowcnt
FROM sysindexes AS i
INNER JOIN sysobjects AS o ON i.id = o.id
WHERE i.indid < 2 AND OBJECTPROPERTY(o.id, 'IsMSShipped') = 0
ORDER BY o.NAME
If you're using SQL 2005 or 2008 querying sysindexes will still work but Microsoft advises that sysindexes may be removed in a future version of SQL Server so as a good practice you should use the DMVs instead, like so:
-- Shows all user tables and row counts for the current database
-- Remove is_ms_shipped = 0 check to include system objects
-- i.index_id < 2 indicates clustered index (1) or hash table (0)
SELECT o.name,
ddps.row_count
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID
AND i.index_id = ddps.index_id
WHERE i.index_id < 2 AND o.is_ms_shipped = 0 ORDER BY o.NAME
Works on Azure, doesn't require stored procs.
SELECT t.name AS table_name
,s.row_count AS row_count
FROM sys.tables t
JOIN sys.dm_db_partition_stats s
ON t.OBJECT_ID = s.OBJECT_ID
AND t.type_desc = 'USER_TABLE'
AND t.name NOT LIKE '%dss%' --Exclude tables created by SQL Data Sync for Azure.
AND s.index_id IN (0, 1)
ORDER BY table_name;
Credit.
This one looks better than the others I think.
USE [enter your db name here]
GO
SELECT SCHEMA_NAME(A.schema_id) + '.' +
--A.Name, SUM(B.rows) AS 'RowCount' Use AVG instead of SUM
A.Name, AVG(B.rows) AS 'RowCount'
FROM sys.objects A
INNER JOIN sys.partitions B ON A.object_id = B.object_id
WHERE A.type = 'U'
GROUP BY A.schema_id, A.Name
GO
Short and sweet
sp_MSForEachTable 'DECLARE #t AS VARCHAR(MAX);
SELECT #t = CAST(COUNT(1) as VARCHAR(MAX))
+ CHAR(9) + CHAR(9) + ''?'' FROM ? ; PRINT #t'
Output:
SELECT
sc.name +'.'+ ta.name TableName, SUM(pa.rows) RowCnt
FROM
sys.tables ta
INNER JOIN sys.partitions pa
ON pa.OBJECT_ID = ta.OBJECT_ID
INNER JOIN sys.schemas sc
ON ta.schema_id = sc.schema_id
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
GROUP BY sc.name,ta.name
ORDER BY SUM(pa.rows) DESC
SQL Server 2005 or later gives quite a nice report showing table sizes - including row counts etc. It's in Standard Reports - and it is Disc Usage by Table.
Programmatically, there's a nice solution at:
http://www.sqlservercentral.com/articles/T-SQL/67624/
Don't use SELECT COUNT(*) FROM TABLENAME, since that is a resource intensive operation. One should use SQL Server Dynamic Management Views or System Catalogs to get the row count information for all tables in a database.
I would make a minor change to Frederik's solution. I would use the sp_spaceused system stored procedure which will also include data and index sizes.
declare c_tables cursor fast_forward for
select table_name from information_schema.tables
open c_tables
declare #tablename varchar(255)
declare #stmt nvarchar(2000)
declare #rowcount int
fetch next from c_tables into #tablename
while ##fetch_status = 0
begin
select #stmt = 'sp_spaceused ' + #tablename
exec sp_executesql #stmt
fetch next from c_tables into #tablename
end
close c_tables
deallocate c_tables
Here's a dynamic SQL approach that also gives you the schema as well:
DECLARE #sql nvarchar(MAX)
SELECT
#sql = COALESCE(#sql + ' UNION ALL ', '') +
'SELECT
''' + s.name + ''' AS ''Schema'',
''' + t.name + ''' AS ''Table'',
COUNT(*) AS Count
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
FROM sys.schemas s
INNER JOIN sys.tables t ON t.schema_id = s.schema_id
ORDER BY
s.name,
t.name
EXEC(#sql)
If needed, it would be trivial to extend this to run over all databases in the instance (join to sys.databases).
select all rows from the information_schema.tables view, and issue a count(*) statement for each entry that has been returned from that view.
declare c_tables cursor fast_forward for
select table_name from information_schema.tables
open c_tables
declare #tablename varchar(255)
declare #stmt nvarchar(2000)
declare #rowcount int
fetch next from c_tables into #tablename
while ##fetch_status = 0
begin
select #stmt = 'select #rowcount = count(*) from ' + #tablename
exec sp_executesql #stmt, N'#rowcount int output', #rowcount=#rowcount OUTPUT
print N'table: ' + #tablename + ' has ' + convert(nvarchar(1000),#rowcount) + ' rows'
fetch next from c_tables into #tablename
end
close c_tables
deallocate c_tables
This is my favorite solution for SQL 2008 , which puts the results into a "TEST" temp table that I can use to sort and get the results that I need :
SET NOCOUNT ON
DBCC UPDATEUSAGE(0)
DROP TABLE #t;
CREATE TABLE #t
(
[name] NVARCHAR(128),
[rows] CHAR(11),
reserved VARCHAR(18),
data VARCHAR(18),
index_size VARCHAR(18),
unused VARCHAR(18)
) ;
INSERT #t EXEC sp_msForEachTable 'EXEC sp_spaceused ''?'''
SELECT * INTO TEST FROM #t;
DROP TABLE #t;
SELECT name, [rows], reserved, data, index_size, unused FROM TEST \
WHERE ([rows] > 0) AND (name LIKE 'XXX%')
SELECT
SUM(sdmvPTNS.row_count) AS [DBRows]
FROM
sys.objects AS sOBJ
INNER JOIN sys.dm_db_partition_stats AS sdmvPTNS
ON sOBJ.object_id = sdmvPTNS.object_id
WHERE
sOBJ.type = 'U'
AND sOBJ.is_ms_shipped = 0
AND sdmvPTNS.index_id < 2
GO
I'm looking to pull a specific line from a number of table that have a field name criteria1. The problem I'm having is that when I combine the Owner and Table Name and try to call "select criteria1 from #t where linenum = 1" SQL is expecting #t to be a table. I need to know how to construct the full table name and then pass it to this query. I know I can us a programming language to access the DB but i need this to be in SQL. If someone knows of a better way of doing this that would be great too.
declare #next as varchar
declare #owner varchar
while 1=1
begin
set #next = (select top 1 o.name FROM syscolumns c inner join sysobjects o on c.id = o.id
where c.name = 'criteria1' and o.id > #next order by o.id)
if #next is null
break
else
begin
set #owner = (select top 1 u.name
FROM syscolumns c inner join
sysobjects o on c.id = o.id left join
sysusers u on o.uid=u.uid
where c.name = 'criteria1' and o.id = #next order by o.id)
declare #t as varchar
set #t = #owner+'.'+#next
select criteria1 from #t where linenum = 1
end
continue
end
You can build the entire query you want as a varchar() and then execute it with the sp_executesql stored procedure.
http://msdn.microsoft.com/en-us/library/ms188001.aspx
In your case, that bit at the end becomes
declare #sql varchar(512);
set #sql = 'select criteria1 from ' + #t + ' where linenum = 1'
sp_executesql #sql
Have you considered the following construct in a stored procedure?
CASE #tablename
WHEN 'table1' THEN SELECT * FROM table1
WHEN 'table2' THEN SELECT * FROM table2
WHEN 'table3' THEN SELECT * FROM table3
WHEN 'table4' THEN SELECT * FROM table4
END
In case you're married to dynamic SQL (considered to be a bad choice for this problem space), this guide to dynamic SQL should help a lot. It helped me and I've used dynamic SQL extensively.
Thanks for all the help. This is what I ended up with.
declare cur cursor for
select u.name + '.' + o.name tname
FROM sysobject o left join
syscolumns c on c.id = o.id left join
sysusers u on o.uid=u.uid
where c.name = 'criteria1'
declare #tn as varchar(512)
open cur
fetch next from cur into #tn
create table holding_table ( val varchar(512), table_name varchar(512))
declare #sql nvarchar(1000)
while ##FETCH_STATUS = 0
begin
set #sql = 'insert into holding_table select criteria1, ''' + #tn + ''' from ' + #tn + ' where linenum = 1'
execute sp_executesql #sql
fetch next from cur into #tn
end
close cur
deallocate cur
Maybe a view can be used here?
CREATE VIEW vCriterias
AS
SELECT 'Table1' AS TableName,
linenum,
criteria1
FROM Table1
UNION ALL
SELECT 'Table2' AS TableName,
linenum,
criteria1
FROM Table2
UNION ALL
SELECT 'Table3' AS TableName,
linenum,
criteria1
FROM Table3
go
Then selection is like:
SELECT criteria1
FROM vCriterias
WHERE linenum = 3
AND TableName IN ('Table1','Table3')