Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « Fix Execution Plan showing ParallelismSQL Saturday Iowa another success! »
    comments

    Something that came out of a recent session I gave at SQL Saturday in Iowa was a discussion on views and the Meta Data that comes along with them. The discussion came about when I had commented, during a session that views were a pain spot for me. Misuse by means of over using them as well as seeing over the years, very little attention to the Meta Data of them. I wanted to give a little more information on the discussion here.

    Non-schema Bound Views in SQL Server rely on returning accurate and reliable results based from the underlying metadata. This is mostly in part to dependencies. With Schema-Bound Views this isn’t as much of a concern since underlying objects cannot be altered without errors being generated. This is the definition of binding them together. With Non-schema Bound Views however, this isn’t the case. In fact, we can create views without the underlying objects ever being in existence in some cases. Before we go into views and maintaining them, let’s dig into the underlying object references we just mentioned.

    How to look at your dependencies?

    In order to review dependencies in SQL Server 2008+, we can use sys.sql_expression_dependencies. This Catalog View returns each dependency on an object created by a user. From BOL, this falls under the following listing

    • Schema-bound entities.
    • Non-schema-bound entities.
    • Cross-database and cross-server entities. Entity names are reported; however, entity IDs are not resolved.
    • Column-level dependencies on schema-bound entities.
    • Column-level dependencies for non-schema-bound objects can be returned by using sys.dm_sql_referenced_entities
    • Server-level DDL triggers when in the context of the master database.

    Reference: http://msdn.microsoft.com/en-us/library/bb677315.aspx

    Of course seeing this means much more. Let’s take a look at AdventureWorks2008. The view HumanResources.vEmployee as the following definition

    1. SELECT
    2. e.[EmployeeID],c.[Title],c.[FirstName],c.[MiddleName],c.[LastName]
    3. ,c.[Suffix],e.[Title] AS [JobTitle] ,c.[Phone],c.[EmailAddress]
    4. ,c.[EmailPromotion],a.[AddressLine1],a.[AddressLine2],a.[City]
    5. ,sp.[Name] AS [StateProvinceName] ,a.[PostalCode],cr.[Name] AS [CountryRegionName]
    6. ,c.[AdditionalContactInfo]
    7. FROM [HumanResources].[Employee] e
    8. INNER JOIN [Person].[Contact] c ON c.[ContactID] = e.[ContactID]
    9. INNER JOIN [HumanResources].[EmployeeAddress] ea ON e.[EmployeeID] = ea.[EmployeeID]
    10. INNER JOIN [Person].[Address] a ON ea.[AddressID] = a.[AddressID]
    11. INNER JOIN [Person].[StateProvince] sp ON sp.[StateProvinceID] = a.[StateProvinceID]
    12. INNER JOIN [Person].[CountryRegion] cr ON cr.[CountryRegionCode] = sp.[CountryRegionCode];

    Looking at this view’s definition we can see that several columns are referred to from the tables. SQL Server tracks these dependencies between the objects by name.

    For example, take the view vEmployee. As the definition shows, the tables Employee, Contact, EmployeeAddress, Address, StateProvince and CountryRegion. Using sys.sql_expression_dependencies this can also be reviewed with a short query.

    1. SELECT
    2.     OBJECT_NAME(referencing_id) AS referencing_entity_name
    3.     ,referenced_server_name AS server_name
    4.     ,referenced_database_name AS database_name
    5.     ,referenced_schema_name AS schema_name
    6.     ,referenced_entity_name
    7. FROM sys.sql_expression_dependencies
    8. Where OBJECT_NAME(referencing_id) = 'vEmployee'

    Resulting in

    The same tables listed in the referenceing_id and using the OBJECT_NAME to return the objects name for more meaningful information. Reversing this, in a sense, and adding the sys.objects catalog view, we can start by looking deeper into the dependencies and focus on one of the tables the view is referencing.

    1. SELECT
    2.     depends.referenced_entity_name,
    3.     OBJECT_NAME(depends.referencing_id) AS referencing_entity_name,
    4.     objs.type_desc AS [Object Type]
    5. FROM sys.sql_expression_dependencies AS depends
    6. INNER JOIN sys.objects AS objs ON depends.referencing_id = objs.object_id
    7. WHERE
    8. referenced_id = OBJECT_ID(N'HumanResources.Employee')
    9. And OBJECT_NAME(referencing_id) = N'vEmployee';

    We now see that relationship in the results between the table and the view in a reverse reference based on the same dependency view. This can be taken a bit further in looking to the columns that are only EmployeeID.

    1. SELECT
    2. g.referenced_schema_name,
    3. g.referenced_entity_name,
    4. g.referenced_minor_name,
    5. OBJECT_NAME(a.referencing_id) View_Name,
    6. g.referenced_minor_id
    7. FROM sys.sql_expression_dependencies a
    8. CROSS APPLY sys.dm_sql_referenced_entities(OBJECT_SCHEMA_NAME(referencing_id) + '.' + OBJECT_NAME(referencing_id), 'OBJECT') g
    9. WHERE OBJECT_NAME(a.referencing_id) = N'vEmployee'
    10. AND g.referenced_minor_id > 0
    11. AND referenced_minor_name = 'EmployeeID'
    12. group by g.referenced_schema_name,
    13. g.referenced_entity_name,
    14. g.referenced_minor_name,
    15. OBJECT_NAME(a.referencing_id),
    16. g.referenced_minor_id
    17. ORDER BY g.referenced_minor_id

    The results of this query can also be quickly obtained by using sys.objects, sys.schemas and the same sys.dm_sql_referenced_entities. All of these catalog views expose the metadata that has been stored on this view. The above shows the referenced_minor_id a numbering system in the view and relationship to the tables. This is where we get into trouble.
    Take this short and the most common problem with this metadata and dependency issue. Run the statements as they are, in order.

    1. CREATE TABLE tbl (col INT, txt varchar(5))
    2. GO
    3.  
    4. CREATE VIEW dbo.vTbl
    5. AS
    6. SELECT * FROM tbl
    7. GO
    8.  
    9. SELECT * FROM sysdepends where id = OBJECT_ID('dbo.vTbl')
    10.  
    11. DROP TABLE tbl
    12. GO
    13.  
    14. CREATE TABLE tbl (txt varchar(5), col INT)
    15. GO
    16. INSERT INTO tbl VALUES ('test2',2)
    17. GO
    18.  
    19. SELECT * FROM vTbl

    The results would be expected to show 2, test2 but they show the opposite, test2, 2. We will talk about the query in the middle of this batch looking at the results of sysdepends in a minute.

    So we can see the INT value is in the place holders of the varchar value. Imagine this with calculations being run on this view. The view is replying on the metadata already created basedoff the original ordinal order of the columns in tbl. This exact example is only possible with the use of the wildcard * but other metadata oddities can happen do to the naming resolutions.

    Look at the table and view just created with the queries from earlier and verify the dependencies.

    Nothing is jumping out until we look at the system view sysdepends and the results that were shown from the batch earlier. Those results are shown below

    Now, run the sysdepends check again
    e.g. SELECT * FROM sysdepends where id = OBJECT_ID('dbo.vTbl')

    WHOA!!! Yes, we have a problem here and actually one of the points where the problem starts with what we tested above. Now we’ve identified this one reaction to the underlying table changes but there are more. Several issues can come from a non-schema bound view and the changes to underlying tables. Mostly the wildcard usage is the worst case practice. Like we said earlier, image calculations being run off the wrong column when the view is using an underlying definition that has been set to calculate things on specific columns. We could fall into sales reporting incorrectly, shipments going to the wrong locations and a mess of other possibilities.

    How to protect from this?

    In the case where views are used heavily, or even at all, coupled by developers or DBAs having the ability to change and alter anything on the database, get in the habit of refreshing the metadata for views weekly (even nightly if heavy usage is on your database). To do this we can use sp_refreshview. This procedure will force a validation and update to the persistent metadata of the view. Sp_refreshview takes a parameter of the view name to update.

    Run the following sp_refreshview and recheck sysdepends

    1. sp_refreshview 'dbo.vTbl'
    2. GO
    The results show that sysdepends now map correctly again.

    To make this more usable in a large system or where many views are managed, a script like below can be used.

    1. DECLARE @viewname NVARCHAR(255)
    2. DECLARE @looper INT = 1
    3. IF OBJECT_ID('tempdb..#viewnames') IS NOT NULL
    4. BEGIN
    5. DROP TABLE #viewnames
    6. END
    7. SELECT
    8. s.[name] + '.' + v.[name] vname,
    9. ID = ROW_NUMBER() OVER (PARTITION BY v.[type_desc] ORDER BY v.[name])
    10. INTO #viewnames
    11. FROM sys.views v
    12. JOIN sys.schemas s ON v.schema_id = s.schema_id
    13. WHERE OBJECTPROPERTY(OBJECT_ID, 'IsSchemaBound') = 0
    14. WHILE @looper <= (SELECT COUNT(*) FROM #viewnames)
    15. BEGIN
    16. SET @viewname = (SELECT vname FROM #viewnames WHERE ID = @looper)
    17. EXEC SP_REFRESHVIEW @viewname
    18. PRINT 'Exec sp_refreshview ''' + @viewname + ''''
    19. SET @looper += 1
    20. END

    Reference: http://wiki.lessthandot.com/index.php/Sp_refreshview_for_all_views_in_a_database

    Closing

    Don’t forget the views! They can be overused and become an extreme pain point for a DBA but they can be managed. Now if the view usage becomes to the point you have developers nesting view after view, rethink the designs and how you are obtaining your data.

    About the Author

    Ted Krueger is a SQL Server MVP and has been working in development and database administration for 13+ years. Specialties range from High Availability and Disaster / Recovery setup and testing methods down to custom assembly development for SQL Server Reporting Services. Ted blogs and is also one of the founders of LessThanDot.com technology community. Some of the articles focused on are Backup / Recovery, Security, SSIS and working on SQL Server and using all of the SQL Server features available to create stable and scalable database services. @onpnt
    Social SitingsTwitterLinkedInLTD RSS Feed
    663 views
    Instapaper

    2 comments

    Comment from: SQLDenis [Member] Email
    10/06/11 @ 10:04
    Comment from: Ted Krueger (onpnt) [Member]
    Ted Krueger (onpnt) Durn it! Sorry, didn't remember you had that one up there
    10/06/11 @ 10:15

    Leave a comment


    Your email address will not be revealed on this site.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)