It's beyond me to create a one to one relationship in SQL Server if you are inserting directly into the related tables; in this article I discuss just how you go about actually implementing a workable one to one relationship. Please feel free to comment or send me email to tonyrogerson@sqlserverfaq.com to discuss.

 

First of all let’s define what we mean by a “one to one relationship”.

A relationship is the definition of two “relations” (tables) having some logical data relationship or dependency such as one to one, one to zero or many, one to many etc.

The one to one relationship means that there must be at least one row in the left table and one corresponding row in the right table, a good definition can be found here: http://folkworm.ceri.memphis.edu/ew/SCHEMA_DOC/comparison/erd.htm (Understanding Entity Relationship Diagrams – section “relationships”).


In SQL Server terms that would be modelled something like below...


create
table Customers (


 

       CustomerSurrogateID  int    not null      identity     

              constraint pk_Customers primary key clustered,

      

       CustomerID           char(5)       not null

              constraint uk_Customer_CustomerID unique,

             

       Name   varchar(100)

       )

      

      

create table CustomersDetail (


 

       CustomerSurrogateID  int    not null

              constraint pk_CustomersDetail primary key clustered

              constraint fk_CustomersDetail_Customers foreign key references Customers( CustomerSurrogateID ),

             

       InitialOrderDate     datetime not null

       )

go


 

If you tried to create a foreign key reference between Customers and CustomersDetail actually in the CREATE TABLE statement you will have noticed that it fails because you haven’t created the CustomersDetail table – this eludes to the problem we are facing when it comes to inserting the data.


 

--     Can only add the foreign key reference here...

alter table Customers add constraint fk_Customers_CustomersDetail foreign key ( CustomerSurrogateID ) references CustomersDetail( CustomerSurrogateID )

go


In the schema above I use a surrogate key on Customers, a surrogate key being an automatically generated value that is part of the logical model and an attribute on the table but has no meaning within the business, it can be exposed to the application developer because it is stable and never changes, I mention that because often there is confusion between a surrogate key and a tuple id (row id), the difference is that the surrogate key never changes whereas the tuple id will change as deletes and inserts occur in the table, anyway, you can’t get access to the tuple-id in SQL Server.


Ok, we have our schema so what happens when we try and insert into the two tables?


We start our transaction so we keep consistency on the inserts into the two tables:



begin
tran


We now do the insert:


insert
Customers ( CustomerID, Name ) values( 'abcde', 'Funky Parts Ltd' )


What happened? Well, we get the error message below:


Msg 547, Level 16, State 0, Line 1

The INSERT statement conflicted with the FOREIGN KEY constraint "fk_Customers_CustomersDetail". The conflict occurred in database "MScBI2010", table "dbo.CustomersDetail", column 'CustomerSurrogateID'.

The statement has been terminated.


 

Why? SQL Server is right to barf; its barf’d because when it checks for a corresponding row in the CustomersDetail table there isn’t one. Now think – is it a simple case of inserting the row in the CustomersDetail table first? Well no, that’s because it’s exactly the same circumstance – there would need to be a corresponding row in the Customers table – basically you are stuck and our use of the FOREIGN KEY constraint for RI has failed us, well – failed us in the practical sense – but it did do its job!


Moving on, why do we have the one to one relationship in the first place? Why not just merge the two sets of columns into a single table and be done with it? Logically the split may make sense, also from an implementation point of view you may want to do it to reduce locking contention thus increasing concurrency.


Worse still, what happens when we execute the above in a transaction? Would you expect the constraint violation to cause the transaction to rollback?



begin
tran


 

insert Customers ( CustomerID, Name ) values( 'abcde', 'Funky Parts Ltd' )


 

select [count(*)]       = ( select COUNT(*) from Customers )

select [scope_identity] = scope_identity()             --     what is the surrogate value?

select [@@trancount]    = @@TRANCOUNT                  --     did it rollback?


 

rollback


 

Msg 547, Level 16, State 0, Line 3

The INSERT statement conflicted with the FOREIGN KEY constraint "fk_Customers_CustomersDetail". The conflict occurred in database "MScBI2010", table "dbo.CustomersDetail", column 'CustomerSurrogateID'.

The statement has been terminated.


 

count(*)

-----------

0


 

scope_identity

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

NULL


 

@@trancount

-----------

1


Two things have happened; firstly the row did not get inserted – the clue there is the result “the statement has been terminated”, remember “statement”; but the @@trancount proves we are still in the original outer transaction.


Realistically (<sarcasm>and we all do this don’t we</>) we should use BEGIN TRY / CATCH to trap the error – example below:



begin
tran


 

begin try


 

       insert Customers ( CustomerID, Name ) values( 'abcde', 'Funky Parts Ltd' )


 

end try

begin catch


 

       rollback tran

       print 'rolled back'


 

end catch


 

if @@TRANCOUNT > 0

begin

       commit tran

       print 'committed'

end



Moving forward I see the two tables logically connected and as such I think we can model a view across the pair of them in the Physical Model, the view is below:



create
view vw_CustomersCustomerDetails

       with schemabinding

as

       select cCustomerID = c.CustomerID, cCustomerSurrogateID = c.CustomerSurrogateID, cName = c.Name,

                 cdCUstomerSurrogateID = cd.CustomerSurrogateID,

                 cdDummyInsert = cd.DummyInsert,

                 cdInitialOrderDate = cd.InitialOrderDate,

                 cdSomeUniqueColReqd = cd.SomeUniqueColReqd

       from dbo.Customers c

              inner join dbo.CustomersDetail cd on cd.CustomerSurrogateID = c.CustomerSurrogateID

go


 

We pull all the columns from both tables and use an INNER JOIN to form the relationship. The naming of the columns needs thinking about and is a personal choice, from my experience I’d say that you ought to name them firstly prefixing with the source table then source column name for example Customers_CustomerID.


We can now insert into the view:



insert
vw_CustomersCustomerDetails ( cCustomerID ) values ( 1 )


 

(1 row(s) affected)


Let’s look at our row:

select * from vw_CustomersCustomerDetails


cCustomerID cCustomerSurrogateID cName                                                                                                cdCUstomerSurrogateID cdDummyInsert cdInitialOrderDate      cdSomeUniqueColReqd

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


 

(0 row(s) affected)


 

Interestingly although we did the insert, got one row affected we do not have in rows returned.


Try the insert again...


insert
vw_CustomersCustomerDetails ( cCustomerID ) values ( 1 )


Msg 2627, Level 14, State 1, Line 1

Violation of UNIQUE KEY constraint 'uk_Customer_CustomerID'. Cannot insert duplicate key in object 'dbo.Customers'.

The statement has been terminated.


Lol, we have 0 rows returned from querying vw_Customer sCustomerDetails yet we get a violation of the unique key on Customers.


Ok, something really daft is going on here and it’s breaking Codds Rule 6 (
http://www.databaseanswers.org/codds_rules.htm); the insert should not work because the View is not updating both tables; this is simply a side effect of the way that the query optimiser expands the View SQL into the main body of the query which I’ve covered on my blog here: http://sqlblogcasts.com/blogs/tonyrogerson/archive/2008/01/03/views-they-offer-no-optimisation-benefits-they-are-simply-inline-macros-use-sparingly.aspx.


We can prevent the engine from doing this silliness by using what is called an INSTEAD OF trigger, there are two types of trigger an INSTEAD OF which is before the update/insert/delete takes place and puts you in control of what will happen because that is the nature of that feature – you are proving logic “instead of” the original dml statement and the after trigger which is post update/insert/delete.



create
trigger trg_CustomersCustomerDetails_instof

       on vw_CustomersCustomerDetails instead of insert

as

begin


 

       --     First insert the Customer, the trigger on Customer

       --     will take care of inserting the CustomerDetail dummy record.

       insert Customers ( CustomerID, Name )

              select cCustomerID, cName

              from inserted

             

       --     Now update the dummy record with the CustomersDetail

       update CustomersDetail

              set DummyInsert = NULL,           --     we now have real data

                     InitialOrderDate = i.cdInitialOrderDate,

                     SomeUniqueColReqd = i.cdSomeUniqueColReqd

       from inserted i

              inner join Customers c on c.CustomerSurrogateID = i.cdCustomerSurrogateID


 

end

go


The above creates our INSTEAD OF trigger thus intercepting the INSERT on the view; we must do the “Insert” processing ourselves, the rows inserted into the view are contained within the system generated “inserted” table.


We are little out of sequence so let’s get back to the solution; we are basically going to use a dummy row in the CustomersDetail table, I can hear people drawing breath already in terms of RI – how can we enforce uniqueness etc. I’ve thought about that and will show you later on.


We need to modify our “right” table and make all the columns except our key Nullable. We also implement our CHECK constraints with slightly difference logic as you can see on the CustomersDetail table and the InitialOrderDate. The CHECK CONSTRAINT must be a table level CHECK CONSTRAINT otherwise bets are off eg.



create
table Customers (


 

       CustomerSurrogateID  int    not null      identity     

              constraint pk_Customers primary key clustered,

      

       CustomerID           char(5)       not null

              constraint uk_Customer_CustomerID unique,

             

       Name   varchar(100)

       )

      

create table CustomersDetail (


 

       CustomerSurrogateID  int    not null

              constraint pk_CustomersDetail primary key clustered

              constraint fk_CustomersDetail_Customers foreign key references Customers( CustomerSurrogateID ),

      

       DummyInsert                char(1) null

              constraint uk_CustomersDetail check( coalesce( DummyInsert, 'N' ) in ( 'Y', 'N' ) ),

             

       InitialOrderDate     datetime null,

      

       SomeUniqueColReqd    int null,

      

              check( ISNULL( DummyInsert, 'N' ) = 'Y' or ( DummyInsert IS NULL and InitialOrderDate IS NOT NULL ) )


 

       )

go


 

If you’ve blatted the tables remember to recreate the trigger:



create
trigger trg_CustomersCustomerDetails_instof

       on vw_CustomersCustomerDetails instead of insert

as

begin


 

       --     First insert the Customer, the trigger on Customer

       --     will take care of inserting the CustomerDetail dummy record.

       insert Customers ( CustomerID, Name )

              select cCustomerID, cName

              from inserted

             

       --     Now update the dummy record with the CustomersDetail

       update CustomersDetail

              set DummyInsert = NULL,           --     we now have real data

                     InitialOrderDate = i.cdInitialOrderDate,

                     SomeUniqueColReqd = i.cdSomeUniqueColReqd

       from inserted i

              inner join Customers c on c.CustomerSurrogateID = i.cdCustomerSurrogateID


 

end

go


The above is not enough for our solution to work properly; inserting into the view is fine now but we need to have trigger logic on both the Customers and CustomersDetail tables.


 

create trigger trg_Customers_ins on Customers for insert

as

begin


 

       --     Make sure there is a row in the table we have a one to one with.

       --     It cannot contain data.

       insert CustomersDetail ( CustomerSurrogateID, DummyInsert )

              select CustomerSurrogateID, 'Y'

              from inserted


 

end

go


 

create trigger trg_CustomersDetail_del on CustomersDetail for delete

as

begin

       --     Need to remove the corresponding Customers row to keep consistency

      

       delete Customers

       where CustomerSurrogateID in ( select CustomerSurrogateID

                                      from deleted )


 

end

go


The solution basically inserts a dummy row into the CustomersDetail table – horrible I know, but we incorporate a DummyInsert column that is set to Y whenever that row is a Dummy; that then allows us to still use CHECK constraints; UNIQUE constraints are implemented using a unique filtered index such as below:



create
unique index fltd_CustomersDetail_InitialOrderDate

       on CustomersDetail( SomeUniqueColReqd )

       where DummyInsert IS NULL         --     Ignore dummy inserts because of one to one relationship

go


In summary; implementing a true one to one relationship in SQL Server is far from easy but I’ve shown you “a way” of doing it. The method uses the dummy row approach which is the only way that would work, but ordinarily if you just use a trigger on the left table to insert the dummy row there is nothing forcing the application developer to remember to go on and update the columns in the right table – inserting through the view forces that; lastly – the use of the DummyInsert column enables you to properly enforce constraints on columns that really do allow Nulls. On the columns that are logically NOT NULL you can do a CHECK CONSTRAINT similar to CHECK ( ( DummyInsert IS NULL AND {your col} IS NOT NULL) OR DummyInsert IS NULL).

UPDATE: 2010-01-23T14:20:00

Been thinking further on this and there are couple of holes that need to be plugged, one of them is updating the surrogate key on the CustomerOrders table - that needs "something" on there to make it insert only and not updateable - any suggestions, hint: something in an update trigger :)

UPDATE: 2010-01-28T14:39:00

Talking in the private groups with Adam Machanic he rightly points out we need deferrable constraints that operate when the final transaction commits which is right - never thought about that, anyway checking the ISO standard its in there....

"The checking of a constraint depends on its constraint mode within the current SQL-transaction. If the constraint mode is immediate, then the constraint is effectively checked at the end of each SQL-statement. If the constraint mode is deferred, then the constraint is effectively checked when the constraint mode is changed to immediate either explicitly by execution of a <set constraints mode statement>, or implicitly at the end of the current SQL-transaction."

We don't have deferrable constraints in SQL Server unfortunetly.

Allan Mitchell and I are starting our Business Intelligence Masters this week at the University of Dundee, as we are up here anyway it seemed a good idea to put on a user group meeting, so, details below - it's this Wednesday in the Queen Mothers Building from 6pm - 9pm.

You received this email because you are a member of the UK SQL Server user group, if you no longer want to receive these emails then just reply to me with the word remove in the subject - it's a manual task so please be patient if I missed you last time.

Please check the full and modified agenda here: http://sqlserverfaq.com?eid=211.

We are currently all talking about when and where to hold meetings, this will get formalised by the end of this month so watch this site :).

We are in the Queen Mothers Building; any problems my mobile is 0796 816 0362.

For directions on how to get their look at the map of campus found here: http://www.computing.dundee.ac.uk/travel.asp, it's literally a 10 minute walk from Dundee station.

Agenda

18:00 - 18:15 Meet & Greet

Meet up and socialise with your friends, meet new people, find out what other people are doing with SQL Server.

18:15 - 18:30 Round Table Discussion - Q & A from and to the audience

Bring your SQL problems and hopefully somebody within the room will be able to fix or advise you. There is always a wealth of experienced people at the user group meetings.

18:30 - 18:45 LiveLock: Always use IDENTITY for your keys

Two sides of the audience, one will argue FOR the statement and one will argue AGAINST.

18:45 - 19:30 SQL Server Tools

Tony Rogerson, SQL Server MVP

You've been tasked with looking after or developing against SQL Server - what next? How can I make sure my SQL is going to work in production and not grind the entire system to a halt?

I will take you through Management Studio and Profiler so you fully understand what is in there and how to use it; when showing query plans I will explain from an entry level how to read them and what some of the access paths and join types mean.

19:30 - 19:50 Break for Pizza

More time to network with your peers, meet new friends and get your questions answered.

19:50 - 21:00 Introduction to SQL Server Integration Services 2008

Allan Mitchell, SQL Server MVP

ETL? You've heard the word (or perhaps not); anyway - you want to know what this really powerful feature that comes with the SQL Server product set is.

Allan will show you some of the killer features in SSIS 2008, this will be an interactive session so bring your questions!

Many thanks,

Tony Rogerson, SQL Server MVP
http://sqlserverfaq.com
http://sqlblogcasts.com/blogs/tonyrogerson
http://twitter.com/tonyrogerson

Yes, copying between word and email always puts an extra line between code lines which makes the code hard to read.

The answer is simple; in Management Studio go into Find and Replace, in "Find Options" check "Use" and select "Regular Expressions".

Now, in the Find what: box enter the text ^\n

In the Replace with: box enter nothing, please don't take me literally on that and actually type the word nothing, I mean leave the text box empty.

Click Replace All.

Da dah!

You are registered for tonights London UG, if you are not coming then please unregister now so I don't send you a bill for £20! The agenda tonight is cracking - so much going on, so much in fact we have to start early tonight - don't worry though, just get there when you can make it.

This session is not available remotely - sorry, you need to come.

Please check the full and modified agenda here: http://sqlserverfaq.com?eid=193.

In summary we've got.... The usual Round table - bring your problem discussion

First ever LiveLock discussion which will figure on "All tables should have a clustered index" - please do some research before tonight and bring some "for the statement" and "against the statment" - not sure how it will go, but hey - should be a bit of fun :)

Dave Ballantyne will do a SQL nugget for us

Martin Cairney will be doing a session on "An introduction to the Power of Policies"

BakBone will be doing a product demonstration on their tools

Siddharth Mehta will be doing a session on "Microsoft Business Intelligence Project Boosters"

Tony (assuming I can stop coughing by tonight) will be going through some of the Internals for the beginner

A great line up - we've still room so tell your friends, again if you aren't coming please tell me.

Remember we start the meeting at 5.30pm.

Location: Microsoft London, Cardinal Place, 100 Victoria Street, London, SW1E 5JL

Many thanks

Tony Rogerson
SQL Server MVP
http://sqlserverfaq.com
http://sqlblogcasts.com/blogs/tonyrogerson

Table Variables are not Transactional, that is to say BEGIN TRAN....ROLLBACK has no effect on them; we can use that to our advantage.

Have you ever been in the situation where you use a log file within your application to log progress, unfortunetly because your process is in a transaction you lose what has happened, well - not anymore - just use a table variable.... 

create table my_log_file (

    msg varchar(1024)

)

declare @t table ( msg varchar(1024) )

begin tran

--  some processing

--  ...

--  ...

--  logging as we go....

insert my_log_file ( msg ) values( '1' )
insert my_log_file ( msg ) values( '2' )
insert my_log_file ( msg ) values( '3' )
insert my_log_file ( msg ) values( '4' )
insert @t ( msg ) select msg from my_log_file

rollback

select * from my_log_file
select * from @t

This Thursday 26th is the London SQL User Group and also the Edinburgh UG; in London we have Tony Rogerson doing a session on Internals Basics aimed at people who aren't experts but will likely benefit as a reminder for those who are, we have a Nugget from David Ballantyne composed from some of his blog posts on ranking problems and udf problems, we also have Siddharth Mehta who will talk about Microsoft Business Intelligence Project Boosters. The Edinburgh group sees Martin Bell talk through some of the items in the Newsgroups as well as a ton of SQL nuggets.

We are also starting our LiveLock discussions, ever watched Argumental? Well, same principal - we have a specific topic, the first one will be "Every table must have a clustered index" and we have a group who argue for and group against, we summarise by pulling it back to a best practice advice to end the stalemate - should be fun!

If you don't want these email blasts then just reply to me with the word remove on the subject.

To register please visit http://sqlserverfaq.com?eid=193.
Make sure you reserve your place sooner because this session will be popular.Come and socialise and learn from your peers; these physical meetings are great places to expand your network, get answers and find out how other people are using SQL Server and what is going on. This is the last meeting this year for London and for those who can we should do drinks after the meeting to continue the SQL chat in an even more informal environment.If you want to twitter please make sure you use the tag #uksqlug so it is shown on the site.Agenda

17:30 - 18:00 Meet & Greet

Meet up and socialise with your friends, meet new people, find out what other people are doing with SQL Server.


18:00 - 18:15 Round Table discussion and Nuggets

Take stock and get the latest news in the SQL Server field. This is also a great opportunity to ask any burning questions you have - perhaps a work problem, general guidance etc..

18:15 - 18:30 LiveLock: Every table should have a Clustered IndexTwo sides of the audience, one will argue FOR the statement and one will argue AGAINST.18:30 - 18:45 Dave Ballantyne SQL Nugget from his blog posts...http://sqlblogcasts.com/blogs/sqlandthelike/archive/2009/09/08/bug-use-of-ranking-functions-result-in-an-inefficient-query-plan.aspxhttp://sqlblogcasts.com/blogs/sqlandthelike/archive/2009/10/15/udf-overhead-a-simple-example.aspx
( with an offshoot to http://sqlblogcasts.com/blogs/sqlandthelike/archive/2009/11/24/the-observer-effect-in-action.aspx)


18:45 - 19:15 SQL Server Internals for the Beginner
Tony Rogerson, SQL Server MVP
Tony will cover SQL Server memory, Database Structure (how they are composed) including why the transaction log is so important and how to fix the amount of space it uses if it grows too big, we will also cover some best practice for instance maintanence plans and finally we will cover Index internals and fragmentation and what causes it.

This really will be from beginners in, but don't be put off - I will try and go as deep as people want and time permits.
Its a good open session so if you want anything specific covering then let me know in advance.19:15 - 19:35 Break with Pizza

19:35 - 20:15 TBC

To be confirmed shortly
20:15 - 21:00 Microsoft Business Intelligence Project Boosters
Siddharth Mehta

BIDS and SSMS are not always sufficient for any MS BI Project. Right from the documentation that might be required just for compliance to tools that are able to fill up the gaps that SSMS or BIDS leave behind, a lot of accessories are required for a project for a leveraged development and delivery.
 
MS BI Project Booster is a kind of kit that consists of a collection of tool & utilities (freewares) and douments and/or documenting methods, that is good to have installed or available in advance, that can help any MS BI Team to tackle issues or cater requirements in a speedier manner at various stages of a project development life cycle. It's a resource kit, and can be used at various stages of the project and by the use of the same, I believe that a project can have the benefit of an Agile BI Development from an implementation perspective.
My Blog: http://siddhumehta.blogspot.com
 
About me: http://beyondrelational.com/blogs/siddharthmehta/about.aspx

Many thanks

Tony Rogerson
SQL Server MVP
http://sqlserverfaq.com
http://sqlblogcasts.com/blogs/tonyrogerson

 

SQL Server 2008 SP1 Cumulative Update (CU5) link: http://support.microsoft.com/kb/975977/LN/

Details on how they have fully fixed the OPTION( RECOMPILE ) Parameter Embedding Optimisation : http://support.microsoft.com/kb/976603/

What is Parameter Embedding Optimisation, well its basically you can do this...

WHERE email = coalesce( @email, email )
      AND name = coalesce( @name, name )
OPTION ( RECOMPILE )

Basically, if @email is NULL then the optimisers optimises out that clause in the query thus giving better access to index choices and negating the need for dynamic SQL.

A very cool feature that came in with RTM, was disabled in CU4 because of the bug and fixed and renabled in CU5.

 

Andrew Fryer has done a number of blog posts on Virtualisation for DBA's for SQL Server - very good and informative. 

http://blogs.technet.com/andrew/archive/2009/10/16/virtualisation-for-the-dba-part-1-are-you-bovvered.aspx

http://blogs.technet.com/andrew/archive/2009/10/12/virtualisation-for-the-dba-part-2-sql-server-management.aspx

http://blogs.technet.com/andrew/archive/2009/10/20/virtualisation-for-the-dba-part-3-sql-server-performance.aspx

http://blogs.technet.com/andrew/archive/2009/10/21/virtualisation-for-the-dba-part-4-licensing-and-support.aspx

 

This Thursday 29th is the London SQL User Group at Microsoft where we have Jamie Thomson talking about "Deploying SSRS Reports using MSBuild", we have one of our meeting sponsors Steve Hitchman from WhereScape demonstrating their Data Warehouse Development Workbench for SQL Server and we also have Andrew Sadler talking about "BI 2.0 - I'm searching.... give me a minute".

If you do not want't these emails then please just reply with remove in the subject and I'll update my list; you've got it because you are registered and still subscribed.

While I'm emailing you, don't forget the next Reading user group meeting is on the 11th Nov and I'll be talking about High Availability (Partitioning, File Groups (use of, backup/restore)) and an Introduction to SQL Server architecture and I'm currently looking for another one or two speakers, so if you want to come to that then get yourself registered -
http://sqlserverfaq.com?eid=171. Please make sure you are registered if you are coming so we know how much pizza we need! If you are already registered then this is also your confirmation.

Register and more details:
http://sqlserverfaq.com?eid=192.

This meeting is being sponsored by Microsoft and WhereScape.

The User Group meetings are very informal, very interactive and a really great method of expanding your knowledge, meeting other SQL Server professionals and getting answers to your problems.

17:30 - 18:00 Meet & Greet

18:00 - 18:15 Round Table discussion and Nuggets

Take stock and get the latest news in the SQL Server field. This is also a great opportunity to ask any burning questions you have - perhaps a work problem, general guidance etc..

18:15 - 19:15 Deploying SSRS reports using MSBuild - Jamie Thomson, SQL Server MVP

Come and see how deployment of Reporting Services can be automated and integrated into msbuild.

19:15 - 19:30 Break and Pizza

19:30 - 20:00 Steve Hitchman - Demonstration of Data Warehouse Development Workbench for SQL Server

20:00 - 21:00 ‘I’m searching .... give me a minute.’ BI 2.0 - Andrew Sadler, Hitachi Consulting

Search functionality is all pervasive. Outlook, explorer, intranet, internet, its everywhere. Why shouldn’t BI be the same?

As the boundaries between structured and non-structured data get blurred, end users are expecting to see and get more context around their BI as well as being lead into their BI in an intuitive user paradigm, like search.

This session highlights some simple yet quite powerful ways to expose some of search functionality that Sharepoint offers, in relation to Mirosoft’s BI offering, using several real world use-cases.

Many thanks

Tony Rogerson
SQL Server MVP
http://sqlserverfaq.com
http://sqlblogcasts.com/blogs/tonyrogerson

There is so much out in the cosmos for helping with the manageability of SQL Server, this particular download from Microsoft is the early concept that later became the Management Data Warehouse in SQL Server 2008 which frankly is way cool.

Anyway - this really cool download helps you find bad queries, check waitstats, anyway - here is the overview from the download page....

Overview

The SQL Server 2005 Performance Dashboard Reports are Reporting Services report files designed to be used with the Custom Reports feature introduced in the SP2 release of SQL Server Management Studio. The reports allow a database administrator to quickly identify whether there is a current bottleneck on their system, and if a bottleneck is present, capture additional diagnostic data that may be necessary to resolve the problem. For example, if the system is experiencing waits for disk IO the dashboard allows the user to quickly see which sessions are performing the most IO, what query is running on each session and the query plan for each statement.

Common performance problems that the dashboard reports may help to resolve include:
- CPU bottlenecks (and what queries are consuming the most CPU)
- IO bottlenecks (and what queries are performing the most IO).
- Index recommendations generated by the query optimizer (missing indexes)
- Blocking
- Latch contention
 
 

Remember its SP2 of SQL Server 2005 you need.

Say you have a table and that table has a secondary history table so you can do windowing, for instance the settings on a specifc entity like the date when we started retaining fees on a debtor account say.

Ordinarily most people would resort to a large series of OR's to check the inserted and deleted tables to see what has changed.

Instead, you can use GROUP BY, DISTINCT and HashBytes to work out what rows (what key) has changed and actually requires sorting (updating the end date on the base table inserting a new row with the new settings).

The trigger below only shows the SELECT statement that gives you the complete logic I'm talking about, of course - this is test only - you should no longer be putting SELECT's in triggers but it highlights the behaviour...

create table some_base_table (

 

    batch_id        int not null primary key clustered,

 

    batch_name      varchar(250) not null,

    some_parm       int null,

    some_parm2      int null

)

 

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 1, 'hello fred', 1, 2 )

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 2, 'hello fred', 1, 2 )

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 3, 'hello fred', 1, 2 )

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 4, 'hello fred', 1, 2 )

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 5, 'hello fred', 1, 2 )

insert some_base_table ( batch_id, batch_name, some_parm, some_parm2 )

    values( 6, 'hello fred', 1, 2 )

 

create trigger trg_some_base_table_check_for_changes on some_base_table for update

as

begin

 

    select batch_id

    from (

        select distinct batch_id, hash_combined = hashbytes( 'sha1', combined )

        from (  select batch_id,

                       combined =(  select batch_id, batch_name, some_parm, some_parm2

                                    from deleted c       --  need old values

                                    where c.batch_id = d.batch_id

                                    for xml path( '' ) )

                from deleted d

                union all

                select batch_id,

                       combined =(  select batch_id, batch_name, some_parm, some_parm2

                                    from some_base_table c       --  need current values (could use inserted here)

                                    where c.batch_id = d.batch_id

                                    for xml path( '' ) )

                from deleted d

            ) as r

        ) as c

    group by batch_id

    having count(*) > 1

 

end

go

 

 

--  test

 

update some_base_table

    set batch_name = lower( batch_name )

where batch_id = 2


 

update some_base_table

    set batch_name = lower( batch_name )

Update 20091022:

HashBytes only takes 8000 bytes so if you have a long row with lots of column you break this and get a binary truncation error; so - instead of using HashBytes use CHECKSUM like discussed earlier in the comments, CHECKSUM works across the varchar(max) the for xml gives you. The important thing here is the use of FOR XML to get around the incompatible data type problem.

 

 

Lots of really useful information: http://support.microsoft.com/kb/2001270/en-us

A must read for anybody with or considering using database mirroring.

Tony.

I had an email today wanting a solution to a row concatenation problem, for those who already know where this is going - yes, its FOR XML, anyway....

Consider the data source:

PRODUCT

PROCESS

TOP

BOTTOM

RIGHT

LEFT

 

 

 

 

 

 

GLASS 1

CUTTING

 

 

 

 

GLASS 1

BEVELLING

 

YES

YES

 

GLASS 1

GRINDING

YES

YES

 

 

GLASS 1

POLISHING

 

 

YES

YES

 

 

 

 

 

 

GLASS 2

CUTTING

 

 

 

 

GLASS 2

BEVELLING

 

YES

YES

 

GLASS 2

MILTERING

 

 

 

YES

GLASS 2

SAND BLASTING

 

YES

 

 

 

 

 

 

 

 

GLASS 3

CUTTING

 

 

 

 

GLASS 3

POLISHING

 

 

YES

YES

GLASS 3

TEMPERING

 

 

 

 

You want the result:

PRODUCT

PROCESS

 

 

GLASS1

CUTTING/   BEVELLING (B,R)/   GRINDING (T,B)/   POLISHING (R,L) 

GLASS2

CUTTING/   BEVELLING (B,R)/   MILTERING (L)/   SAND BLASTING (B) 

GLASS3

CUTTING/   POLISHING (R,L)/   TEMPERING

The FOR XML extension is the perfect tool for the job...

declare @input table (

    Item     varchar(50) not null,

    Process  varchar(50) not null,

    P_Top    char(1) null,

    P_Bottom char(1) null,

    P_Right  char(1) null,

    P_Left   char(1) null

)

 

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Blah', 'P1', null, null, null, null )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Blah', 'P2', null, 'Y', 'Y', null )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Blah', 'P3', 'Y', 'Y', null, null )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Blah', 'P4', null, null, 'Y', 'Y' )

   

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Humbug', 'P1', null, null, null, null )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Humbug', 'P2', null, 'Y', 'Y', null )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Humbug', 'P3', 'Y', null, null, 'Y' )

insert @input ( Item, Process, P_Top, P_Bottom, P_Right, P_Left )

    values( 'Humbug', 'P4', null, null, 'Y', 'Y' )

   

select *

from @input

 

select r.Item,

       Concatenated = substring( replace( replace( r.Concatenated, ' ()', '' ), '(,', '(' ), 3, 4096 )

from (

    select r.Item,

           Concatenated = ( select '/ ' + Process + ' ('

                                        + case when P_Top    = 'Y' then ',T' else '' end

                                        + case when P_Bottom = 'Y' then ',B' else '' end

                                        + case when P_Right  = 'Y' then ',R' else '' end

                                        + case when P_Left   = 'Y' then ',L' else '' end + ')' as [text()]

                            from @input i

                            where i.Item = r.Item

                            for xml path( '' ) )

    from (

        select Item

        from @input

        group by Item

            ) as r

    ) as r

           

 

See attached for the deck and scripts, any questions or you want to talk further just email me @ tonyrogerson@torver.net.

For the event notifications stuff see Martin Bell's blog: http://sqlblogcasts.com/blogs/martinbell/archive/2009/05/22/Event-Notifications.aspx

 

Don't forget tomorrow night is the first Cardiff based user group meeting where I will present on Ensuring Business Continuity (Backups, Mirroring and Monitoring) and Allan Mitchell will do SQL 2008 Integration Services - some of the things that make it better, details: http://sqlserverfaq.com?eid=198.

Also tomorrow night there is an Edinburgh user group meeting where Rob Carrol will present on "Your own Performance Analysis Toolkit" and Martin Bell will do an "Introduction to Powershell", details:
http://sqlserverfaq.com?eid=202.

Mark Whitehorn is organising a MSc in Business Intelligence - a 1 year full time or 2 year part time course at the Dundee university, for more information check my
blog about it.

There is a permanent DBA job going at the Old Mutual Asset Managers (
see details), for more information just email me Tony Rogerson and I'll give you the details - no agencies though!

I posted the decks and examples from my talk at the SQL Server Data Management Conference last Tuesday -
click for the link.

Allan Mitchell and myself will be starting weekly LiveMeetings again from the end of this month - I'll get the details and abstracts on the site later in the week.

Next week we have a Reading meeting on Wednesday at TVP which will have a developer/dba focus (agenda will be confirmed this week); also we have a Manchester user group meeting on the Thursday which will be a Reporting Services evening.

If you don't want these emails just reply to me with remove in the subject.

Many thanks

Tony Rogerson
SQL Server MVP
http://sqlserverfaq.com
http://sqlblogcasts.com/blogs/tonyrogerson

More Posts Next page »