Trying to create a table of table names and max values - sql

In SQL Server, we have a number of tables, all containing a field last_modified that records when a particular record was created, modified, or flagged for deletion. I want to create a table of table names and the max() value of last_modified.
I'm brute forcing it as follows:
I run a query modified from Query to list number of records in each table in a database to list tables that have rows and eliminate some internal tables.
SELECT
t.NAME AS TableName,
p.[Rows]
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
WHERE
t.NAME NOT LIKE 'dt%' AND t.name not like '%ml%' and
t.NAME not in ('OrderHeaders', 'OrderHeaderExtendedText', 'OrderLIDetails', 'OrderLIDetailExtendedText', 'UserCustomerXRef', 'UserDetails', 'UserDetailExtendedText', 'UserTypeDescription') and
p.rows <> 0 and
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, p.[Rows]
ORDER BY
TableName
This gives me a table like this:
Then I paste this output into Excel and create a series of queries there:
A2.value reads BidCustomerXRef. C2.Value reads ="select top 1 last_modified, '" & A2 & "' as 'Table' from " & A2 & " where deleted = 'N' order by last_modified desc" and so I get
select top 1 last_modified, 'BidCustomerXRef' as 'Table'
from BidCustomerXref
where deleted = 'N'
order by last_modified desc`
So I copy all of those rows to my SQL query window and I get this:
What I want is a single table that looks like this:
TableName
last_modified
BidCustomerXRef
2022-06-21 21:30:07.287
Bids
2022-06-22 20:00:06.383
CustomerARDetail
2022-06-22 18:00:11.923
etc.

Here is one way you can do this. I am using the system tables to generate dynamic sql instead of using a dreaded cursor or other sort of iteration going row by agonizing row.
declare #sql nvarchar(max) = ''
select #sql += 'select TableName = ''' + s.name + '.' + t.name + ''', last_modified = (select max(last_modified) from ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' where deleted = ''N'') union all '
from sys.tables t
join sys.schemas s on s.schema_id = t.schema_id
join sys.indexes i ON t.object_id = i.object_id
join sys.partitions p ON i.object_id = p.object_id and i.index_id = p.index_id
where t.NAME NOT LIKE 'dt%'
and t.name not like '%ml%'
and t.NAME not in ('OrderHeaders', 'OrderHeaderExtendedText', 'OrderLIDetails', 'OrderLIDetailExtendedText', 'UserCustomerXRef', 'UserDetails', 'UserDetailExtendedText', 'UserTypeDescription')
and p.rows > 0
and i.object_id > 255
and i.index_id <= 1
order by t.name
select #sql = left(#sql, len(#sql) - 9) --removes the last UNION ALL
select #sql
--uncomment the line below once you have evaluated that the dynamic sql is correct
--exec sp_executesql #sql

Related

SQL get dynamic query result into another query

I would like to get, for each line, the result of my ReqCount query directly into my main SELECT
(the main query is simplified on purpose).
I've tried using the EXEC sp_executesql but it was unsuccessful.
How can I manage to do this?
BEGIN
SELECT DISTINCT
TBL.name AS TableName
, IDX.type_desc
, COL.name
, 'SELECT MAX(occurs) FROM (SELECT ' + COL.name + ', count(*) as occurs FROM ' + TBL.name
+ ' GROUP BY ' + COL.name + ') a' as ReqCount
FROM sys.tables AS TBL
INNER JOIN sys.schemas SCH ON TBL.schema_id = SCH.schema_id AND TBL.name <> 'sysdiagrams'
INNER JOIN sys.indexes IDX ON TBL.object_id = IDX.object_id AND IDX.type = 0
LEFT JOIN sys.columns COL ON COL.object_id = TBL.object_id AND (COL.name LIKE '%id' OR COL.name LIKE '%code')
END
TableName type_desc name ReqCount
t_intervention_instruction HEAP NULL NULL
t_constat HEAP con_code SELECT MAX(occurs) FROM (SELECT con_code, count(*) as occurs FROM t_constat GROUP BY con_code) a
t_cri_clientele HEAP NULL NULL
t_ope_sur_branchement HEAP osb_id SELECT MAX(occurs) FROM (SELECT osb_id, count(*) as occurs FROM t_ope_sur_branchement GROUP BY osb_id) a

Get all tables from database, if the table has some records present

Is there any way to get all the tables, with a condition that if table is not empty(if it has some records present).
I can get the list of tables with :
USE 'DatabaseName'
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_Schema = 'dbo'
ORDER BY table_NAME.
But, I want something like
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_Schema = 'dbo'
AND "**table has some records present**" ORDER BY table_NAME
Try :
SELECT
OBJECT_NAME(T.OBJECT_ID) AS TABLE_NAME,
SUM(P.ROWS) AS TOTAL_ROWS
FROM
sys.tables T
INNER JOIN
sys.partitions P
ON T.OBJECT_ID = P.OBJECT_ID
WHERE
P.INDEX_ID IN (0,1)
GROUP BY
T.OBJECT_ID
HAVING
SUM(P.ROWS) > 0
run this
select 'select count(*) [entries],'''+ TABLE_NAME + ''' from '+ quotename(TABLE_SCHEMA) + '.' + quotename(TABLE_NAME) + ' union' from INFORMATION_SCHEMA.TABLES
to get a list of every table and the number of entries in that table then you can put that into a temp table and look for anything where entries >1.
Remember to remove the union at the end.
select * from
(
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
) as T where RowCnt>0
More help can get you from here

SQL Search for a Binary value

I've seen similar queries where a string or character value is used to search an entire database. Those queries do not return results that lie in a BINARY(8) field. I've tried to modify those queries to no avail.
Is there a way to search the entire database for specific binary values, such as 0x0000000000000017?
Thanks guys.
You can use the system tables to find this.
MSSQL:
SELECT t.name AS table_name,
c.name AS column_name,
ty.name
FROM sys.tables AS t
INNER JOIN sys.columns c
ON t.OBJECT_ID = c.OBJECT_ID
INNER JOIN sys.types ty
ON t.schema_id = ty.schema_id
WHERE ty.system_type_id = 173
ORACLE:
SELECT owner,table_name, column_name,data_TYPE
FROM all_tab_columns where data_TYPE = 'RAW';
Well...
select *
from foo
where foo.binary8column = 0x0000000000000017
should do. If you want to enumerate all the tables and find all the binary or varbinary columns, this query
select table_name = object_schema_name(tn.object_id) + '.' + tn.name ,
column_name = c.name ,
type = t.name + '(' + convert(varchar,c.max_length) + ')'
from sys.types t
join sys.columns c on c.system_type_id = t.system_type_id
join sys.tables tn on tn.object_id = c.object_id
where t.name in ( 'binary', 'varbinary' )
and c.max_length >= 8
should give enough information to generate the queries for every such table.

How to find which indexes and constraints contain a specific column?

I am planing a database change and I have a list of columns included in process. Can I list all indexes in which is a specific column included?
Edit
So far I have (combined from answers):
declare #TableName nvarchar(128), #FieldName nvarchar(128)
select #TableName= N'<<Table Name>>', #FieldName =N'<<Field Name>>'
(SELECT distinct systab.name AS TABLE_NAME,sysind.name AS INDEX_NAME, 'index'
FROM sys.indexes sysind
INNER JOIN sys.index_columns sysind_col
ON sysind.object_id = sysind_col.object_id and sysind.index_id = sysind_col.index_id
INNER JOIN sys.columns sys_col
ON sysind_col.object_id = sys_col.object_id and sysind_col.column_id = sys_col.column_id
INNER JOIN sys.tables systab
ON sysind.object_id = systab.object_id
WHERE systab.is_ms_shipped = 0 and sysind.is_primary_key=0 and sys_col.name =#FieldName and systab.name=#TableName
union
select t.name TABLE_NAME,o.name, 'Default' OBJ_TYPE
from sys.objects o
inner join sys.columns c on o.object_id = c.default_object_id
inner join sys.objects t on c.object_id = t.object_id
where o.type in ('D') and c.name =#FieldName and t.name=#TableName
union
SELECT u.TABLE_NAME,u.CONSTRAINT_NAME, 'Constraint' OBJ_TYPE
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE u
where u.COLUMN_NAME = #FieldName and u.TABLE_NAME = #TableName
) order by 1
But I am not too happy with combining of sys. and 'INFORMATION_SCHEMA.' Can it be avoided?
---Using sp_helpindex and your TableName
exec sp_helpindex YourTableName
---Using sys.tables with your TableName and ColumnName
select distinct c.name, i.name, i.type_desc,...
from sys.indexes i
join sys.index_columns ic on i.index_id = ic.index_id
join sys.columns c on ic.column_id = c.column_id
where i.object_id = OBJECT_ID(N'YourTableName') and c.name = 'YourColumnName'
EDIT: As per comment, you can also join object_Ids without using distinct
select c.name, i.name, i.type_desc
from sys.indexes i
join sys.index_columns ic on i.index_id = ic.index_id and i.object_id = ic.object_id
join sys.columns c on ic.column_id = c.column_id and ic.object_id = c.object_id
where i.object_id = OBJECT_ID(N'YourTableName') and c.name = 'YourColumnName'
SELECT * FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
USE TABLE_NAME IN WHERE if you what to know the constraint on a table.
and use column_name if you know the column name.
To get information regarding all indexes in which is a specific column is included following two catalog views can be used:
sys.indexes , sys.index_columns
Query:
SELECT
sysind.name AS INDEX_NAME
,sysind.index_id AS INDEX_ID
,sys_col.name AS COLUMN_NAME
,systab.name AS TABLE_NAME
FROM sys.indexes sysind
INNER JOIN sys.index_columns sysind_col
ON sysind.object_id = sysind_col.object_id and sysind.index_id = sysind_col.index_id
INNER JOIN sys.columns sys_col
ON sysind_col.object_id = sys_col.object_id and sysind_col.column_id = sys_col.column_id
INNER JOIN sys.tables systab
ON sysind.object_id = systab.object_id
WHERE (1=1)
AND systab.is_ms_shipped = 0
AND sys_col.name IN(specific column list for which indexes are to be queried)
ORDER BY
systab.name,sys_col.name, sysind.name,sysind.index_id
Hope this helps!
If you have access to the sys schema on your database then you can query sys.indexes to get the index ID and use that with the function index_col to get the columns. The final parameter for index_col is the index of the column within the index (i.e. if there are 3 columns in the index you would have to call index_col 3 times with 1, 2, 3 in the last parameter)
select index_id from sys.indexes
where object_id = object_id(#objectname)
select index_col(#objectname, #indexid, 1)
This should give you constraints as well as they are just special indexes.

Get index statistics information for tables referenced in a specific view

I'm trying to amend the following script to point at just the indexes associated with a particular view vw_foo. Is this possible?
SELECT name AS index_name,
STATS_DATE(OBJECT_ID, index_id) AS StatsUpdated
FROM sys.indexes
Edit
When I say "associated" I mean the indexes on the underlying tables that are used to create the view
Possibly, it will be helpful for you -
SELECT
SCHEMA_NAME(o.[schema_id]) + '.' + o.name
, s.name
, statistics_update_date = STATS_DATE(o.[object_id], stats_id)
FROM sys.objects o
JOIN sys.stats s ON o.[object_id] = s.[object_id]
WHERE o.[type] = 'V' AND o.name = 'vw_foo'
DECLARE #table_name SYSNAME
SELECT #table_name = 'dbo.vw_foo'
EDITED
When I say "associated" I mean the indexes on the tables that are used
by the view
This query returns the list of the views where is used the specified table + shows additional info about the used index -
SELECT
o.table_name
, b.view_name
, i.name
, stast_updates = STATS_DATE(i.[object_id], i.index_id)
, dm_ius.last_user_seek
, dm_ius.last_user_scan
, dm_ius.last_user_lookup
, dm_ius.last_user_update
, dm_ius.user_updates
, dm_ius.user_lookups
, dm_ius.user_scans
, dm_ius.user_seeks
FROM (
SELECT
table_name = s.name + '.' + o.name
, o.[object_id]
FROM sys.objects o
JOIN sys.schemas s ON o.[schema_id] = s.[schema_id]
WHERE o.[type] = 'U'
AND s.name + '.' + o.name = #table_name
) o
JOIN sys.indexes i ON o.[object_id] = i.[object_id]
AND i.[type] > 0
AND i.is_disabled = 0
AND i.is_hypothetical = 0
LEFT JOIN sys.dm_db_index_usage_stats dm_ius ON i.index_id = dm_ius.index_id AND dm_ius.[object_id] = i.[object_id]
OUTER APPLY (
SELECT
view_name = r.referencing_schema_name + '.' + r.referencing_entity_name
, r.referencing_id
FROM sys.dm_sql_referencing_entities (o.table_name, 'OBJECT') r
JOIN sys.objects o2 ON r.referencing_id = o2.[object_id]
WHERE o2.[type] = 'V'
) b
WHERE b.view_name IS NOT NULL
The existing answers were probably heading towards checking the name in sys.objects but never do it. But there's no need to do so anyway, since the OBJECT_ID() function lets you get an object_id in a clean fashion:
SELECT name AS index_name,
STATS_DATE(OBJECT_ID, index_id) AS StatsUpdated
FROM sys.indexes
WHERE object_id = OBJECT_ID('vw_foo')