How to count rows in table join? - sql

I am developing a SQL sproc and I want to return the number of rows for each table. How could I rewrite this statement so that it will list number of rows from each table below?
SELECT COUNT(*)
FROM [test_setup_details_form_view] [tsdf]
JOIN [test_setup_header_form_view]
ON [test_setup_header_form_view].[test_setup_header_id]
= [tsdf].[test_setup_header_id]
JOIN [test_header_rv] [th] with(nolock)
ON [th].[test_setup_header_id]
= [test_setup_header_form_view].[test_setup_header_id]
JOIN [test_details_answers_expanded_view] [tdae]
ON [tdae].[test_setup_details_id] = [tsdf].[test_setup_details_id]
AND [th].[test_header_id] = [tdae].[test_header_id]
JOIN [event_log_rv] [e]
ON [e].[event_log_id] = [tdae].[event_log_id]
When I execute this statement, it just gives me the total rows after all of the joins.

If you are trying to just get counts for each of these tables irrespective of the joins:
SELECT
OBJECT_SCHEMA_NAME([object_id]),
OBJECT_NAME([object_id]),
c
FROM
(
SELECT [object_id],
c = SUM(row_count)
FROM
sys.dm_db_partition_stats -- no NOLOCK necessary
WHERE
index_id IN (0,1)
AND OBJECT_NAME([object_id]) IN
(
N'test_setup_details_from_view',
N'test_setup_header_from_view',
... etc etc. ...
)
GROUP BY [object_id]
) AS x;

Use count (distinct <columnname>) on a unique column for each table that you need to count.

From each table? Why not use the metadata tables then?
You are trying to do something in code that already exists in the metadata tables:
Select
schema_name(schema_id) + '.' + t.name as TableName
, i.rows
from sys.tables t (nolock)
join sys.sysindexes i (nolock) on t.object_id = i.id
and i.indid < 2

Related

Can't assign an alias to result of a join - SQL

I have a DB with lots of tables without use. I'd like to filter out the tables without any data. So I used a snippet How to fetch the row count for all tables by #ismetAlkan.
However, I want to filter out 0, so used something like this and it doesn't work.
USE [my_db]
GO
SELECT * FROM
(
SELECT SCHEMA_NAME(A.schema_id) + '.' +
A.Name, SUM(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
) AS Result
where Result.RowCount > 0
GO
Any help appreciated!
There are three problems.
SELECT * FROM
(
SELECT ObjectName = SCHEMA_NAME(obj.[schema_id])
+ '.' + obj.name,
[RowCount] = SUM(p.rows)
FROM sys.objects AS obj
INNER JOIN sys.partitions AS p
ON obj.[object_id] = p.[object_id]
WHERE obj.type = 'U'
GROUP BY obj.[schema_id], obj.name
) AS Result
WHERE Result.[RowCount] > 0;
When you move a query into a derived table, subquery, or CTE, all of the columns need to have names.
'Alias' should be [Alias] since the former makes it look like a string and that form is deprecated in some contexts.
As Larnu pointed out, ROWCOUNT needs to be escaped in all spots, not just one.

Trying to create a rolling row limit stored procedure

Platform: SQL Server
Goal: get the current number of rows and subtract from it the amount I want to keep and then delete the remainder. I was planning to turn it into a stored procedure if I could get it working.
For the code I was thinking something like this:
SET N = (EXEC sp_spaceused dbo.Name rows)
SET D = (%N%-30000000)
DELETE TOP (%D%) FROM dbo.Name
I used sp_spaceused to avoid locking the table to input statements, as would be the case with count.
You can use an updatable CTE or derived table to delete. Assuming you wanted to delete random rows from the table, you don't need any further calculations.
Don't use sp_spaceused, get the data from sys.partitions instead
DECLARE #N int = (
SELECT SUM(p.rows)
FROM sys.partitions p
INNER JOIN sys.tables t ON p.[object_id] = t.[object_id]
INNER JOIN sys.schemas s ON s.[schema_id] = t.[schema_id]
WHERE t.name = N'Name'
AND s.name = N'dbo'
AND p.index_id IN (0,1)
);
WITH cte AS (
SELECT TOP (#N - 30000000)
*
FROM dbo.Name
)
DELETE FROM cte;
db<>fiddle

How to fetch tables and their corresponding column names used in stored procedures using t-sql?

I just want to get the tables and their corresponding columns which are written as a part of stored procedure using t-sql? How can I get a list of the same? Can anyone help me? Thanks.
If you have table names with you, use following query to get the required details as:
SELECT COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,IS_NULLABLE,COLUMN_DEFAULT,TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN ('TBL1','TBL2') ORDER BY TABLE_NAME
I tried following effort to get my query resolved.
SELECT distinct sp.name as StoredProc, tbl.name AS [Table], col.name AS [Column]
FROM sysobjects sp
INNER JOIN sysdepends sd ON sp.id = sd.id
INNER JOIN sysobjects tbl ON tbl.id = sd.depid
INNER JOIN syscolumns col ON col.colid = sd.depnumber AND col.id = sd.depid
WHERE sp.name IN ( SELECT name FROM sysobjects WHERE id IN(SELECT id from syscomments
WHERE text LIKE '%passParamToGetYourSPs_%'))
--group BY sp.name
--AND sp.name = 'Search_SP_Name'
ORDER BY sp.name, tbl.name, col.name
Thanks for all your efforts. Smile | :)

How to determine if a specific set of tables in a database are empty

I have database A which contains a table (CoreTables) that stores a list of active tables within database B that the organization's users are sending data to.
I would like to be able to have a set-based query that can output a list of only those tables within CoreTables that are populated with data.
Dynamically, I normally would do something like:
For each row in CoreTables
Get the table name
If table is empty
Do nothing
Else
Print table name
Is there a way to do this without a cursor or other dynamic methods? Thanks for any assistance...
Probably the most efficient option is:
SELECT c.name
FROM dbo.CoreTables AS c
WHERE EXISTS
(
SELECT 1
FROM sys.partitions
WHERE index_id IN (0,1)
AND rows > 0
AND [object_id] = OBJECT_ID(c.name)
);
Just note that the count in sys.sysindexes, sys.partitions and sys.dm_db_partition_stats are not guaranteed to be completely in sync due to in-flight transactions.
While you could just run this query in the context of the database, you could do this for a different database as follows (again assuming that CoreTables does not include schema in the name):
SELECT c.name
FROM DatabaseA.CoreTables AS c
WHERE EXISTS
(
SELECT 1
FROM DatabaseB.sys.partitions AS p
INNER JOIN DatabaseB.sys.tables AS t
ON p.[object_id] = t.object_id
WHERE t.name = c.name
AND p.rows > 0
);
If you need to do this for multiple databases that all contain the same schema (or at least overlapping schema that you're capturing in aggregate in a central CoreTables table), you might want to construct a view, such as:
CREATE VIEW dbo.CoreTableCounts
AS
SELECT db = 'DatabaseB', t.name, MAX(p.rows)
FROM DatabaseB.sys.partitions AS p
INNER JOIN DatabaseB.sys.tables AS t
ON p.[object_id] = t.[object_id]
INNER JOIN DatabaseA.dbo.CoreTables AS ct
ON t.name = ct.name
WHERE p.index_id IN (0,1)
GROUP BY t.name
UNION ALL
SELECT db = 'DatabaseC', t.name, rows = MAX(p.rows)
FROM DatabaseC.sys.partitions AS p
INNER JOIN DatabaseC.sys.tables AS t
ON p.[object_id] = t.[object_id]
INNER JOIN DatabaseA.dbo.CoreTables AS ct
ON t.name = ct.name
WHERE p.index_id IN (0,1)
GROUP BY t.name
-- ...
GO
Now your query isn't going to be quite as efficient, but doesn't need to hard-code database names as object prefixes, instead it can be:
SELECT name
FROM dbo.CoreTableCounts
WHERE db = 'DatabaseB'
AND rows > 0;
If that is painful to execute you could create a view for each database instead.
In SQL Server, you can do something like:
SELECT o.name, st.row_count
FROM sys.dm_db_partition_stats st join
sys.objects o
on st.object_id = o.object_id
WHERE index_id < 2 and st.row_count > 0
By the way, this specifically does not use OBJECT_ID() or OBJECT_NAME() because these are evaluated in the current database. The above code continues to work for another database, using 3-part naming. This version also takes into account multiple partitions:
SELECT o.name, sum(st.row_count)
FROM <dbname>.sys.dm_db_partition_stats st join
<dbname>.sys.objects o
on st.object_id = o.object_id
WHERE index_id < 2
group by o.name
having sum(st.row_count) > 0
something like this?
//
foreach (System.Data.DataTable dt in yourDataSet.Tables)
{
if (dt.Rows.Count != 0) { PrintYourTableName(dt.TableName); }
}
//
This is a way you can do it, that relies on system tables, so be AWARE it may not always work in future versions of SQL. With that strong caveat in mind.
select distinct OBJECT_NAME(id) as tabName,rowcnt
from sys.sysindexes si
join sys.objects so on si.id=si.id
where indid=1 and so.type='U'
You would add to the where clause the tables you are interested in and rowcnt <1

Query to get a Table Name based on its ID

I have a table (DB_TableInfo) in my DB like the following
TableId Type
859374678 R
579845658 B
478625849 R
741587469 E
.
.
.
this table represents all tables in my DB. What I wanna do is to write a query to select tables of Type 'R', get their Id and return the Name of the table belonging to that Id (the TableName column is not available in the specified table)
Can anybody help me out?
I wanna write a query similar to this one!
SELECT TableID = OBJECT_NAME FROM [DB_TableInfo] WHERE Type = 'R'
From the mention of sys.objects and use of square brackets I assume you are on SQL Server.
You can use the object_name function.
SELECT OBJECT_NAME(TableID) /*Might match objects that aren't tables as well though*/
FROM [DB_TableInfo]
WHERE Type = 'R'
Or join onto sys.tables
SELECT T.name
FROM [DB_TableInfo] D
join sys.tables T ON D.TableID = T.object_id
WHERE D.Type = 'R'
And to exclude empty tables
SELECT t.name
FROM DB_TableInfo d
JOIN sys.tables t ON d.TableId = t.object_id
JOIN sys.dm_db_partition_stats ps ON ps.object_id = t.object_id
WHERE d.Type = 'R' and ps.index_id <= 1
GROUP BY d.TableId, t.name
HAVING SUM(ps.row_count) > 0