Associate Document with all Candidates in Candidate_Document table - sql-server

CREATE TABLE [CandidateDocsAssociation](
[Row_ID] [bigint] IDENTITY(1,1) NOT NULL,
[Doc_ID] [bigint] NOT NULL,
[Candidate_ID] [bigint] NOT NULL,
) ON [PRIMARY]
GO
I have the above table structure to store the association between documents and candidates. Row_ID is an auto generated primary key. Doc_ID is a foreign key referencing the documents table. Candidate_ID is also a foreign key referencing the Candidates table.
A candidate can be associated with more than one document and one document can be associated with multiple candidates.
What i want to achieve is insert a default common document (Doc_ID) for all candidates(DISTINCT) if a Candidate_ID row with a DOC_ID of 2 does not already exist.
I'm not getting the below code to work
WHILE EXISTS (SELECT DISTINCT Candidate_ID from CandidateDocsAssociation
WHERE Doc_ID <> (SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'))
BEGIN
INSERT CandidateDocsAssociation (Doc_ID, Candidate_ID) VALUES ((SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'),Candidate_ID)
END
GO

Forget about while-loop, SQL is about set manipulation so you must adjust your thinking to it. What you need is set of candidates that do not have document with id=2 associated. For each such candidate you need to create an association with the default document. Let's try to write that down:
INSERT INTO [CandidateDocsAssociation] VALUES([Candidate_ID], [Doc_ID])
SELECT [Candidate_ID], 2
FROM [Candidate] c -- I assume you have table of all candidates
WHERE NOT EXISTS (
SELECT * FROM [CandidateDocsAssociation] a
WHERE a.[Candidate_ID] = c.[Candidate_ID] AND a.[Doc_ID] = 2
)

Related

Unique constraint including field in referenced table

Is is possible to add a uniqueness constraint to a table that would include a field from a referenced table? If so, how can that be done?
(Note that I'm using Microsoft SQL Server.)
To explain why this is necessary, here's a simplified version of my problem.
Firstly, let's say I have a table of things, defining the fixed properties of each individual thing:
CREATE TABLE dbo.things(
id INT IDENTITY(1,1) NOT NULL
CONSTRAINT [things$PrimaryKey] PRIMARY KEY CLUSTERED,
thing_name NVARCHAR(20) NOT NULL
CONSTRAINT [things$thing_name] UNIQUE,
-- etc.
);
I also have different configurations, for a range of different elements, not just things, and these are captured in a table too:
CREATE TABLE dbo.configurations(
id INT IDENTITY(1,1) NOT NULL
CONSTRAINT [configurations$PrimaryKey] PRIMARY KEY CLUSTERED,
config_name NVARCHAR(20) NOT NULL
CONSTRAINT [configurations$config_name] UNIQUE,
-- etc.
);
I then want to have different configurations for each thing:
CREATE TABLE dbo.thing_configurations(
id INT IDENTITY(1,1) NOT NULL
CONSTRAINT [thing_configurations$PrimaryKey] PRIMARY KEY CLUSTERED,
thing_id INT NOT NULL
CONSTRAINT [thing_configurations$things_fk] REFERENCES dbo.things(id),
quantity INT NOT NULL
CONSTRAINT [thing_configurations$quantity] CHECK(quantity >= 0),
-- etc.
);
Next, I want to identify the configuration of each thing for each particular configuration in an association table. I can ensure that the configuration and associated thing configuration are unique, but I want to ensure that each thing has no more than one thing configuration for each configuration.
CREATE TABLE dbo.config_thing_configurations(
config_id INT NOT NULL
CONSTRAINT [config_thing_configurations$configurations_fk] REFERENCES dbo.configurations(id),
thing_config_id INT NOT NULL
CONSTRAINT [config_thing_configurations$thing_configurations_fk] REFERENCES dbo.thing_configurations(id)
-- How can I ensure that I do not have multiple configurations of a each thing in a configuration.
);
More specifically, say I have these things:
id
thing_name
1
Apple
2
Bicycle
...
...
And these configurations:
id
config_name
1
Base configuration.
2
Experiment #1
...
...
And these thing configurations:
id
thing_id
quantity
1
1 (Apple)
20
2
1 (Apple)
30
3
2 (Bicycle)
5
...
...
How can I prevent each configuration from having two or more different configurations of each individual thing in the thing_configurations table?
config_id
thing_config_id
1
1 (20 Apples)
1
2 (30 Apples)
...
...
For example, in the above table, I have two conflicting configurations for Apples in configuration 1.
Ideally, I'd like to be able to enforce a uniqueness constraint for config_id and the thing_id referenced through the thing_config_id. Is that possible?
So this is possible, through the creation of an indexed view in combination with a unique index.
In this specific example, this is achieved by first creating a view that associates the configuration and thing identifiers:
CREATE VIEW dbo.thing_config_view WITH SCHEMABINDING AS
SELECT
thing_configurations.thing_id,
config_thing_configurations.config_id
FROM dbo.config_thing_configurations
INNER JOIN dbo.thing_configurations ON config_thing_configurations.thing_config_id = thing_configurations.id;
Next, we create a unique index on those fields from the view:
CREATE UNIQUE CLUSTERED INDEX [thing_config_view$unique_thing_configurations] ON dbo.thing_config_view(thing_id, config_id);
and it works exactly as required.
This could be handled through Triggers. The trigger content would depend on what you want to occur when a query is called that would violate the condition. Something like this for insert (untested):
CREATE TRIGGER tr_config_thing_configurations ON config_thing_configurations
INSTEAD OF INSERT
AS
IF EXISTS(
SELECT 1
FROM inserted i
INNER JOIN thing_configurations tc ON tc.id = i.thing_config_id
INNER JOIN things t ON t.id = tc.thing_id
WHERE t.id IN (
SELECT it.id
FROM config_thing_configurations ictc
INNER JOIN thing_configurations itc ON itc.id = ictc.thing_config_id
INNER JOIN things it ON it.id = itc.thing_id
WHERE
ictc.config_id = i.config_id
)
BEGIN
RAISERROR ('Configuration already has a configuration for thing', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END
INSERT INTO config_thing_configurations (config_id, thing_config_id) SELECT config_id, thing_config_id FROM inserted;
GO
You would need another for update or you could write a single to handle both cases.

Cloning A Table Along With Its Foreign Keys

How can I clone a table with guid's as well as another table that references those guid's as foreign keys.
The two tables have a one to many relationship. I would need to do this in an automated way.
My problem is being able to reference the relationship between the two tables when newly created guid's are created during the clone.
EDIT: I need to clone the entries to the tables themselves, not new tables.
Basically, we want to duplicate the Tables with new Ids but keep the relationship the same as the originals.
If required, you could keep the same Ids, but in practice this shouldn't be a requirement; for testing, the Ids should not matter - only the relationship.
I'll demonstrate this with two Tables:
The first is AnimalType with Id (uniqueidentifier - RowGuid, Primary Key) and AnimalType (nvarchar) Columns
The second is Animal with AnimalName (nvarchar) and AnimalType (uniqueidentifier, Foreign Key) Columns
For the parent/lookup Table:
Create a new Table (newTable) to populate with the data of the existing Table (oldTable).
Create newTable with its own Primary Key Id column (ROWGUID, IDENTITY etc.) with its Default
Create an extra Column in newTable to hold a copy of oldTable's Id Column values
The Id Column in newTable will generate unique Ids on creation of records
The second (child) Table:
Create a new Table (newChildTable) to populate with the data of the existing Table (oldChildTable).
Create newChildTable with its own Foreign Key Column to point to newTable's Primary Key Column
Create an extra Column in newChildTable to hold a copy of oldChildTable's Foreign Key Column values
Once created, we populate the new parent/lookup Table with the data from the original Table, placing the Id values in the extra Column added for this data. The Table's own Ids will generate uniquely as usual.
Next, we populate the child Table with the data from its original Table, placing the original Foreign Key Column values into the added Column for this data.
Next, we join the two new tables on the Columns that hold the original Id values and update the Foreign Key Column values to the new Ids in the parent/lookup Table.
Finally, we can remove the Columns holding the original Id values, and we are left with two Tables linked to the same data but by the new Ids we generated when created when the records were copied.
You will not have any reference to the original Ids - just in case of selecting the wrong Table at any time in your testing (although this should be done in a different server...). If you needed the original Ids too, you can perform the above, not move the Ids around, rename the Columns etc. - as you wish really.
/*
Create copy of parent/lookup Table with its own Id column
Add a column to hold the original Ids
*/
CREATE TABLE [dbo].[AnimalTypeBak](
[Id] [uniqueidentifier] ROWGUIDCOL NOT NULL CONSTRAINT [DF_AnimalTypeBak_Id] DEFAULT (newid()),
[OriginalId] [uniqueidentifier] NOT NULL,
[AnimalType] [nvarchar](32) NOT NULL,
CONSTRAINT [PK_AnimalTypeBak] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
GO
/*
Create copy of child Table
Add a column to hold the original Foreign Key values
*/
CREATE TABLE [dbo].[AnimalBak](
[AnimalName] [nvarchar](20) NOT NULL,
[OriginalAnimalType] [uniqueidentifier] NOT NULL,
[AnimalType] [uniqueidentifier] NOT NULL
) ON [PRIMARY]
GO
/*
Import data from the parent/lookup Table placing the origional Ids into the added Column
*/
INSERT INTO [dbo].[AnimalTypeBak]
([OriginalId]
,[AnimalType])
SELECT [Id], [AnimalType]
FROM [dbo].[AnimalType]
GO
/*
Import data from the child Table placing the origional Foreign Key values into the added Column
*/
INSERT INTO [dbo].[AnimalBak]
([OriginalAnimalType]
,[AnimalName])
SELECT [AnimalType], [AnimalName]
FROM [dbo].[Animal]
GO
/*
Update the child Table placing the new parent/lookup Ids into the Foreign Key Column
*/
UPDATE [dbo].[AnimalBak]
SET [dbo].[AnimalBak].[AnimalType] = [dbo].[AnimalTypeBak].[Id]
FROM [dbo].[AnimalBak]
INNER JOIN [dbo].[AnimalTypeBak]
ON [dbo].[AnimalBak].[OriginalAnimalType] = [dbo].[AnimalTypeBak].[OriginalId]
GO
/*
Drop the redundant Columns
*/
ALTER TABLE [dbo].[AnimalBak]
DROP COLUMN [OriginalAnimalType]
GO
ALTER TABLE [dbo].[AnimalTypeBak]
DROP COLUMN [OriginalId]
/*
Add the Foreign Key Contraint between the two Tables
*/
ALTER TABLE [dbo].[AnimalBak] WITH CHECK ADD CONSTRAINT [FK_AnimalBak_AnimalTypeBak] FOREIGN KEY([AnimalType])
REFERENCES [dbo].[AnimalTypeBak] ([Id])
GO
/*
And select the data to ensure the data is related as it was in the original Tables
*/
SELECT a.AnimalName, a.AnimalType, b.AnimalType FROM [dbo].[AnimalBak] as a INNER JOIN [dbo].[AnimalTypeBak] as b ON b.Id = a.AnimalType
declare #Parents table (Id uniqueidentifier, Name varchar(50));
declare #Children table (Id uniqueidentifier, ParentId uniqueidentifier, Name varchar(50));
declare #NewId uniqueidentifier = newid();
insert into #Parents values (#NewId, 'Original parent');
insert into #Children values (newid(), #NewId, 'First original child');
insert into #Children values (newid(), #NewId, 'Second original child');
declare #Ids table (CloneId uniqueidentifier, OriginalId uniqueidentifier);
merge #Parents as target
using (
select
CloneId = newid(),
OriginalId = Id,
Name = Name + ' (Cloned)'
from
#Parents
where
Id = #NewId
)
as source on source.CloneId = target.Id
when not matched by target then
insert (Id, Name)
values (source.CloneId, source.Name)
output
source.CloneId, source.OriginalId
into #Ids (CloneId, OriginalId);
merge #Children as target
using (
select
Id = newid(),
ParentId = ids.CloneId,
Name = Name + ' (Cloned)'
from
#Children c
inner join #Ids ids on ids.OriginalId = c.ParentId
)
as source on source.Id = target.Id
when not matched by target then
insert (Id, ParentId, Name)
values (source.Id, source.ParentId, source.Name);
select * from #Parents
select * from #Children

One to many dynamic database relations issue

I just need some confirmation is database designed like this is fine or not. And if not am I doing something wrong here.
I have following tables:
TableA{TableAID,...}
TableB{TableBID,...}
TableC{TableCID,...}
etc.
And I have one table that I use like some kind of 'news feed'. When I add something in any table A,B,C I also add row in this table.
Feed{FeedID, TypeID, ReferenceID,...}
FeedID is PK auto increment
TypeID is number that reference types table and based on this ID I know is row in this table from table A,B,C.
ReferenceId is ID of item in tables A,B,C.
A,B,C tables all have different fields.
Now when I want to get feed data I also need to grab some data from each of this table to use it in application. In my query to get this I use a lot SELECT CASE CLAUSE like:
I first join to all tables in query (A,B,C)
...
CASE Feed.TypeId
WHEN 1 THEN tableA.someData
WHEN 2 THEN tableB.someData
WHEN 3 THEN tableC.someData
END AS Data,
...
Without getting into suitability of this for a specific purpose, your supertype-subtype model is "reversed".
So DDL looks something like
CREATE TABLE Feed (
FeedID integer IDENTITY(1,1) not null
, FeedType char(1) not null
-- Common_Columns_Here
, Common_Column varchar(20)
);
ALTER TABLE Feed ADD CONSTRAINT pk_Feed PRIMARY KEY (FeedID) ;
CREATE TABLE Feed_A (
FeedID integer not null
-- A_Specific_Columns_Here
, A_Specific_Column varchar(20)
);
ALTER TABLE Feed_A ADD
CONSTRAINT pk_Feed_A PRIMARY KEY (FeedID)
, CONSTRAINT fk1_Feed_A FOREIGN KEY (FeedID) REFERENCES Feed(FeedID) ;
CREATE TABLE Feed_B (
FeedID integer not null
-- B_Specific_Columns_Here
, B_Specific_Column varchar(20)
);
ALTER TABLE Feed_B ADD
CONSTRAINT pk_Feed_B PRIMARY KEY (FeedID)
, CONSTRAINT fk1_Feed_B FOREIGN KEY (FeedID) REFERENCES Feed(FeedID) ;
CREATE TABLE Feed_C (
FeedID integer not null
-- C_Specific_Columns_Here
, C_Specific_Column varchar(20)
);
ALTER TABLE Feed_C ADD
CONSTRAINT pk_Feed_C PRIMARY KEY (FeedID)
, CONSTRAINT fk1_Feed_C FOREIGN KEY (FeedID) REFERENCES Feed(FeedID) ;
Now, in order to read from this structure, create a view first
create view vFeed as
select
f.FeedID
, FeedType
, Common_Column
, A_Specific_Column
, B_Specific_Column
, C_Specific_Column
from Feed as f
left join Feed_A as a on (a.FeedID = f.FeedID and f.FeedType = 'A')
left join Feed_B as b on (b.FeedID = f.FeedID and f.FeedType = 'B')
left join Feed_C as c on (c.FeedID = f.FeedID and f.FeedType = 'C')
;
Look what happens when I want to select data which I know is from feed A. Note that FeedType is not specified in this query, only column name which belongs to Feed_A (and common column).
select
FeedID
, Common_Column
, A_Specific_Column
from vFeed;
Notice that execution plan shows only Feed and Feed_A tables, query optimizer eliminated tables _B and _C; no need to touch those two.
In other words, you can ask for a specific feed data by simply using only specific columns in a query, and let the optimizer sort everything else out -- no need for CASE ... WHEN .. acrobatics from your example.
As I suggested in my comment (and along with #Andomar's wisdom), I think something like this would work better:
CREATE TABLE dbo.FeedTypes
(
FeedTypeID INT IDENTITY(1,1) PRIMARY KEY,
SomedataA INT,
SomedataB VARCHAR(32),
SomedataC DATETIME
--, ... other columns
);
CREATE TABLE dbo.Feeds
(
FeedID INT IDENTITY(1,1) PRIMARY KEY,
FeedTypeID INT NOT NULL FOREIGN KEY
REFERENCES dbo.FeedTypes(FeedTypeID)
--, ... other columns
);
You could enforce the presence/absence of data in the relevant columns for a given type using complex check constraints or triggers. But you'd have to have pretty complex logic (as you would in your current model) if a feed can change types easily.
Add all the data you wish to display in the "News Feed" in the Feed table. It is duplicate data, but it will make your life a lot easier in the long run.
It also ensures that your newsfeed stays historically correct. This means that when I update a record in one of the three tables, the "old" feed data stays intact instead of being updated with the new values.

Can this be done with Entity Framework 4 ? If not, what can do this?

I'm making a simplistic trivial pursuit game. I'm not sure if (and then how) I can do the following with EF4 :-
I have a table structure as follows.
Table: TrivialPursuitQuestion
=> ID
=> Unique Question
=> AnswerId
=> AnswerType (ie. Geography, Entertainment, etc).
Table: GeographyAnswer
=> ID
=> Place Name
=> LatLong
Table: EntertainmentAnswer:
=> ID
=> Name
=> BordOn
=> DiedOn
=> Nationality .. and other meta data
... etc ..
So when a person asks a unique question ... the stored proc is able to figure out what type of answer it is (ie. AnswerType field) ... and therefore query against the correct table.
EG.
SELECT #AnswerId = AnswerId, #AnswerType = AnswerType
FROM TrivialPursuitQuestions
WHERE UniqueQuestion = #Question
IF #AnswerId > 0 AND #AnswerType > 0 BEGIN
IF #AnswerType = 1
SELECT *
FROM GeographicAnswers
WHERE AnswerId = #AnswerID
IF #AnswerType = 2
SELECT *
FROM EntertainmentAnswer
WHERE AnswerId = #AnswerId
... etc ...
END
Now .. i'm not sure how to do this with EF. First of all, the stored proc can now return MULTIPLE result types .. so i'm not sure if that's really really bad.
So then I thought, maybe the stored procedure should return Multiple Recordsets .. with all but one recordset containing result(s) ... because by design, only one answer type will ever be found...
EG.
-- Same SELECT as above...
IF #AnswerId > 0 BEGIN
SELECT *
FROM GeographicAnswers
WHERE AnswerId = CASE #AnswerId WHEN 1 THEN #AnswerID ELSE 0 END
SELECT *
FROM EntertainmentAnswer
WHERE AnswerId = CASE #AnswerId WHEN 2 THEN #AnswerID ELSE 0 END
... etc ...
END
and this will return 6 (multiple) recordsets ... but only one of these should ever have some data.
Now, if this is a better solution ... is this possible with EF4 and how?
I'm trying to avoid doing TWO round trips to the db AND also having to figure out WHAT to try and retrieve ... i don't want to have to figure it out .. i'm hoping with some smart modelling the system is just smart enough to say 'OH! u this is the right answer'. Sort of like a Answer Factory (ala Factory Pattern) but with Sql Server + EF4.
ANyone have any ideas?
In the EF Designer you could create an entity called Answer and then create entities that derive from answer called GeographyAnswer, EntertainmentAnswer, ... using a discriminator AnswerType in the Answer entity. Add the Question entity. Add the association from question to Answer, mark it 1:1. Let EF generate the DDL for your database.
Now you can do question.Answer to get the Answer for a Question. You can look at the Type of Answer and show the appropriate UI.
Though, this also begs the question, if it's a 1:1 correspondence between question and answer, why not have a single entity QuestionAnswer and derive from that to create QuestionAnswerGeography, QuestionAnswerEntertainment, ... Now you can pick Questions of a specific type easily: dataContext.QuestionAnswers.ofType<QuestionAnswerGeography>() for example.
Here's an Entity Diagram for the case where an answer may be shared by multiple questions:
This model will allow you do a query like this:
var geographyQuestions = objectContext.Answers.OfType<Geography>().SelectMany(answer => answer.Questions);
The DDL generate by this model is:-
-- Creating table 'Answers'
CREATE TABLE [dbo].[Answers] (
[Id] int IDENTITY(1,1) NOT NULL
);
GO
-- Creating table 'Questions'
CREATE TABLE [dbo].[Questions] (
[Id] int IDENTITY(1,1) NOT NULL,
[AnswerId] int NOT NULL,
[QuestionText] nvarchar(max) NOT NULL
);
GO
-- Creating table 'Answers_Geography'
CREATE TABLE [dbo].[Answers_Geography] (
[PlaceName] nvarchar(max) NOT NULL,
[LatLong] nvarchar(max) NOT NULL,
[Id] int NOT NULL
);
GO
-- Creating table 'Answers_Entertainment'
CREATE TABLE [dbo].[Answers_Entertainment] (
[Name] nvarchar(max) NOT NULL,
[BornOn] datetime NULL,
[DiedOn] datetime NULL,
[Id] int NOT NULL
);
GO
-- --------------------------------------------------
-- Creating all PRIMARY KEY constraints
-- --------------------------------------------------
-- Creating primary key on [Id] in table 'Answers'
ALTER TABLE [dbo].[Answers]
ADD CONSTRAINT [PK_Answers]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
-- Creating primary key on [Id] in table 'Questions'
ALTER TABLE [dbo].[Questions]
ADD CONSTRAINT [PK_Questions]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
-- Creating primary key on [Id] in table 'Answers_Geography'
ALTER TABLE [dbo].[Answers_Geography]
ADD CONSTRAINT [PK_Answers_Geography]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
-- Creating primary key on [Id] in table 'Answers_Entertainment'
ALTER TABLE [dbo].[Answers_Entertainment]
ADD CONSTRAINT [PK_Answers_Entertainment]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
-- --------------------------------------------------
-- Creating all FOREIGN KEY constraints
-- --------------------------------------------------
-- Creating foreign key on [AnswerId] in table 'Questions'
ALTER TABLE [dbo].[Questions]
ADD CONSTRAINT [FK_QuestionAnswer]
FOREIGN KEY ([AnswerId])
REFERENCES [dbo].[Answers]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- Creating non-clustered index for FOREIGN KEY 'FK_QuestionAnswer'
CREATE INDEX [IX_FK_QuestionAnswer]
ON [dbo].[Questions]
([AnswerId]);
GO
-- Creating foreign key on [Id] in table 'Answers_Geography'
ALTER TABLE [dbo].[Answers_Geography]
ADD CONSTRAINT [FK_Geography_inherits_Answer]
FOREIGN KEY ([Id])
REFERENCES [dbo].[Answers]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
-- Creating foreign key on [Id] in table 'Answers_Entertainment'
ALTER TABLE [dbo].[Answers_Entertainment]
ADD CONSTRAINT [FK_Entertainment_inherits_Answer]
FOREIGN KEY ([Id])
REFERENCES [dbo].[Answers]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
GO

INSTEAD OF UPDATE Trigger and Updating the Primary Key

I am making changes to an existing database while developing new software. There is also quite a lot of legacy software that uses the database that needs to continue working, i.e. I would like to maintain the existing database tables, procs, etc.
Currently I have the table
CREATE TABLE dbo.t_station (
tx_station_id VARCHAR(4) NOT NULL,
tx_description NVARCHAR(max) NOT NULL,
tx_station_type CHAR(1) NOT NULL,
tx_current_order_num VARCHAR(20) NOT NULL,
PRIMARY KEY (tx_station_id)
)
I need to include a new field in this table that refers to a Plant (production facility) and move the tx_current_order_num to another table because it is not required for all rows. So I've created new tables:-
CREATE TABLE Private.Plant (
PlantCode INT NOT NULL,
Description NVARCHAR(max) NOT NULL,
PRIMARY KEY (PlantCode)
)
CREATE TABLE Private.Station (
StationId VARCHAR(4) NOT NULL,
Description NVARCHAR(max) NOT NULL,
StationType CHAR(1) NOT NULL,
PlantCode INT NOT NULL,
PRIMARY KEY (StationId),
FOREIGN KEY (PlantCode) REFERENCES Private.Plant (PlantCode)
)
CREATE TABLE Private.StationOrder (
StationId VARCHAR(4) NOT NULL,
OrderNumber VARCHAR(20) NOT NULL,
PRIMARY KEY (StationId)
)
Now, I don't want to have the same data in two places so I decided to change the dbo.t_station table into a view and provide instead of triggers to do the DELETE, INSERT and UPDATE. No problem I have [most of] them working.
My question regards the INSTEAD OF UPDATE trigger, updating the Primary Key column (tx_station_id) and updates to multiple rows.
Inside the trigger block, is there any way to join the inserted and deleted [psuedo] tables so that I know the 'before update primary key' and the 'after update primary key'? Something like this...
UPDATE sta
SET sta.StationId = ins.tx_station_id
FROM Private.Station AS sta
INNER JOIN deleted AS del
INNER JOIN inserted AS ins
ON ROW_IDENTITY_OF(del) = ROW_IDENTITY_OF(ins)
ON del.tx_station_id = sta.StationId
At this stage I've put a check in the trigger block that rollbacks the update if the primary key column is updated and there is more than one row in the inserted, or deleted, table.
The short answer is no.
You could put a surrogate key on Private.Station, and expose that through the view, and use that to identify before and after values. You wouldn't need to change the primary key or foreign key relationship, but you would have to expose some non-updateable cruft through the view, so that it showed up in the pseudo-tables. eg:
alter table Private.Station add StationSk int identity(1,1) not null
Note, this may break the legacy application if it uses SELECT *. INSERT statements without explicit insert column lists should be ok, though.
Short of that, there may be some undocumented & consistent ordering between INSERTED and DELETED, such that ROW_NUMBER() OVER (ORDER BY NULLIF(StationId,StationId)) would let you join the two, but I'd be very hesitant to take the route. Very, very hesitant.
Have you intentionally not enabled cascade updates? They're useful when primary key values can be updated. eg:
CREATE TABLE Private.Station (
StationId VARCHAR(4) NOT NULL,
Description NVARCHAR(max) NOT NULL,
StationType CHAR(1) NOT NULL,
PlantCode INT NOT NULL,
PRIMARY KEY (StationId),
FOREIGN KEY (PlantCode) REFERENCES Private.Plant (PlantCode)
ON UPDATE CASCADE
-- maybe this too:
-- ON DELETE CASCADE
)
Someone might have a better trick. Wait and watch!

Resources