I'm collecting metadata using the sys.* views, and according to the documentation, the sys.identity_columns view will return the seed and increment values like so.
CREATE TABLE ident_test (
test_id int IDENTITY(1000,10),
other int
)
SELECT name, seed_value, increment_value
FROM sys.identity_columns
WHERE object_id = OBJECT_ID( 'ident_test' )
However, the above query just returns one column. Is it just me?
(Note: I've had to change this question somewhat from its earlier version.)
Shouldn't you reverse the from and join, like this:
SELECT c.name, i.seed_value, i.increment_value
from sys.identity_columns i
join sys.columns c
ON i.object_id = c.object_id
AND i.column_id = c.column_id
You are missing the Where clause. Your query is effectively saying 'Give me all of sys.columns and any matching rows from sys.identity_columns you have (but give me null if there are no matching rows)'.
By adding the Where clause below you'll change it to only return where an exact match is returned, which is the same as an inner join in this instance really.
SELECT
c.name, i.seed_value, i.increment_value
FROM
sys.columns c
LEFT OUTER JOIN sys.identity_columns i
ON i.object_id = c.object_id
AND i.column_id = c.column_id
Where I.seed_value is not null
So I think your data is correct, there are no results to view though.
Are you sure you are running this in a database with tables with IDENTITY columns?
SELECT c.name, i.seed_value, i.increment_value
FROM sys.columns c
INNER JOIN sys.identity_columns i
ON i.object_id = c.object_id
AND i.column_id = c.column_id
Returns rows for me in a regular production database with a few identities.
Using a LEFT JOIN returns these rows as well as many which are not IDENTITY
I ran this on another database, and I noticed some NULLs are returned (even in the INNER JOIN case). This is because some of the columns are in VIEWs.
Try adding:
INNER JOIN sys.tables t
ON t.object_id = c.object_id
To filter only to actual IDENTITY columns in tables.
your query returns what I'd expect [see below]; it returns the single meta-data row about the single identity column (test_ID) in table (ident_test), the oter column (other) has no meta-data in the sys.identity_column as is is not an identity.
SELECT name, seed_value, increment_value
FROM sys.identity_columns
WHERE object_id = OBJECT_ID( 'ident_test' )
select name, is_identity, is_nullable
from sys.columns
WHERE object_id = OBJECT_ID( 'ident_test' )
Which gives
name seed_value increment_value
-----------------------------------------
test_id 1000 10
(1 row(s) affected)
name is_identity is_nullable
-------------------------------------
test_id 1 0
other 0 1
(2 row(s) affected)
Related
I am working on a project to document the tables in a database. One of the features I would like to include is a meta-table that includes some facts and statistics for every column in the table. I already have a query that does the following:
Column
Data Type
Nullable
Primary Key
Foreign Key
Col A
string
N
PK
Col B
string
N
FK
Etc.....
I would also like to get some common statistics of each column as either another table (or preferably as part of the above table) that would look like:
Column
Min
Max
Count
Count Distinct
Col A
1
486842
486842
486842
Col B
1
756
486842
395
Col C
1
5
210523
3
Perhaps also "Most Common Value" and "# of Most Common Value". Basic information that will give me a quick idea as to the distribution and uniqueness of values in a column. The calculations aren't the hard part. I don't understand how to connect actual tables to meta data. It also has a confusing back-and-forth pivot aspect.
Can this be done dynamically without typing out each and every column?
The following query produces the first table:
select
col.name as column_name,
t.name as data_type,
case when col.is_nullable = 0 then 'N'
else 'Y' end as nullable,
case when pk.column_id is not null then 'PK'
else '' end as primary_key,
case when fk.parent_column_id is not null then 'FK'
else '' end as foreign_key
from sys.tables as tab
left join sys.columns as col
on tab.object_id = col.object_id
left join sys.types as t
on col.user_type_id = t.user_type_id
left join (
select index_columns.object_id,
index_columns.column_id
from sys.index_columns
inner join sys.indexes
on index_columns.object_id = indexes.object_id
and index_columns.index_id = indexes.index_id
where indexes.is_primary_key = 1
) as pk
on col.object_id = pk.object_id
and col.column_id = pk.column_id
left join (
select fc.parent_column_id,
fc.parent_object_id
from sys.foreign_keys as f
inner join sys.foreign_key_columns as fc
on f.object_id = fc.constraint_object_id
group by fc.parent_column_id, fc.parent_object_id
) as fk
on fk.parent_object_id = col.object_id
and fk.parent_column_id = col.column_id
where tab.name = 'table_products'
order by primary_key desc, foreign_key desc, nullable asc, column_name asc;
Edit: Solved via comments showing that SQL Server Column Statistics already provide this type of information.
I have two tables. I need to find the column(s) that relate them. There are not any foreign keys. The primary key for 'CUSTTABLE' is 'ACCOUNTNUM' and for 'DIRPARTYTABLE' is 'RECID'. The related columns are 'CUSTTABLE.PARTY' and 'DIRPARTYTABLE.RECID'. I need this to return 'CUSTTABLE.ACCOUNTNUM', 'DIRPARTYTABLE.NAME', 'DIRPARTYTABLE.PARTYNUMBER' on the same line. The code I have returns column names that match. However, the names are often never the same in my case.
Is there a similar way to return the columns with matching values, not names? even if it returns multiple columns that would help. In reality, these tables have 100+ columns and I have many tables to determine the same type of relationship.
CUSTTABLE:
ACCOUNTNUM CUSTGROUP PARTY
305342 LTL 5637459693
305343 LTL 5637468513
305345 LTL 5637472531
305398 LTL 5637468514
305405 LTL 5637468515
DIRPARTYTABLE:
NAME PARTYNUMBER RECID
ZEP MFG 1500121 5637459693
TABER EXTRUSIONS 1500122 5637459694
LAWSON PRODUCTS 1500123 5637459695
KIMRAY 1500124 5637459696
ANCHOR PAINT MFG 1500125 5637459697
RESULT:
ACCOUNTNUM NAME PARTYNUMBER
305342 ZEP MFG 1500121
Query:
select A.COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS A
join INFORMATION_SCHEMA.COLUMNS B
on A.COLUMN_NAME = B.COLUMN_NAME
where A.TABLE_NAME = 'CUSTTABLE'
and B.TABLE_NAME = 'DIRPARTYTABLE'
Seems you are working on D365 Database, in that case, usually DIRPARTY table contains all the info i.e. Employee, Customer, Vendor etc.. these would be filtered by DIRPARTY.INSTANCERELATIONTYPE. For the Customers CUSTTABLE.PARTY = DIRPARTYTABLE.RECID while INSTANCERELATIONTYPE = 1155 or 5767, This is standard info, you may want verify INSTANCERELATIONTYPE match with your setup.
Regarding relationship identification, I would suggest you to go through form properties - query from D365 application it self, otherwise it would be difficult to predict the relationship without verifying actual data unless you have meta data document available, because sometime we cannot relay on one column for key relation. i.e. VENDOR.ACCOUNTID is not key column unless you consider VENDOR.ENTITYID or VENDOR.DATAAREAID.
However, following query might be helpful as starting point. You can find similar name column in various number of tables excluding the tables that doesn't have any records.
with CTE as
(
SELECT DB_ID () DatabaseID,
t.Name AS TableName,
p.rows AS RowCounts,
SUM(a.total_pages) * 8 AS TblTotalSizeKB,
SUM(a.used_pages) * 8 AS UsedSpaceKB
-- CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS UsedSpaceMB,
FROM
sys.tables as 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
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
--LEFT OUTER JOIN
-- sys.schemas s ON t.schema_id = s.schema_id
WHERE t.OBJECT_ID > 255 and p.rows > 0 --and T.name like '%WORKFLOWTRACKINGSTATUSTABLE%' -----------Enter TABLE name (search key word) here
GROUP BY T.name, p.Rows
)
select C.COLUMN_NAME,DATA_TYPE, CTE.*
from CTE
JOIN INFORMATION_SCHEMA.COLUMNS AS C on CTE.TableName = C.TABLE_NAME
WHERE COLUMN_NAME LIKE '%Hire%' -----------Enter COLUMN name (search key word) here
ORDER BY TABLE_NAME
GO
In article about sys.indexes there is a phrase that this view
Contains a row per index or heap of a tabular object, such as a table,
view, or table-valued function.
I was interested to find a size of such an index.
So I created function with index:
create function fIndexSize()
returns #res table
(
object_id int not null
, name varchar(128) not null
, primary key (object_id)
)
as
begin
insert into #res
select object_id, name
from sys.objects
where object_id > 255
return
end
Here we can see the name of new index:
There is also a record in sys.indexes:
Usually I get sizes of indexes using this query:
select
o.schema_id
, o.object_id
, o.name
, o.type_desc
, sum (a.total_pages) * 8.00 / 1024 / 1024 as TotalSpaceGB
from sys.objects o
inner join sys.indexes i on o.object_id = i.object_id
inner join sys.partitions p on i.object_id = p.object_id and i.index_id = p.index_id
inner join sys.allocation_units a on p.partition_id = a.container_id
where (o.name = 'fIndexSize' or i.name like 'PK__fIndexSi%')
group by o.schema_id, o.object_id, o.name, o.type_desc
But this time nothing was returned.
Can anyone give me advice how to find size of such an index?
Yes you can find the size of this index, but you should consider it's living only for a time of a batch and you should look for it in tempdb (as it is table variable):
create function fIndexSize()
returns #res table
(
object_id_xxxx int not null
, name varchar(128) not null
, primary key (object_id_xxxx)
)
as
begin
insert into #res
select object_id, name
from sys.objects
where object_id > 255
return
end;
go
select i.name,
c.name,
8 * SUM(au.used_pages) as size_kb
from tempdb.sys.indexes i
join tempdb.sys.columns c
on i.object_id = c.object_id
join tempdb.sys.partitions as p
on p.object_id = i.object_id and p.index_id = i.index_id
join tempdb.sys.allocation_units as au
on au.container_id = p.partition_id
where c.name = 'object_id_xxxx'
group by i.name,
c.name
I left the column name here only to show that the index found is what we are looking for, and I chose the column name with xxxx for distinguish it well
The result of a table-valued function is not stored in a permanent table in the database. It is generated on the fly during the query execution.
Yes, you have a row in sys.indexes which tells you index properties, like type (clustered or not), is_primary_key, is_unique, etc.
But, there are no corresponding rows in sys.partitions and in sys.allocation_units. That's why your query returns nothing. If you replace inner joins with left joins, you'd see one row with NULL as TotalSpaceGB.
So, documentation is correct. Documentation doesn't say that table-valued functions will have rows in sys.allocation_units.
Each invocation of the function may return different number of rows. This set of rows doesn't exist before the query runs and it doesn't exist after the query finishes.
Even during the function execution sys.partitions and sys.allocation_units are empty for this index (PK__fIndexSi...).
When I looked at the actual execution plan of the query
select * from fIndexSize()
I could see that optimiser creates a temp table behind the scenes. Well, it has to store the rows somewhere and they are stored in TempDB.
So, you should run your select from sys.allocation_units using tempdb.
At first I used SQL Sentry Plan Explorer to see the name of the temporary table:
Then I ran your query against TempDB:
I'm trying to get the last time a table was updated by the users:
Declare #Collect Table (Name Varchar(100),last_user_update datetime)
Insert into #Collect
EXEC sp_MSForEachTable 'SELECT ''?'' as TableName,
last_user_update
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID(''SP3D_DB_RESEARCH_MDB'') AND OBJECT_ID = OBJECT_ID(''?'')'
SELECT * FROM #Collect ORDER BY last_user_update DESC
The problem is that in the results, some tables are appearing 3 times (please see the image bellow)
Since it appears that all tables duplicated have the same last updated time. Is there any way to group the results by the table name?
If the values are indeed the same, you can just add DISTINCT to the query, and have it return unique results
SELECT DISTINCT ''?'' as TableName, last_user_update ...
If you want to group after the fact, and only the last update interests you, you can do
SELECT TableName, max(last_user_update) as last_update
FROM #Collect
GROUP BY TableName
ORDER BY 2 DESC
Tables can have multiple indexes. The dynamic management view sys.dm_db_index_usage_stats will have separate entries for each index.
If you want to see the index name for each one, try this:
SELECT
o.name as TableName,
i.name as IndexName,
istats.last_user_update
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
inner join sys.indexes i
on i.index_id = istats.index_id
and i.object_id = istats.object_id
order by
o.name,
i.name
Or, if you don't care about that and just want the last update time, you can group by the table name:
SELECT
o.name as TableName,
max(istats.last_user_update)
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
group by
o.name
You can do an insert directly into your table with this query:
declare #Collect table (Name varchar(100),last_user_update datetime)
insert into #Collect
select
o.name as TableName,
istats.last_user_update
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
inner join sys.indexes i
on i.index_id = istats.index_id
and i.object_id = istats.object_id
where database_id = db_id('SP3D_DB_RESEARCH_MDB')
Also, I'm not sure what your goal is, but please understand that this view only has entries for indexes that have activity on them. If an index is unused, it is not in this view. The first access creates a row in the view. The real interesting stuff on this view is the seek and scan information.
See this note from MSDN:
When an index is used, a row is added to sys.dm_db_index_usage_stats
if a row does not already exist for the index. When the row is added,
its counters are initially set to zero.
If your goal is to enumerate all the indexes and then show the last update date for all of them, you'll need to join to sys.indexes and then left join to sys.dm_db_index_usage_stats.
I am using SQL Server 2008 R2.
I have a table in which I have a column that have a not null constraint.
Now, what if I want to check if column has not null constraint defined or not for specific column?
Is there any query to find out it?
Thanks in advance..
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
This query will show all columns from all tables and a whole host of information on them. The column you would want is: IS_NULLABLE that can have the value 'YES' or 'NO'
COLUMNS (Transact-SQL)
Something like
SELECT o.name AS tab, c.name AS col, c.is_nullable
FROM sys.objects o
INNER JOIN sys.columns c ON c.object_id = o.object_id
WHERE o.name like '%yourtable%' and type = 'U'
See sys.columns and sys.objects
There's some catalog views you can use:
// information about check constraints
select * from sys.check_constraints
// information about specific columns
select name, is_nullable from sys.columns
// information about tables
select * from sys.tables
The sys.columns is_nullablefield holds information about nullability.
there is a table sys.all_columns
and a column in this table called is_nullable
http://technet.microsoft.com/en-us/library/ms177522(v=sql.105).aspx
select s.name, c.name, c.is_nullable from sys.tables s, sys.all_columns c
where s.object_id = c.object_id
and s.type = 'U' -- USER_TABLE
and c.is_nullable = 1
It is a simple command that will list - Field, Type, Null, Key, Default
SHOW FIELDS FROM Your_Table_Name;