How Many Ways Can You Say "Database" in Metadata?

The answer, in SQL Server 2008, is quite a few.  A day or so ago Paul Randall tweeted about the lack of consistency within the column naming in system objects that reference databases (id or name).  It got me to thinking about the ones I knew which naturally led to being bothered that there might be others I don't know about (sometimes I really wish I could turn that off, oh well).

So to satisfy my curiosity (and hopefully enlighten/entertain you) here is the path I took to get the answer.

First, query the catalog view sys.all_columns for columns containing db or database in their names.

SELECT c.name AS ColumnName
FROM sys.all_columns AS c
    INNER JOIN sys.all_objects AS o ON o.[object_id] = c.[object_id]
WHERE c.name LIKE '%db%' OR c.name LIKE '%database%'
GROUP BY c.name;

After a quick look through those results I boiled it down to a meaningful list and went back to sys.all_columns.  The query below returns 65 rows for my instance.

SELECT c.name AS ColumnName
    ,SCHEMA_NAME(o.[schema_id]) AS SchemaName
    ,o.name AS ObjectName
    ,o.type_desc
FROM sys.all_columns AS c
    INNER JOIN sys.all_objects AS o ON o.[object_id] = c.[object_id]
WHERE c.name IN(
                'database'
                ,'Database Name'
                ,'database_id'
                ,'database_name'
                ,'DatabaseID'
                ,'DatabaseName'
                ,'db_id'
                ,'db_name'
                ,'dbid'
                ,'dbname'
                ,'Master DBID'
                ,'orig_db'
                ,'referenced_database_name'
                ,'resource_database_id'
                ,'SourceDatabaseID'
                )
ORDER BY ColumnName
    ,SchemaName
    ,ObjectName;

There were a couple I had forgotten about but most items are DMVs.  Still some fun detail to have in the back of your head for the next time you need it.  Or just remember this blog post.  Either way. :)


Posted by: whitneyw
Posted on: 6/16/2009 at 11:59 PM
Tags: ,
Categories: BlogEngine.NET
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Transact-SQL To Get Index Page Counts For A Database

One of the attendees of my session on metadata last Saturday asked about how to get at this information.  We were already time constrained at 50 minutes per session so I was not able to fit that query in with the remaining items I wanted to show.

The following query starts with the catalog view sys.partitions.  This might throw people as they would assume that partitioning must be implemented to make use of this view.  One of the architectural changes made in SQL Server 2005 is that all tables are technically on a partition (the value being 1 if non-partitioned).  We also make use of sys.destination_data_spaces, sys.allocation_units, and a few other general catalog views.

SELECT sc.name AS SchemaName
    ,t.name AS ObjectName
    ,i.name AS IndexName
    ,i.type_desc AS IndexType
    ,f.name AS FileGroupName
    ,[rows] AS NumberOfRows
    ,au.total_pages AS TotalPages
FROM sys.partitions AS p
    INNER JOIN sys.tables AS t ON t.[object_id] = p.[object_id]
    INNER JOIN sys.schemas AS sc ON sc.[schema_id] = t.[schema_id]
    LEFT JOIN sys.indexes AS i ON p.[object_id] = i.[object_id]
        AND p.index_id = i.index_id
    LEFT JOIN sys.destination_data_spaces AS dds ON 
        dds.partition_scheme_id = i.data_space_id
        AND dds.destination_id = p.partition_number
    LEFT JOIN sys.filegroups AS f ON f.data_space_id = i.data_space_id
    LEFT JOIN (
                SELECT container_id, SUM(total_pages) AS total_pages
                FROM sys.allocation_units
                GROUP BY container_id
                ) AS au ON au.container_id = p.partition_id
WHERE i.index_id > 0
ORDER BY SchemaName, ObjectName, IndexName;

This query also returns the filegroup the particular index uses.  If you aren’t currently utilizing multiple filegroups for your database I would strongly recommend investigating that option.

Let me know if you have questions (or requests).  Enjoy…


Posted by: whitneyw
Posted on: 4/29/2009 at 10:00 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Transact-SQL To Return Default Constraint Values

I’ve been doing quite a bit of modeling lately and needed to go back and verify that I had the various default constraints as I wanted them on my tables.  In this case, I was looking for tables with constraint value using the getdate() system function.

Here is the T-SQL to get at the default constraint values:

SELECT SCHEMA_NAME(t.[schema_id]) AS SchemaName
    ,t.name AS TableName
    ,c.name AS ColumnName
    ,dc.[definition]
FROM sys.tables AS t
    INNER JOIN sys.columns AS c 
        ON c.[object_id] = t.[object_id]
    INNER JOIN sys.default_constraints AS dc 
        ON dc.[parent_object_id] = c.[object_id]
        AND dc.parent_column_id = c.column_id
WHERE t.is_ms_shipped = 0
    AND dc.definition = '(GETDATE())'
ORDER BY SchemaName
    ,TableName
    ,ColumnName;

As an aside, I’ve been using the super handy new datetimeoffset data type everywhere I had normally used datetime.  The default constraint you would want to implement with datetimeoffset is sysdatetimeoffset() instead of getdate().

I really wish this had been implemented back in the 2005 product, I would have written a lot less code to do time zone to time zone conversion.  Oh well, progress is good whenever it comes.

Enjoy!


Posted by: whitneyw
Posted on: 4/21/2009 at 10:06 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed

Transact-SQL Character Detection

I was helping a colleague out with some data migration today and got started discussing methods of finding data that would cause problems.  In today’s case he needed to verify the phone numbers contained in a database table didn’t contain a couple values that would break later application formatting.  Here is a simple example of the data (note the row constructor if trying this in pre SQL 2008 versions):

CREATE TABLE #PhoneNumbers
(
PhoneNumber    nvarchar(30)
);
INSERT #PhoneNumbers (PhoneNumber)
VALUES ('11122223333')
    ,('111.222.3333')
    ,('111-222-3333')
    ,('111 222 3333')
    ,('111/222/3333');

Now we could simply apply this query to test for any character data in our phone number:

SELECT *
FROM #PhoneNumbers
WHERE ISNUMERIC(PhoneNumber) = 1;

But this doesn’t really get at what we want.  In this case, we only care about certain characters.  To our aid comes the trusty PATINDEX() function, allowing us to write this query:

SELECT *
FROM #PhoneNumbers
WHERE PATINDEX('%[. /]%', PhoneNumber) > 0;

This allows us to ignore phone numbers with “-“ as they don’t offend our application’s sensibilities.  The same query can be used later with a REPLACE() as the basis for an update to remove the invalid characters.  Good times all around.


Posted by: whitneyw
Posted on: 4/21/2009 at 10:05 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Transact-SQL To Return Object Parameter Properties

I helped a fellow Magenicon with this detail today and thought it might be helpful to others.  Note, this will only work in instances that are SQL 2005 or greater.

SELECT SCHEMA_NAME(o.[schema_id]) AS SchemaName
    ,o.name AS ObjectName
    ,REPLACE(o.type_desc, '_', ' ') AS ObjectType
    ,ap.parameter_id AS ParameterNumber
    ,ap.name AS ParameterName
    ,CASE
        WHEN t.name IN ( 'char', 'nchar', 'varchar'
                        , 'nvarchar', 'binary', 'image'
                        , 'varbinary' ) THEN t.name 
            + ' (' + CONVERT(varchar(10), ap.max_length) + ')'
        WHEN t.name IN ( 'bigint', 'bit', 'int', 'money'
                        , 'smallint', 'smallmoney', 'tinyint'
                        , 'float', 'real', 'date', 'datetime2'
                        , 'datetime', 'smalldatetime', 'time'
                        , 'cursor', 'hierarchyid', 'sql_variant'
                        , 'table', 'timestamp', 'uniqueidentifier'
                        , 'xml', 'sysname', 'text', 'ntext' ) THEN t.name
        WHEN t.name = 'datetimeoffset' THEN t.name 
            + ' (' + CONVERT(varchar(10), ap.scale) + ')'
        WHEN t.name IN ('decimal', 'numeric') THEN t.name 
            + ' (' + CONVERT(varchar(10), ap.[precision]) 
            + ', ' + CONVERT(varchar(10), ap.scale) + ')'
        ELSE 'Unknown'
    END AS DataType
    ,CASE
        WHEN ap.is_output = 1 THEN 'Yes'
        ELSE 'No'
    END AS IsOutputParameter
    ,COALESCE(ap.default_value, 'N/A') AS DefaultValue
FROM sys.all_parameters AS ap
    INNER JOIN sys.objects AS o ON o.[object_id] = ap.[object_id]
    INNER JOIN sys.types AS t ON t.system_type_id = ap.system_type_id
ORDER BY SchemaName
    ,ObjectName
    ,ParameterNumber;

Posted by: whitneyw
Posted on: 4/7/2009 at 1:10 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Split a string in Transact SQL (without looping)

During the course of two days in the MSDN TSQL forums I must have seen 10 requests that were basically the same issue.  Some string of values is passed from your application and you need to split them into the individual items.  Here is a quick implementation that will get you a string splitting inline function that requires no recursion.

First, we need to create a numbers table that we will later utilize for string parsing.  I’m using a version Itzik Ben-Gan’s number table that he posts frequently in SQL Mag articles.

-- Create and populate an auxiliary table of numbers (1,000,000 entries)
IF OBJECT_ID('dbo.Numbers') IS NOT NULL 
DROP TABLE dbo.Numbers;
GO
CREATE TABLE dbo.Numbers
(
number INT NOT NULL CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED
);

SET NOCOUNT ON;

DECLARE
  @max AS INT,
  @rc AS INT;

SET @max = 1000000;
SET @rc = 1;

BEGIN TRAN;
  INSERT INTO dbo.Numbers(number) VALUES(1);

  WHILE @rc * 2 <= @max
  BEGIN
    INSERT INTO dbo.Numbers(number)
      SELECT number + @rc 
      FROM dbo.Numbers;

    SET @rc = @rc * 2;
  END

  INSERT INTO dbo.Numbers(number)
    SELECT number + @rc 
    FROM dbo.Numbers
    WHERE number + @rc <= @max;
COMMIT TRAN; 
GO

Next, we create the inline function that will do the heavy lifting for us.  I’ve added an input for separator as hard coding the comma almost always guarantees some other character will later be needed.

IF OBJECT_ID('dbo.SplitString', 'IF') IS NOT NULL
DROP FUNCTION dbo.SplitString;
GO
CREATE FUNCTION dbo.SplitString(@arr AS VARCHAR(8000), @sep AS CHAR(1))
  RETURNS TABLE
AS
RETURN
  SELECT
    (number - 1) - LEN(REPLACE(LEFT(@arr, number - 1), @sep, '')) + 1 AS pos,
    SUBSTRING(@arr, number, CHARINDEX(@sep, @arr + @sep, number) - number) AS element
  FROM dbo.Numbers
  WHERE number <= LEN(@arr) + 1
    AND SUBSTRING(@sep + @arr, number, 1) = @sep;
GO

Finally, we call our function with a string of values.  If we needed to then utilize this for filtering we could use the CROSS APPLY operator introduced in SQL 2005.

DECLARE @arr varchar(8000);
DECLARE @sep char(1);

SET @arr = '1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C';
SET @sep = ',';

SELECT *
FROM dbo.SplitString(@arr, @sep);
This will give us a two row result set containing a position and value.  Simple, easy, and set based to boot!  And now I can just reference this post instead of cutting and pasting code.  :)

Posted by: whitneyw
Posted on: 10/10/2008 at 10:48 PM
Tags:
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

How to quickly get stored procedure text

I've answered this question twice today so I thought it made good sense to blog it.  I am going to give three examples that will quickly get you to the text of a stored procedure or other object type.  I am assuming you are looking to skip the [Database Name] --> Programmability --> Stored Procedures path of Object Browser, although the new "DROP and CREATE to" option in SQL 2008 is nice.  We will be using the following methods today:

  • OBJECT_DEFINITION()
  • sys.sql_modules
  • sp_helptext

First up is OBJECT_DEFINITION().  This system function takes an object_id and returns a varchar(max) output of the object source text.  There are multiple types that can be used with OBJECT_DEFINITION, check the BOL reference for a complete list.  The syntax for this function is:

 

SELECT OBJECT_DEFINITION(OBJECT_ID(N'dbo.GetCustomer', 'P')) AS objectText;

 

Next up is the object catalog view sys.sql_modules.  This view returns a row for each object that is a SQL language-defined module.  Again, check the BOL reference for your complete list of types.  This view returns many useful pieces of information but today we are only after the definition column, which contains the SQL text of the module.  Here's the simple query:

 

SELECT [definition] AS objectText FROM sys.sql_modules WHERE [object_id] = OBJECT_ID(N'dbo.GetCustomer', 'P');

 

Last is the old standby sp_helptext.  This system stored procedure takes and object name (and potentially a column name) and returns the definition of multiple object types with a 255 character row output.  The source for this stored procedure is sys.sql_modules, referenced above.  Here is a sample call to this stored procedure:

EXECUTE sp_helptext @objName = N'dbo.GetCustomer';

The one additional option provided by sp_helptext is the ability to return computed column definitions.  To get that detail we simply add the columnname parameter:

 

EXECUTE sp_helptext @objName = N'dbo.Customer', @columnname = 'CustomerComputedColumn';

 

So the next time you have that "what does this object do?" moment give one of these options a try.


Posted by: whitneyw
Posted on: 7/22/2008 at 11:07 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

SQL Basics - Finding identity columns

After a bit of delay I'm back with the last installment of SQL Basics. I planned to write this blog several times over the last few days but to be honest I had other interests. My daughter said her first words this week, which just happened to be "dada", and has wanted to play all evening. How do you say no to that?

So today I'm talking about identity columns and how to find them in your database. I'm not going to get into the debate over whether or not you should use identity columns, I'm just pointing you to where they are. :)

If you read the last two posts you will recognize that we are headed down similar paths for finding identity columns. We will be focusing on a couple of the catalog views as well as making use of a system function. For the purposes of this blog I'm returning the following detail from each query:

  • Table name
  • Column name
  • Data type
  • Current identity value

Option 1 is sys.identity_columns, a catalog view returning a row for each object with an identity column. This view was introduced in SQL 2005 and inherits from the sys.columns catalog view. The syntax is:

Option 2 is sys.columns, a catalog view returning a row for all objects that have columns. The syntax for this view is:

Option 3 is sys.syscolumns, a compatibility view returning a row for each table, view, and stored procedure in your database. The syntax for this view is:

Option 4 makes use of the OBJECTPROPERTY system function. This function returns a wealth of information about objects. Note that the scope of this function is database specific. The one drawback to this option is that the return of the column name and data type are not easily done without a join to one of the objects above, which would defeat the point. But if you are only looking for tables that contain identity columns this is a good option. Here is the syntax:

So there are four ways to get at identity columns in your database. I hope this series of posts has been helpful. If there are other topics you would like to see drop me an email, otherwise we headed back to SQL 2008 in anticipation of next week's launch event here in Atlanta.

-W


Posted by: whitneyw
Posted on: 4/23/2008 at 11:29 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

SQL Basics - Finding tables

We are in part two of our four part series around basic operations in TSQL.  Today we will be finding tables based on certain criteria.  Here are a couple I will show:

  • Finding table names matching a string
  • Finding tables containing a certain data type
  • Excluding system objects from queries for tables

The first item is pretty straightforward and mirrors our prior discussion on columns.  I will show you four ways to query for a list of tables matching a string.

Option 1 uses the sys.tables catalog view.  This view returns a row for each table object, currently user tables only.  The syntax is:

SELECT * FROM sys.tables WHERE name LIKE 'Product%';

Option 2 uses the sys.objects catalog view.  This view returns a row for each user defined object in a database.  The syntax is:

SELECT * FROM sys.objects WHERE type = 'U' AND name LIKE 'Product%';

Option 3 uses the sys.sysobjects compatibility view.  This view contains one row for each object in a database.  This view is created for backward compatibility, so its future is not guaranteed.  The syntax is:

SELECT * FROM sys.sysobjects WHERE type = 'U' AND name LIKE 'Product%';

Option 4 uses the INFORMATION_SCHEMA.TABLES view.  This view returns one row for each table in the current database that can be accessed by the current user in the current database.  The syntax is:

SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'Product%';

To find tables containing certain data types we have two options.  The sys.tables, sys.objects, or sys.sysobjects views could be used in the manner below (swapping out [object_id] for [id] if using sys.sysobjects). 

SELECT t.name AS tableName ,c.name AS columnName ,ty.name AS dataType FROM sys.tables AS t INNER JOIN sys.columns AS c ON c.[object_id] = t.[object_id] INNER JOIN sys.types AS ty ON ty.system_type_id = c.system_type_id WHERE ty.name IN('varchar', 'nvarchar');

The same thought could be expressed a little differently against INFORMATION_SCHEMA.  The syntax is:

SELECT * FROM INFORMATION_SCHEMA.TABLES AS T INNER JOIN INFORMATION_SCHEMA.COLUMNS AS C ON C.TABLE_NAME = T.TABLE_NAME WHERE C.DATA_TYPE IN('varchar', 'nvarchar');

For certain searches for tables you might want to exclude the system objects stored in meta data.  One method for this I have already mentioned, indirectly, is the sys.tables compatibility view.  For now it only contains user defined objects.  It does, however, contain a very important column named is_ms_shipped.  This column also exists in sys.objects and allows us to effectively filter from those tables.  Your syntax would simply be:

SELECT * FROM sys.tables WHERE is_ms_shipped = 0;

or

SELECT * FROM sys.objects WHERE is_ms_shipped = 0;

An additional option is to make use of the OBJECTPROPERTY function.  This would allow us to use sys.sysobjects as well.  The syntax would be:

SELECT * FROM sys.sysobjects WHERE type = 'U' AND OBJECTPROPERTY(id, 'IsMSShipped') = 0;

Up next, identity columns.  I hope this is helpful for everyone, if not at least it will be over quickly.  :)


Posted by: whitneyw
Posted on: 4/16/2008 at 11:31 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

SQL Basics - Finding a given column

I have decided to step away from the SQL 2008 CTP for a bit and post a couple items that keep coming up.  I have been spending more time in the MSDN Transact SQL forums and have noticed a consistent stream of questions around some basic operations with transact SQL.  After a conversation with a client this week that touched on the same subject  I thought "I smell a blog series".  :)

So I have decided to post a blog a day this week on what I'm calling "SQL Basics".  I will be covering the following items:

  • Finding a given column by name
  • Finding tables based on a certain criteria (i.e. name, containing certain data types, etc.)
  • Finding identity columns
  • String concatenation tricks

So with those topics in mind, let's get rolling.  Today I will show you four ways to find a given column in your database by name.

Option 1 uses the sys.columns catalog view.  This view returns a row for each column of an object that has columns, these include:

  • Table valued assembly functions
  • Inline table-valued functions
  • Internal tables
  • System tables
  • Table-valued SQL functions
  • User tables
  • Views

The syntax is:

--Option 1
SELECT OBJECT_NAME(object_id) AS objectName
    ,name AS columnName
FROM sys.columns
WHERE name = 'ProductID';

Option 2 uses the sys.syscolumns compatibility view.  This view returns a row for every column in every table and view, as well as a row for each parameter in a stored procedure in the database.  This view is created for backward compatibility, so its future is not guaranteed.

The syntax is:

--Option 2
SELECT OBJECT_NAME(id) AS objectName
    ,name AS columnName
FROM sys.syscolumns
WHERE name = 'ProductID';

Option 3 utilizes the sys.all_columns object catalog view.  This view shows the columns of all user-defined and system objects.

The syntax is:

--Option 3
SELECT OBJECT_NAME(object_id) AS objectName
    ,name AS columnName
FROM sys.all_columns AS ac
WHERE name = 'ProductID';

Option 4 uses the the INFORMATION_SCHEMA.COLUMNS view.  This view returns one row for each column that can be accessed by the current user in the current database.

The syntax is:

--Option 4
SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'ProductID';

So there are four ways to get around in meta data when you need to do a column search.  Take your time to expand the queries provided and notice the wealth of information provided to you in the various views.  Happy hunting!


Posted by: whitneyw
Posted on: 4/15/2008 at 11:32 PM
Tags:
Categories: Blog
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed