Tag: system catalog views

TIE Fighters and sys.types: “There’s Too Many of Them!”

Y-Wing As the Y-Wing pilot in Star Wars: Episode VI – Return of the Jedi proclaimed, sometimes there’s too many of them. He was referring to TIE Fighters during the assault on the second Death Star. I echoed the sentiment while querying the system catalog for a client’s database. I was building the foundation for a data dictionary (at the end of the project, unfortunately) and started by pulling a list of all the fields in the database along with their data types. I was dismayed when my tables returned more records than there were fields. There were too many of them.

To show why this happenned, I will start by creating a simple table.

 

CREATE TABLE dbo.TooManyOfThem

(

      RecordID int NOT NULL

    , RecordName nvarchar(100) NULL

)

 

Notice that I have an nvarchar field in there. It turns out, that was the culprit. Let’s query the system catalog to return the columns from this table along with their associated data types.

SELECT

      o.name AS TableName

    , c.name As ColumnName

    , t.name AS DataTypeName

FROM sys.columns c

INNER JOIN sys.objects o

ON o.object_id = c.object_id

INNER JOIN sys.types t

ON t.system_type_id = c.system_type_id 

WHERE o.name = 'TooManyOfThem'

 

Since my TooManyOfThem table has two columns, I would expect to get two records returned by this query. Here’s is what I get.

TableName            ColumnName      DataTypeName

-------------------- --------------- ------------

TooManyOfThem        RecordID        int

TooManyOfThem        RecordName      nvarchar

TooManyOfThem        RecordName      sysname 

 

(3 row(s) affected)

 

I have a bad feeling about this. These aren’t the results I’m looking for. Are my fields riding single file to hide their numbers?

My RecordName field is showing up more than once. Notice that the second occurrence has DataTypeName of “sysname.” I reached out with my feelings and modified my query to get more fields returned.

SELECT

      o.name AS TableName

    , c.name As ColumnName

    , c.system_type_id AS C_system_type_id

    , c.user_type_id AS C_user_tpe_id

    , t.system_type_id AS T_system_type_id

    , t.user_type_id AS T_user_type_id

    , t.name AS DataTypeName

FROM sys.columns c

INNER JOIN sys.objects o

ON o.object_id = c.object_id

INNER JOIN sys.types t

ON t.system_type_id = c.system_type_id 

WHERE o.name = 'TooManyOfThem'

 

When I run this new query, I get the following results.

TableName            ColumnName      C_system_type_id C_user_tpe_id T_system_type_id T_user_type_id DataTypeName

-------------------- --------------- ---------------- ------------- ---------------- -------------- ------------

TooManyOfThem        RecordID        56               56            56               56             int

TooManyOfThem        RecordName      231              231           231              231            nvarchar

TooManyOfThem        RecordName      231              231           231              256            sysname

 

(3 row(s) affected)

 

We see that in sys.types, the system_type_id of 231 leads to both “nvarchar” and “sysname.” Let’s write a query to return the types whose system_type_id values appear more than once.

SELECT

      system_type_id

    , user_type_id

    , name

FROM sys.types

WHERE system_type_id IN

(

SELECT

      system_type_id

FROM sys.types

GROUP BY system_type_id

HAVING COUNT(*) > 1

)

We get the following results.

system_type_id user_type_id name

-------------- ------------ ---------------

240            128          hierarchyid

240            129          geometry

240            130          geography

231            231          nvarchar

231            256          sysname

 

(5 row(s) affected)

 

As we see, the system_type_id is not unique in sys.types. The user_type_id, however, does not repeat. So, I change my join to the sys.types view from system_type_id to user_type_id.

SELECT

      o.name AS TableName

    , name As ColumnName

    , name AS DataTypeName

FROM sys.columns c

INNER JOIN sys.objects o

ON o.object_id = c.object_id

INNER JOIN sys.types t

ON t.user_type_id = c.user_type_id 

WHERE o.name = 'TooManyOfThem'

 

Now I get the results I’m looking for.

TableName            ColumnName      DataTypeName

-------------------- --------------- ------------

TooManyOfThem        RecordID        int

TooManyOfThem        RecordName      nvarchar

 

(2 row(s) affected)

 

So, the path you choose will certainly affect your destiny. Here’s another path for you: Robot Chicken Star Wars. Seriously. It’s some of the funniest stuff I’ve ever seen.

 

 

 

 

 

Finding Foreign Keys

No, this is not a new initiative by United States Immigration and Customs Enforcement. I am talking databases.

I recently found myself in a situation where I wanted to find all the tables that referenced a particular table via foreign key relationships. I had heard that relying on the built-in dependency tracking (right-clicking the object in SSMS and choosing View Dependencies) was not always reliable. Indeed, SQL Server MVP Aaron Bertrand (Twitter) wrote a great blog post about ways in which sysdepends, sp_depends, and sys.sql_dependencies can lose touch with reality.

I figured I could find the info I wanted by querying the system catalog views. Here is the script I wrote using AdventureWorks as the sample DB.

USE AdventureWorks

GO

 

DECLARE 

      @ReferencedSchema varchar(1000)

    , @ReferencedObject varchar(1000)

 

SET @ReferencedSchema = 'HumanResources'

SET @ReferencedObject = 'Employee'

 

SELECT 

      rs.name + '.' + ro.name AS ReferencedObject

    , rc.name AS ReferencedColumn

    , ps.name + '.' + po.name AS ParentObject

    , pc.name AS ParentColumn

    , co.name AS ConstraintName

FROM sys.foreign_key_columns fkc

INNER JOIN sys.objects co

ON co.object_id = fkc.constraint_object_id

INNER JOIN sys.objects ro

ON ro.object_id = fkc.referenced_object_id

INNER JOIN sys.schemas rs

ON rs.schema_id = ro.schema_id

INNER JOIN sys.columns rc

ON rc.object_id = ro.object_id

AND rc.column_id = fkc.referenced_column_id

INNER JOIN sys.objects po

ON po.object_id = fkc.parent_object_id

INNER JOIN sys.schemas ps

ON ps.schema_id = po.schema_id

INNER JOIN sys.columns pc

ON pc.object_id = po.object_id

AND pc.column_id = fkc.parent_column_id

WHERE rs.name = @ReferencedSchema

AND ro.name = @ReferencedObject

ORDER BY 

      ps.name

    , po.name

 

Here are the results I get.

image

You can see that there are 7 foreign key constraints that reference the HumanResources.Employee table in my copy of Adventureworks. You can see that Parent object to which each of these constraints belong. And you can see the actual name of each constraint.

Feel free to take that query and tweak it to add information you would find useful. Also feel free to post it in comments here. I really want my blog to be a two-way street and would love to encourage debate.

I also encourage you to play around with querying the system catalog views. You can find some really cool stuff in there.