Generating 6-Digit All-Numeric File Numbers for Search - 13 Jul 2006

The July Reader Challenge, "Creating an Easy Query Mechanism for Gathering Customer Data," had no winners. Here’s a recap of the problem and the solution to the July Reader Challenge.

6 Min Read
Generating 6-Digit All-Numeric File Numbers for Search - 13 Jul 2006

The July Reader Challenge, "Creating an Easy Query Mechanism for Gathering Customer Data," had no winners. Here’s a recap of the problem and the solution to the July Reader Challenge.

Problem:

Adam is a DBA for a hosting company that provides free and paid database access for its customers. The databases reside on SQL Server 2000 instances. Each instance can host up to 100 databases and contains a dbmaster database, which collects and maintains various statistics from the databases. A database-creation utility creates each customer's database and an AdminConfig table in each database. The AdminConfig table maintains different configuration information that the customer creates.

For ad-hoc reporting purposes or as part of a monitoring application, Adam often queries the AdminConfig information from each hosted database. Help Adam create a simple query mechanism in the dbmaster database while adhering to the following requirements:

  1. The query mechanism should be efficient and easy to maintain.

  2. The query mechanism should let Adam easily query the AdminConfig table on each hosted database.

  3. The database name must pass as a parameter.

  4. If the database name is NULL, the query should return the AdminConfig-table data from all the hosted databases.

  5. Adam should be able to use a SELECT statement to join the query results with other tables.

To create the dbmaster database and several databases with the AdminConfig table, use the is as following sample setup:

CREATE DATABASE dbmaster  CREATE DATABASE db1  CREATE DATABASE db2  CREATE DATABASE db3  GO  CREATE TABLE db1.dbo.AdminConfig ( id INT NOT NULL PRIMARY KEY, configname VARCHAR(30) NOT NULL, dbname AS db_name() )  INSERT INTO db1.dbo.AdminConfig VALUES(1, 'a')  CREATE TABLE db2.dbo.AdminConfig ( id INT NOT NULL PRIMARY KEY, configname VARCHAR(30) NOT NULL, dbname AS db_name() )  INSERT INTO db2.dbo.AdminConfig VALUES(1, 'a')  CREATE TABLE db3.dbo.AdminConfig ( id INT NOT NULL PRIMARY KEY, configname VARCHAR(30) NOT NULL, dbname AS db_name() )  INSERT INTO db3.dbo.AdminConfig VALUES(1, 'a')  GO

Solution:

To get the results from each hosted database's AdminConfig table, Adam can use the UNION operator in the following query:

USE dbmaster  GO  SELECT id, configname, dbname FROM db1.dbo.AdminConfig  UNION ALL  SELECT id, configname, dbname FROM db2.dbo.AdminConfig  UNION ALL  SELECT id, configname, dbname FROM db3.dbo.AdminConfig  GO

To parameterize the dbname option, Adam can use an inline table-valued function. The following code shows the definition of the table-valued function:

USE dbmaster  GO  CREATE FUNCTION GetAdminConfig (@db nvarchar(128) = NULL)  RETURNS TABLE  AS  RETURN (      SELECT id, configname, dbname FROM db1.dbo.AdminConfig WHERE @db = N'db1' OR @db IS NULL      UNION ALL      SELECT id, configname, dbname FROM db2.dbo.AdminConfig WHERE @db = N'db2' OR @db IS NULL      UNION ALL      SELECT id, configname, dbname FROM db3.dbo.AdminConfig WHERE @db = N'db3' OR @db IS NULL  )  GO

In this code, the GetAdminConfig table-valued function retrieves the AdminConfig table from the specified database or, if the database name is unspecified, from all the hosted databases. The search predicate uses the @db variable, instead of the dbname column, as a filter. Because the check can be done against only the @db variable, the query optimizer can eliminate the SELECT statements that don’t match the passed database name. This check is performed either at compile-time, in which case only one SELECT statement gets executed if the correct database name is specified, or at run time before execution by using a special filter called STARTUP EXPR.

The showplan output for the SELECT statement below shows that if the @db database name is specified as 'db1,' only the SELECT statement from db1 database is executed:

USE dbmasterGOSET showplan_text ONGOSELECT * FROM GetAdminConfig( N'db1' )GO    SET showplan_text OFF    GO      /*      |--COMPUTE Scalar(DEFINE:([AdminConfig].[id]=[AdminConfig].[id],   [AdminConfig].[name]=[AdminConfig].[name], [Expr1012]=db_name(CONVERT(NULL))))          |--COMPUTE Scalar(DEFINE:([Expr1012]=db_name(CONVERT(NULL))))                |--CLUSTERED INDEX Scan(OBJECT:([db1].[dbo].[AdminConfig].  [PK__AdminConfig__76CBA758]))    */

If the database name is specified in a variable, the check for @db is performed at run time as shown in the following showplan output (look for the STARTUP EXPR filters):

SET showplan_text ON   GODECLARE @db nvarchar(128)    SET @db = N'db1'    SELECT * FROM GetAdminConfig( @db )   GO    SET showplan_text OFF  GO      /*     |--Concatenation          |--Filter(WHERE:(STARTUP EXPR([@db]='db1' OR [@db]=NULL)))          |    |--COMPUTE Scalar(DEFINE:([Expr1015]=db_name(CONVERT(NULL))))       |         |--CLUSTERED INDEX Scan(OBJECT:([db1].[dbo].[AdminConfig].  [PK__AdminConfig__76CBA758]))          |--Filter(WHERE:(STARTUP EXPR([@db]='db2' OR [@db]=NULL)))           |    |--COMPUTE Scalar(DEFINE:([Expr1016]=db_name(CONVERT(NULL))))         |         |--CLUSTERED INDEX Scan(OBJECT:([db2].[dbo].[AdminConfig].  [PK__AdminConfig__76CBA758]))          |--Filter(WHERE:(STARTUP EXPR([@db]='db3' OR [@db]=NULL)))                |--COMPUTE Scalar(DEFINE:([Expr1017]=db_name(CONVERT(NULL))))                     |--CLUSTERED INDEX Scan(OBJECT:([db3].[dbo].[AdminConfig].  [PK__AdminConfig__76CBA758]))    */

When the database name is specified as a variable in a query of the following GetAdminConfig table-valued function, Adam can use the SET STATISTICS IO output to determine whether he's accessed only the db1 database's AdminConfig table.

SET STATISTICS IO ON   DECLARE @db nvarchar(128)    SET @db = N'db1'    SELECT * FROM GetAdminConfig( @db )    SET STATISTICS IO OFF    GO      /*TABLE 'AdminConfig'. Scan COUNT 0, logical reads 0, physical reads 0, read-ahead reads 0.TABLE 'AdminConfig'. Scan COUNT 0, logical reads 0, physical reads 0, read-ahead reads 0.TABLE 'AdminConfig'. Scan COUNT 1, logical reads 2, physical reads 0, read-ahead reads 0.*/

Now, Adam can efficiently use the inline table-valued function to query the AdminConfig table in each of the hosted databases because the query resolves to only one table when the database name is specified. When a new database is created, Adam can recreate the GetAdminConfig table-valued function as part of the database-creation utility, so that the new database includes the SELECT statement that refers to the AdminConfig table.

Using the inline table-valued function, now Adam can easily and efficiently perform the following queries:

SELECT * FROM GetAdminConfig(DEFAULT)GO   SELECT * FROM GetAdminConfig(N'db1')GODECLARE @db nvarchar(128)   SET @db = N'db1'SELECT * FROM GetAdminConfig(@db)  GO  SELECT * FROM GetAdminConfig(NULL)GOSELECT dbname, COUNT(*) AS cnt FROM GetAdminConfig(NULL) GROUP BY dbnameGOCREATE TABLE #config ( configname VARCHAR(30) NOT NULL, param VARCHAR(255) NOT   NULL )INSERT INTO #config VALUES('a', '%p1, %p2')  SELECT *    FROM #config AS c   JOIN GetAdminConfig(DEFAULT) AS g    ON g.configname = c.configname  DROP TABLE #config   GO

AUGUST READER CHALLENGE:

Now, test your SQL Server savvy in the August Reader Challenge, "Generating 6-Digit All-Numeric File Numbers for Search" (below). Submit your solution in an email message to [email protected] by July 20. Umachandar Jayachandran, a SQL Server Magazine technical editor, will evaluate the responses. We’ll announce the winner in an upcoming SQL Server Magazine UPDATE. The first-place winner will receive $100, and the second-place winner will receive $50.

Problem:

Roger is a database architect for a company that builds SQL Server 2005-based applications for law firms. One such application that generates case-file numbers and maintains client records. User requests from the application, such as adding a new case record, will trigger the generation of sequential, six-digit, all-numeric case file numbers, which the application's screen will display. Users can then search for a case or look up a particular record by using the generated case file number.

Help Roger design a CaseFiles table that fulfills the following requirements:

  1. Generates unique numeric values between 1000 and 999,999 that have six digits with leading zeroes.

  2. Employs the six-digit file number column as the primary search mechanism so that any queries on the table that use a specific file number or set of file numbers will be executed in the most efficient manner possible.

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like