views:

317

answers:

2

I have four tables: Messages, MessageCategory, MessageStatus and MessageLevel.

MessageCategory, MessageStatus and MessageLevel all just have three fields: Identity (primary key), Code and Description. Messages refers to these three fields and has a few other data fields, including Identity (primary key) MessageText and Order. The Identity fields are auto-incremented fields.

I now need to write an SQL script to add some default data to all four tables. Problem is, I need to create a script which will be sent to a customer who will then execute this script. I cannot write a bit more intelligent code to do the whole update. And while three tables are just simple insert statements, it's the Messages table that causes me some additional headaches.

I cannot remove any indices and I cannot assume it starts counting at 1 for the primary keys.

So, as an example, her's some data:

INSERT INTO MessageCategory (Code) Values ('Cat01');
INSERT INTO MessageStatus (Code) Values ('Status01');
INSERT INTO MessageLevel (Code) Values ('Level01');

And the messages would need something like this:

INSERT INTO Messages(Category, Status, Level, MessageText, Order) 
VALUES(
  (SELECT Identity from MessageCategory where Code='Cat01'), 
  (SELECT Identity from MessageStatus where Code='Status01'), 
  (SELECT Identity from MessageLevel where Code='Level01'), 
  'Just some message', 
  1
);

That won't work, though. So, what's the trick to get this working? (Keeping the code readable too...)

Unfortunately, I don't have access to the other database. I can test it but once it seems to work, it's just a matter of send-and-pray-it-works...

+2  A: 
INSERT INTO Messages 
  (Category, Status, Level, MessageText, [Order]) 
SELECT
  (SELECT TOP 1 [Identity] from MessageCategory where Code='Cat01')  AS Category,
  (SELECT TOP 1 [Identity] from MessageStatus where Code='Status01') AS Status,
  (SELECT TOP 1 [Identity] from MessageLevel where Code='Level01')   AS Level,
  (SELECT 'Just some message')   AS MessageText, 
  (SELECT 1)                     AS [Order]

The above would work for SQL Server. Note that both Identity and Order are reserved T-SQL keywords and should not be used for column names. Also note that sub-queries must not return more than one row for this to work, to ensure that I have included TOP 1 statements.

The next thing to note is that the column aliases (AS Category etc.) are not strictly necessary. Their order is what counts. I would include them for readability, especially when the SELECT list gets longer.

Tomalak
I tend to always use square brackets [] around all table and field names in my SQL scripts. That solves the possible conflicts. I tend to use more reserved words as field and table names so adding brackets is an automated action for me. :-)
Workshop Alex
So you should have done that in your sample code. as well. ;-)</hint>
Tomalak
Yeah, I expected those brackets would confuse a few so I actually removed them. :-)
Workshop Alex
Note that this only works if Code is unique in your table. I assume it is in this case, but...
Kendrick
@Kendrick: No, this is why I have included `TOP 1` clauses. It would "work" for non-unique codes as well. Kind of.
Tomalak
I kept the top-1 just in case anyway. :-) There's an unique key on those fields so it should not be possible, but still... It shows the essence of the insert query.
Workshop Alex
That's a good attitude. As Python Zen #2 says: "Explicit is better than implicit." :)
Tomalak
+4  A: 

If it's a single script, store your identities in variables:

declare MessageCategoryID int;
declare MessageStatusID int;
declare MessageLevel int;
INSERT INTO MessageCategory (Code) Values ('Cat01');
set @MessageCategoryID=scope_identity();
INSERT INTO MessageStatus (Code) Values ('Status01');
set @MessageStatudID=scope_identity();
INSERT INTO MessageLevel (Code) Values ('Level01');
set @MessageLevelID=scope_identity();

INSERT INTO Messages(Category, Status, Level, MessageText, Order) 
    VALUES(
        @MessageCAtegoryID,
        @MessageStatusID,
        @MessageLevelID,
        'Just some message',
        1);
Kendrick
It is a single script but there will be several dozens of records in the Messages table.
Workshop Alex
ANd never use @@identity use scope_identity instead. @@identity can cause major dat integrity issues if a trigger is added to your table.
HLGEM
Never used scope_identity(), but will from now on. Thanks for the tip!
Kendrick