In my current day job, one of the things I need to do is load log files from devices into SQL Server. Nothing out of the ordinary, but the data volume made for an interesting time - because each log file has maybe 100 or 200 rows. So not the sort of volume that you'd like to bulk load, and definitely not the sort of volume that you'd want to insert with individual insert statements. Because we're using SQL Server 2008 I thought that table-valued parameters would be the obvious choice.
So I read up on it and became concerned after reading Bob Beachemin's Blog at SQL Skills about TVP use and plan compilation. That had me worried, and so I was pleased to find, when I checked again, that he had blogged about it again, but this time with more positive news.
So I decided to implement a solution for data loading based on the use of TVPs. Researching that some more, I found a blog by Leonard Lobel about passing the data in. His method of explitly implementing IEnumerable<SqlDataRecord> fit pretty exactly with the method in which I wanted to pass the data to SQL Server. So I sat down at home and came up with the following utility class (yes I am sad and write day job code in the evenings!) which I thought would be good to share with you...
IEnumerable<SqlDataRecord>
The idea is that it's a simple wrapper around List<T> that provides the explicit implementation of IEnumerable<SqlDataRecord>, but while remaining generic in and of itself. This necessitated that the classes that would use the list would be able to represent themselves as a SqlDataRecord object, and that the generic list would infer this through the use of a generic type constraint.
List<T>
SqlDataRecord
So here's the interface:
So we have two methods, CreateDataRecord - which is the method that creates the SqlDataRecord object and populates it with columns, and PopulateDataRecord which is passed the existing SqlDataRecord and is expected to call the various set methods in order to set the values.
CreateDataRecord
PopulateDataRecord
Let's have a look at the list class itself then...
No particular rocket-science here. Just a generic class with a type constraint, implementing the IEnumerator using the yield and yield break keyword. Job done.
IEnumerator
yield
yield break
Any questions?
Tags: clr, table-types, objects
clr
There was a question on Ask SQL Server Central recently, which got me thinking. Was there a way that we could mandate that all object use should be qualified? As it turns out, the answer is relatively simple - and took the form of a DDL trigger.
Now, this DDL trigger is 2008+ only, due to it's use of [sys].[sql_expression_dependencies], but what it effectively does is make sure that any object that is referenced by the object we've just created must be referenced by it's schema qualified name.
[sys].[sql_expression_dependencies]
Without further ado, here is the code:
ALTER TRIGGER [trig_InhibitNonQualifiedObjectUse] ON DATABASE FOR DDL_DATABASE_LEVEL_EVENTSAS BEGIN SET NOCOUNT ON; DECLARE @data XML; DECLARE @schema sysname; DECLARE @object sysname; DECLARE @eventType sysname; DECLARE @fullObjectName [varchar](MAX); SET @data = EVENTDATA(); SET @eventType = @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname'); SET @schema = @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname'); SET @object = @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname'); SET @fullObjectName = QUOTENAME(@schema) + '.' + QUOTENAME(@object); IF @object IS NOT NULL BEGIN IF EXISTS (SELECT * FROM [sys].[sql_expression_dependencies] WHERE [referencing_id] = OBJECT_ID(@fullObjectName) AND [referenced_schema_name] IS NULL) BEGIN DECLARE @errorMessage [varchar](MAX); SET @errorMessage = @eventType + ' - ' + @fullObjectName + ': Operation terminated due to non-qualified object use'; RAISERROR(@errorMessage,17,0) ROLLBACK; END ENDEND;GO
Tags: triggers, t-sql, objects
t-sql
In versions of SQL Server prior to 2000, the column and parameter metadata was a mess. In fact, most of the metadata was a mess, but columns and parameters were particularly messy, seeing as they were all stored in [syscolumns]. Columns had positive colid values, parameters had negative colid values (except for return values, which were 0). Nasty.
[syscolumns]
Thankfully, in SQL Server 2005 this situation was sorted out considerably, leaving us with a system view for each category of object: [sys].[columns] and [sys].[parameters]. They still have some things in common - because they both deal with typed information, each of them have the columns [system_type_id], [user_type_id], [max_length], [precision] and [scale].
[sys].[columns]
[sys].[parameters]
[system_type_id]
[user_type_id]
[max_length]
[precision]
[scale]
Let's look at what these columns mean:
system_type_id
[sys].[types]
user_type_id
max_length
[text]
[xml]
precision
[numeric]
[decimal]
scale
is_xml_document
xml_collection_id
total_digits
decimal_places
object_id
column_id
collation_name
is_nullable
is_ansi_padded
is_rowguidcol
is_identity
[sys].[identity_columns]
is_computed
[sys].[computed_columns]
is_filestream
is_replicated
is_non_sql_subscribed
is_merge_published
is_dts_replicated
default_object_id
rule_object_id
[sys].[check_constraints]
is_sparse
is_column_set
So - quite a lot of useful stuff there. So what does [sys].[parameters] have in store for us? Let's have a look:
name
parameter_id
[column_id]
is_output
is_cursor_ref
has_default_value
default_value
[has_default_value]
So, a lot of information there then. It's not perfect, and there are some massive omissions, as well as things that MSDN clearly states incorrectly (for example, are scale and precision 0 for non-numeric types?). But, the system views available in SQL Server 2005+ give us a huge improvement over what was avialable previously, and give us a huge amount of information that we can use when writing system that work generically with SQL Server data, or even when writing Dynamic SQL.Next time we look at the typing system, and look at user defined types and assembly types.
Tags: metadata, columns, objects
metadata
So what do those columns in [sys].[objects] mean? These columns are found in all the type-specific system views (as ‘inherited columns’), and contain the basic information that applies to any schema-scoped object. Let’s have a look at them:
[sys].[objects]
principal_id
[sys].[database_principals]
schema_id
[sys].[schemas]
parent_object_id
type
[sys].[default_constraints]
[sys].[sql_modules]
[sys].[foreign_keys]
[sys].[foreign_key_columns]
[sys].[assembly_modules]
[sys].[internal_tables]
[sys].[plan_guides]
[sys].[key_constraints]
[sys].[synonyms]
[sys].[service_queues]
[sys].[triggers]
[sys].[table_types]
[sys].[tables]
[sys].[views]
[sys].[extended_procedures]
type_desc
create_date
modify_date
is_ms_shipped
is_published
is_schema_published
Note that the SQL 2000 system table [sysobjects] was reasonably similar to this, but less clear. For example, the status column had different meanings for different objects. The new system views are in a better 'sql format' - i.e. they are much closer to something that a sql developer would be proud of.
[sysobjects]
Tags: metadata, objects
Most common objects in SQL Server are schema-scoped, that is they belong to a schema. In SQL Server 2000, schemas and database users were effectively the same thing, so objects could be called ‘user-scoped’. However, starting with SQL Server 2005 schemas and users (database principals) were separated. This means that schemas are now no longer tied directly to the users in the database.
So, schema scoped objects are those that belong directly to a schema. A schema can be thought of as a logical partition within a database which directly serves two purposes:
Schema scoped objects can be found generally in the [sys].[objects] table. This table contains the following object types:
In this list, some of the object types are directly schema-scoped (i.e. they have a schema because you have chosen to place them in a schema for a reason) and some are indirectly schema-scoped (i.e. they have a schema because they are a child object of another object that belongs to a schema). While indirectly schema-scoped objects belong to their schema, they cannot have a schema different to that of their parent object.
So, the directly schema scoped objects in [sys].[objects] are:
And the indirectly schema scoped objects in [sys].[objects] are:
However, there are other object types which are schema-scoped that are not present in [sys].[objects]. Hang on, doesn’t the MSDN description for [sys].[objects] say ‘Contains a row for each user-defined, schema-scoped object that is created within a database.’? Yes it does, but XML Schema Collections, user defined SQL types and user defined CLR types are all schema-scoped, yet do not appear in [sys].[objects]. Hey, that’s life (and also why I had a hard time writing my schema comparison tool). In the next article, I will explain what the columns in [sys].[objects] mean, and begin to explain the metadata for the various supplementary system views that provide extra information that is relevant to each different type.