SQL Server 2008: What to set FK as PK? - sql-server

I'm putting together my own database and from examples I've seen, Foriegn Key can also be set as Primary Keys.
I was creating my Tables so that all of my FK were also PK. Is this wrong? When should a FK be a PK? Does it have to be a PK?
Primary Key's make sense in their own table... as the Id and Identity. But when using the Id is another table, does it have to be a PK as well?

A Foreign Key should only be the Primary Key when your trying to create a 1 to 1 or 1 to zero/1 mapping.
Example:
I have a Person table, an Employee table, and a Contractor table. All Employees are people, all Contractors are people and every Person is either an employee or a Contractor
Essentially you would end up with something like this.
In response to your people have multiple addresses you should create an association table. Here is a diagram.
As you can see now every person can have many addresses and since each Employee is a person then every Employee can have many addresses. This is the same for Contractor as well.
Edited: Here is the Change Script from SQL Server
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Address
(
AddressId bigint NOT NULL,
Address nvarchar(50) NULL,
City nvarchar(50) NULL,
State nvarchar(50) NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Address ADD CONSTRAINT
PK_Address PRIMARY KEY CLUSTERED
(
AddressId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Address SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Person
(
PersonId bigint NOT NULL,
Name nvarchar(50) NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Person ADD CONSTRAINT
PK_Person PRIMARY KEY CLUSTERED
(
PersonId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Person SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.PersonAddress
(
PersonId bigint NOT NULL,
AddressId bigint NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
PK_PersonAddress PRIMARY KEY CLUSTERED
(
PersonId,
AddressId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
FK_PersonAddress_Person FOREIGN KEY
(
PersonId
) REFERENCES dbo.Person
(
PersonId
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
FK_PersonAddress_Address FOREIGN KEY
(
AddressId
) REFERENCES dbo.Address
(
AddressId
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE dbo.PersonAddress SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Employee
(
EmployeeId bigint NOT NULL,
EmployeeNumber nvarchar(50) NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Employee ADD CONSTRAINT
PK_Employee PRIMARY KEY CLUSTERED
(
EmployeeId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Employee ADD CONSTRAINT
FK_Employee_Person FOREIGN KEY
(
EmployeeId
) REFERENCES dbo.Person
(
PersonId
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE dbo.Employee SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Contractor
(
ContractorId bigint NOT NULL,
ContractorNumber nvarchar(50) NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Contractor ADD CONSTRAINT
PK_Contractor PRIMARY KEY CLUSTERED
(
ContractorId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Contractor ADD CONSTRAINT
FK_Contractor_Person FOREIGN KEY
(
ContractorId
) REFERENCES dbo.Person
(
PersonId
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE dbo.Contractor SET (LOCK_ESCALATION = TABLE)
GO
COMMIT

There is only one scenarios that would require a FK to also be a PK.
When the table represents a subclass, or subset, of the things in the PK table, for example, Eg, SalariedEmployees table which has a FK to the Employees table...

A FK is a field pointing to the PK of another table. That's it.
A table linked to itself could contain a FK pointing to its own PK.
A PK that is also a FK can only happen in the child table of a 1 to 1 relationship.

An FK should only also be a PK if the two tables have a one-to-one relationship and the second table was added because the first was too wide and these were items not always needed in most queries.
It will not work at all if you have a one-to-many relationship or a many-to-many relationship.
FKs are much more often not also the PK. If I havea person table and a related address table, If I make the PK and the FK the same thing, then I can only store one address, but most address tables allow for mulitple addresses for the same person or organization. IN that case you would have and AddressID as the PK and a person_id as the FK to the person table. This is the most common PK/FK scenario.

One situation where a given column is both a PK and an FK is the relational model for the gen-spec design pattern. In one of the other responses "Employees" is a specialization of Persons". The PK in the employees tables references the PK in the Persons table. So the PK in the specialized table is also an FK.
This allows the creation of a view that joins employees and persons to provide in a single view all the data about employees, whether that data is peculiar to employees (like "Hire Date") or is common to all persons, whether or not they are employees (like "Date of Birth").
It is not good practice to make every PK also be an FK. The FKs should reflect the logical structure of the data. If the logical model is illogical, you're headed for trouble.

Related

Deleting tables with referential integrity - get REFERENCE constraint

I want to delete a User (parent table) which has blog comment(s) and blog comment reply(s)
I have it coded to 1st delete the BlogCommentReply (child to the BlogComment), then the BlogComment (parent to BlogCommentReply), then the user (parent to both).
I get the error:
The DELETE statement conflicted with the REFERENCE constraint "FK_BlogCommentReply_UserId". The conflict occurred in database "DBGbngDev", table "dbo.BlogCommentReply", column 'UserId'.
I have FK keys on the BlogCommentReply and BlogComment tables.
1.) Did I create the table structure correctly?
2.) Do I need cascades - why?
3.) Is the delete code order correct?
_ I am of the belief that the parent table cannot be deleted until the
children are deleted first. Is that correct?
Table creates:
CREATE TABLE [dbo].[User]
(
[UserId] [int] IDENTITY(1,1) NOT NULL, -- PK
other columns....
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[UserId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS =
ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[BlogComment]
(
[BlogCommentId] [int] IDENTITY(1,1) NOT NULL, -- PK
[UserId] [int] NOT NULL, -- FK
other columns....
CONSTRAINT [PK_BlogComment] PRIMARY KEY CLUSTERED
(
[BlogCommentId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[BlogComment] WITH CHECK ADD CONSTRAINT [FK_BlogComment_UserId] FOREIGN
KEY([UserId]) REFERENCES [dbo].[User] ([UserId])
GO
CREATE TABLE [dbo].[BlogCommentReply]
(
[BlogCommentReplyId] [int] IDENTITY(1,1) NOT NULL, -- PK
[UserId] [int] NOT NULL, -- FK
[BlogCommentId] [int] NOT NULL, -- FK
other columns....
CONSTRAINT [PK_BlogCommentReply] PRIMARY KEY CLUSTERED
(
[BlogCommentReplyId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS =
ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_UserId] FOREIGN
KEY([UserId]) REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_BlogCommentId]
FOREIGN KEY([BlogCommentId]) REFERENCES [dbo].[BlogComment] ([BlogCommentId])
GO
Stored procedure (simplified for discussion) that does the deletes:
DELETE dbo.BlogCommentReply
FROM dbo.BlogComment a
WHERE ( BlogCommentReply.BlogCommentId = a.BlogCommentId AND BlogCommentReply.UserId = #a_UserId )
DELETE dbo.BlogComment
WHERE UserId = #a_UserId
DELETE dbo.[User]
WHERE ( UserId = #a_UserId )
It appears that your FK's are doing their jobs and preventing you from creating orphaned rows when deleting data from BlogCommentReply.
Your first DELETE from BlogCommentReply can be rewritten as two statements to make work correctly:
-- Remove replies added by the deleted user
DELETE
FROM dbo.BlogCommentReply
WHERE (UserId = #a_UserId)
-- Remove blog comment replies added by other users creating replies to a comment made by the deleted user
DELETE
FROM dbo.BlogCommentReply
WHERE (BlogCommentId IN (
SELECT BlogCommentId
FROM BlogComment
WHERE (UserId = #a_UserId)
))
In the first statement you are simply trying to delete rows from BlogCommentReply that use a FK to the User table.
In the second statement you delete any reply to a blog comment that will be deleted.
In response to your specific questions:
1. Did I create the table structure correctly?
It looks fine although there are several ways to achieve the same.
2. Do I need cascades - why?
No. Cascade deletes reek of poor design and lazy development. Try to
avoid them (note this is an opinion not a statement of fact).
There are some interesting points in this question.
3. Is the delete code order correct?
Yes, delete from the tables in order of key precidence.
4. I am of the belief that the parent table cannot be deleted until the children are deleted first. Is that correct?
Yes indeed, without cascaded deletes that is correct and, in your
case, the error thrown shows that the referential integrity of the
database is sound.

Drop and add constraints in one stored procedure

I would like to drop a primary key and then add a primary key to the same column in 1 stored procedure.
I have the drop constraints within Begin Transaction' andCommit Transactionthen try to add the constraints within another set ofBegin TransactionandCommit Transaction`.
I am getting the error that a primary key already exists for this column when it was dropped in the first set of transactions.
This is the code:
BEGIN TRANSACTION
ALTER TABLE [dbo].[Lens] DROP CONSTRAINT [FK_Lens_Style];
ALTER TABLE [dbo].[Lens] DROP CONSTRAINT [FK_Lens_Type];
ALTER TABLE [dbo].[Coating] DROP CONSTRAINT [PK_Coatings];
ALTER TABLE [dbo].[CoatingCost] DROP CONSTRAINT [PK_CoatingCost];
COMMIT TRANSACTION
BEGIN TRANSACTION
ALTER TABLE [dbo].[Coating]
ADD CONSTRAINT [PK_dbo.Coating]
PRIMARY KEY CLUSTERED ([CoatingId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
COMMIT TRANSACTION
If the first set of constraints are dropped and committed, shouldn't I be able to add a primary key on the column that previously had a primary key?
UPDATE
I think I know the problem. It is because there is a Foreign Key associated with the primary key that I am trying to drop.
I will need to drop all of the foreign keys associated with all of the primary keys that I am trying to drop. Then add the primary keys back. Then add the foreign keys back.
Are you sure that the problem is not with the same index name existing in the database in the moment when your code tries to create one? The name of an index must be unique in the entire database scope not only in a single table scope.
You might want to diagnose it by putting a below query before, between and after your transaction operations:
SELECT
so.name AS tableName
, si.name AS indexName
, si.type_desc AS indexType
, si.is_primary_key AS isPK
FROM
sys.indexes AS si
JOIN sys.objects AS so ON si.object_id = so.object_id
WHERE
si.name IS NOT NULL /*otherwise it will select all tables without explicit index*/
AND
so.type = 'U'/*otherwise it unnecessarily will select indexes of system tables*/

A flexible foreign key

I have a sql server database. I'm developing a vb.net application.
Now I'm thinking to have an "Event" table that should keep all the events related with my database. But one of the fields of this table should be a field ObjectID that is related with the record that this event has to do. But this record may be on different tables. For example :
Event 1 ---- Record 25 on table Clients
Event 2 ---- Record 30 Table Invoices
Event 3 ---- Record 40 Table Articles
...
The problem is that this field ObjectID should be a Foreign key in a flexible way , because may be related with different tables.
Is there any way I can resolve this case ?
Thank you !
One way to solve it would be to add a table to your database to act like a base for the other tables and connet it with a one to one relationship to the other tables, and then connect the events table to this base table.
This will allow you to keep data integrity for each of the tables.
The base table can be as simple as just one column, or can have columns thay all other tables have in common, thus implementing a sort of "inheritance" in your data structure.
Create the base table (assuming no common columns between other tables):
CREATE TABLE TblObjectBase
(
ObjectBase_Id int IDENTITY(1,1) PRIMARY KEY
)
Then, for any other table that needs to be referenced by the ObjectId in the Events table:
CREATE TABLE TblClients
(
Client_Id int PRIMARY KEY,
Client_FirstName varchar(10),
Client_LastName varchar(10),
-- Other client related data
CONSTRAINT FK_TblClients_TblObjectBase
FOREIGN KEY(Client_Id)
REFERENCES TblObjectBase(ObjectBase_Id)
)
CREATE TABLE TblInvoices
(
Invoice_Id int PRIMARY KEY,
-- other incoice related data
CONSTRAINT FK_TblInvoices_TblObjectBase
FOREIGN KEY(Invoice_Id)
REFERENCES TblObjectBase(ObjectBase_Id)
)
The only thing remaining is to insert a new value to the TblObjectBase for any insert on your other tables. This can be be easily achived by either stored procedures or instead of insert triggers.
An insert procedure could look like this:
CREATE PROCEDURE Insert_TblClients
(
#Client_FirstName varchar(10),
#Client_LastName varchar(10),
-- any other client related data you might have
)
AS
DECLARE #ClientId int
-- Insert a new record to the base table:
INSERT INTO TblObjectBase DEFAULT VALUES;
-- Get the id you've just inserted:
SELECT #ClientId = SCOPE_IDENTITY();
-- Insert the data to the clients table:
INSERT INTO TblClients
(Client_Id, Client_FirstName, Client_LastName.....) VALUES
(#ClientId, #Client_FirstName, #Client_LastName...)
An instead of insert trigger would look like this:
CREATE TRIGGER TblClients_IO_Insert ON TblClients INSTEAD OF INSERT
AS
BEGIN
DECLARE #ClientId int
-- Insert a new record to the base table:
INSERT INTO TblObjectBase DEFAULT VALUES;
-- Get the id you've just inserted:
SELECT #ClientId = SCOPE_IDENTITY();
INSERT INTO TblClients
(Client_Id, Client_FirstName, Client_LastName.....)
SELECT #ClientId, Client_FirstName, Client_LastName.....
FROM inserted
END
If you choose to go with the instead of insert, the fact that the Identity value comes from another table should be transparent to the client (your vb.net program).
SQL Server doesn't support creating such constraint.
But you can simulate the link programmatically without much trouble.
CREATE TABLE tbl_Event
(
[idEvent] INT IDENTITY(1,1) NOT NULL,
[TableSource] INT NOT NULL,
[SourceId] INT NOT NULL
--Events fields
CONSTRAINT [PK_Tests] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
In the above exemple, SourceId is the foreign key
TableSource is used to know which table the foreign key is from.
In table source you could use sys.objects.object_id of the table.
However, since you don't have much control on those keys that are managed by SQL, I recommend using you own table with defined constant for each table instead of sys.objects.
This way you also have more control on which table can have foreign key in this table and it become really handy overtime.
CREATE TABLE tbl_tableSource(
[idTableSource] INT IDENTITY(1,1) NOT NULL,
[Constant] INT NOT NULL,
[Name] NVARCHAR(255) NULL
CONSTRAINT [PK_Tests] PRIMARY KEY CLUSTERED
(
[idTableSource] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO tbl_tableSource
VALUES(1000, 'tbl_SomeTable')
INSERT INTO tbl_tableSource
VALUES(2000, 'tbl_SomeOtherTable')
Also, this kind of relation, is less good for performance then standard one. So the field type of your keys and of the constant is really important. It should not be heavy. This because you will need to create an index on tbl_event.
To simulate cascade delete ON, you need to implement trigger on parent table like this :
CREATE TRIGGER OneParentTableOnDelete ON tbl_SomeTable
FOR DELETE
AS
BEGIN
DELETE tbl_Event
FROM DELETED
INNER JOIN tbl_Event ON tbl_Event.TableSource = [Constant for tbl_SomeTable]
AND tbl_Event.idSource = DELETED.id --PrimaryKey
END
To retrieve data, you can then do like this
--For events about one foreign key
SELECT *
FROM tbl_event
WHERE tbl_Event.TableSource = [Constant for tbl_SomeTable]
AND tbl_Event.idSource = #idPrimareyKeyOfSomeTable
--Fore events about multiple foreign key
SELECT *
FROM [tbl_SomeTable]
INNER JOIN tbl_event ON tbl_Event.TableSource = [Constant for tbl_SomeTable]
AND tbl_Event.idSource = [tbl_SomeTable].id --PrimaryKey
You could create an 'INDEXED VIEW' -- define it however you like to pull the objectIDs + any other relevant fields, as you've described, from record 25 of the clients table, record record 30 of the invoices table, and so on.
You could do the same thing with a stored procedure.
Depending on how you intend to consume the data -- if you're just pulling it into a vb.net datatable, either of these options should work fine.
A benefit to an approach like this is that you don't have to create or maintain any new tables in your database.

Inserting records in database tables using "inheritance"

I have a table whose primary key is also a foreign key to the primary key of another table (i.e. "inheritance" as simulated in a database).
/****** Object: Table [dbo].[BaseClass] Script Date: 07/15/2011 18:17:27 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[BaseClass](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](50) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_BaseClass] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [TestConcepts]
GO
/****** Object: Table [dbo].[DerivedTable] Script Date: 07/15/2011 18:17:49 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DerivedTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[SpecialProperty] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_DerivedTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DerivedTable] WITH CHECK ADD CONSTRAINT [FK_DerivedTable_BaseClass] FOREIGN KEY([ID])
REFERENCES [dbo].[BaseClass] ([ID])
GO
ALTER TABLE [dbo].[DerivedTable] CHECK CONSTRAINT [FK_DerivedTable_BaseClass]
GO
What is the proper way to insert records in this situation? Obviously an insert doesn't return the PK of the inserted row (plus the child's table's PK is identity as well).
Here are a few examples of this pattern.
The sub-type table should not have auto-increment ID, the ID matches the one in the super-type table.
The basic technique (using your example) looks something like
DECLARE #MY_ID integer;
INSERT INTO BaseTable(Title, Description)
VALUES ('title_here', 'blah, blah');
SELECT #MY_ID = SCOPE_IDENTITY();
INSERT INTO DerivedTable(ID, SpecialProperty)
VALUES (#MY_ID, newid()); -- the SpecialProperty is uniqueidentifier
One approach is to create a view, one for each sub-type table, or just one over all of them. Then create an INSTEAD OF INSERT TRIGGER on the view and use the technique inside the trigger.
You may also find this technique for capturing multiple inserted IDs useful too.
Nobody calls this "inheritance". That's not what it is. It's a relation, the R in RDBMS.
INSERTs do tell you the PK of the just-inserted row. Look at ##SCOPE_IDENTITY on SQL Server.
The DERIVEDCLASS table could have an auto-incremented (identity) PK but if so there must be another column that is a foreign key reference back to BASETABLE:
BASETABLE
id int pk autoincrement
baseattribute1
baseattribute2
etc etc
DERIVEDTABLE
id int pk autoincrement
**baseid** foreign key references BASETABLE(id)
extendedattribute1
extendedattribute2
This would permit multiple derivations of each base entity. Placing a unique index on DERIVEDTABLE.baseid or making baseid the PK would prevent this, if that is desired.
The following would instantiate the members of the base class and their derived instances and/or extended properties if any [which it is will depend on whether baseid has unique constraint in DerivedTable; if the latter it could be the PK in a one-to-one relationship with BaseTable, rather than a many-to-one]:
select * from BASETABLE
LEFT JOIN DERIVEDTABLE
on BASETABLE.id = DERIVEDTABLE.baseid
Instances of the base class that have not been extended will have NULL in the extendedattribute columns.
To find only those entities that have been extended use an inner join:
select * from DERIVEDTABLE
inner join BASETABLE
on DERIVEDTABLE.baseid = BASETABLE.id

SQL Server Column Display Order

When you create a table via scripts it appears to just list the columns in order of creation. It's a minor annoyance, but I like a certain method to the madness. When I later add a column with ALTER TABLE, how do I make it show up in a specific place when viewing the table in interactive tools such as enterprise manager?
ex:
Table Foo
---------
FooID
BarID
Name
Address
Worth
I want to
ALTER TABLE Foo
ADD BazID INT NULL
and have BazID listed between BarID and Name when I use Management Studio.
You can't do that - a SQL Server table really doesn't know anything about order of columns - there is no order in the tuple algebra that's the foundation of the relational database systems.
What SQL Server Management Studio shows you is just its own display "optimization" - you can change it there, but the standard SQL Data Definition Language (DDL) statement have no notion of "column order" - you cannot ALTER a table to move a column to a different position.
If you change the column display order in SSMS, what it'll do in the background is rename the old table to a temporary name, create the new table with the columns in the order you specified, and then copy around the data. That's why modifying the column order on a large table can take almost forever - it's not just a simple little DDL statement to be executed - it's a major undertaking.
Marc
The way the sql server management studio and other big name tools like redgate do it, is to make a new temp table, copy the information over, then drop the old table (constraints & unique indexes first), rename the temp table to the old table name, then re-add any constraints and indexes. You really can't re-order what's already there.
Here's an example:
-- here's a sales table
CREATE TABLE [dbo].[Sales](
[SalesId] [int] IDENTITY(1,1) NOT NULL,
[Month] [int] NOT NULL,
[Year] [int] NOT NULL,
[InvoiceAmount] [decimal](15, 2) NOT NULL,
[SalesRepId] [int] NOT NULL,
[BranchId] [int] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED
(
[SalesId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--Here's the sales table adding a column called description between SalesId & Month
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Sales
(
SalesId int NOT NULL IDENTITY (1, 1),
Description varchar(MAX) NULL,
Month int NOT NULL,
Year int NOT NULL,
InvoiceAmount decimal(15, 2) NOT NULL,
SalesRepId int NOT NULL,
BranchId int NOT NULL
) ON [PRIMARY]
TEXTIMAGE_ON [PRIMARY]
GO
SET IDENTITY_INSERT dbo.Tmp_Sales ON
GO
IF EXISTS(SELECT * FROM dbo.Sales)
EXEC('INSERT INTO dbo.Tmp_Sales (SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId)
SELECT SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId FROM dbo.Sales WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_Sales OFF
GO
DROP TABLE dbo.Sales
GO
EXECUTE sp_rename N'dbo.Tmp_Sales', N'Sales', 'OBJECT'
GO
ALTER TABLE dbo.Sales ADD CONSTRAINT
PK_Sales PRIMARY KEY CLUSTERED
(
SalesId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT

Resources