A table that has relation to itself issue - sql-server-2005

I've defined table with this schema :
CREATE TABLE [dbo].[Codings]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NULL,
[CodeId] [int] NOT NULL,
[Title] [nvarchar](50) COLLATE Arabic_CI_AI NOT NULL,
CONSTRAINT [PK_Codings]
PRIMARY KEY CLUSTERED ([Id] ASC) WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
And fill it up with data like this :
Id ParentId CodeId Title
----------- ----------- ----------- ----------
1 NULL 0 Gender
2 1 1 Male
3 1 2 Female
4 NULL 0 Educational Level
5 4 1 BS
6 4 2 MS
7 4 3 PHD
Now I'm looking for a solution, in order, when I delete a record that is parent (like Id = 1 or 4), it deletes all children automatically (all records that have a ParentId = 1 or 4).
I supposed I can do it via relation between Id and Parent Id (and set cascade for delete rule), but when I do that in MMS, the Delete Rule or Update Rule in Properties is disabled.
My question is: what can I do to accomplish this?
Thank you
Edit:
When I wrote (like Id = 1 or 4) I meant the records that are parent, not a child, and I don't mean query like like Id = 1 or 4
Some friend implied I can do it via a delete trigger, but I supposed I can do it via relation

Maybe you have to define a index on the ParentID column first. You can't put constraints on columns that aren't indexed.

I always script for solutions to this, IE in the application search for the ID, then delete where all parent IDs = ID, then delete the parent record.

You can accomplish it with a "DELETE TRIGGER" - just use it to delete any rows that have matching parents. It's essentially the same thing a cascade-delete would do.
CREATE TRIGGER t_Codings_delete
ON Codings
AFTER DELETE
AS
BEGIN
DELETE Codings
FROM Codings c
JOIN DELETED d -- Virtual table containing rows you just deleted
ON c.ParentId = d.Id
END

How about an OR clause?
Delete From TableName
Where ID = 4 OR ParentId = 4

I don't think you can have cascading deletes on a self-refrencing table. You can probably do it with a join.
Delete from codings
inner join codings2 on codings.id = codings2.parentid
where codings.id = myid
Note not tested.

Related

SQL Server trigger not acting as expected

I have these two tables:
CREATE TABLE [dbo].[Test_Table1]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[f_id] [int] NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Test_Table2_Tbl]
(
[f_id] [int] IDENTITY(1,1) NOT NULL,
[text] [varchar](500) NULL
) ON [PRIMARY]
And a trigger:
CREATE TRIGGER [dbo].[trg_Test_Trigger_Delete]
ON [dbo].[Test_Table2_Tbl]
AFTER DELETE
AS
INSERT INTO Test_Table2_Tbl (text)
(SELECT id FROM deleted)
UPDATE Test_Table1_Tbl
SET f_id = NULL
WHERE f_id IN (SELECT id FROM deleted)
GO
The keen observer will realize that 'id' does not exist in 'deleted'.
SQL catches that error in the INSERT, but without the INSERT it will let you add the trigger without any complaints.
Why doesn't it catch that error? (Invalid column name 'id'.)
My second question is, why does the UPDATE statement update EVERY column to NULL when id is not found in deleted?
I'm just very confused and need so clarity on why every record matches that WHERE clause.
Your error in the UPDATE is because the query is treated as a correlated subquery:
UPDATE Test_Table1_Tbl
SET f_id = NULL
WHERE Test_Table1_Tbl.f_id IN (SELECT Test_Table1_Tbl.id FROM deleted d);
id doesn't resolve in deleted, so SQL looks out at the next level.
These are the scoping rules for subqueries. That is why you should always use qualified table names when you have subqueries:
UPDATE Test_Table1_Tbl
SET f_id = NULL
WHERE Test_Table1_Tbl.f_id IN (SELECT d.id FROM deleted d);

Generate a sub job

PS: Added a new image to better describe what i would like to achieve
As I couldn't find a way to phase the question, therefore it limits the possibility that I could get a ''ready'' solution, pardon me if this is available.
I am self-learning SQL, and would hope to gain some valuable lessons and information on how to write something as the following, greatly appreciated!
Seeking to write lines that allows me to add a master job (eg. 05-16-00000)
in each master job, there will be other "jobs" so it should generate (eg. 05-16-0000 - 01 .. XX). How can I have it written in a way?
[2
[]2
Just hold an id for each record and if a row has a parent, you set a parent_job_id to the corresponding id. Rows with no parent have the parent_job_id set to NULL.
CREATE TABLE `dbname`.`job`
( `id` BIGINT NOT NULL,
`description` VARCHAR(45) NULL,
`parent_job_id` BIGINT NULL,
PRIMARY KEY (`id`)
);
Get master jobs:
SELECT
`job`.`id`,
`job`.`description`,
`job`.`parent_job_id`
FROM
`testdb`.`job`
WHERE
`job`.`parent_job_id` IS NULL
;
If you are looking for children of job 3 replace the WHERE clause with
WHERE `job`.`parent_job_id` = 3
As you show in your later added example you want to m:n link the table with itself. Create a table with parent and child IDs.
CREATE TABLE `dbname`.`job_parent_child`
( `parent_id` BIGINT NOT NULL,
`child_id` BIGINT NOT NULL,
PRIMARY KEY (`parent_id`, `child_id`)
);
Same example - get all childs with parent job 3
SELECT * FROM `dbname`.`job` AS `child`
INNER JOIN `dbname`.`job_parent_child` AS `mn`
ON `child`.`id` = `mn`.`child_id`
WHERE `mn`.`parent_id` = 3
;
According to your last edit just select the job ids (and possibly other data if needed) from the table and iterate over the rows.
SELECT DISTINCT `JOB ID` FROM `jobs`;
Output the master job row of the html table. Then query with prepared statement
SELECT * FROM `jobs` WHERE `JOB ID` = ?;
Output all the rows. That's really all.

How to re-number T-SQL's system auto-increment Identity column?

I have an auto-increment primary key in a SQL table lets say table look like this:
CREATE TABLE [Table] (--Identifier contains a space and uses a reserved keyword.
[ID] [int] IDENTITY(1,1) NOT NULL ,
[Name] [varchar](50) NULL,
CONSTRAINT [PK__Table] PRIMARY KEY CLUSTERED ([ID] ASC)
);
ID | Name|
1 John
2 Jack
3 Bill
4 Joe
Then I delete row 2 Jack:
ID | Name|
1 John
3 Bill
4 Joe
And what I want to achieve is to change id column so the table will look like this
ID | Name|
1 John
2 Bill
3 Joe
Is there a way to do it?
I will never do that but you can:
create a new autoincrement primary key named ID2
delete ID column
rename ID2 column as ID
Quick and dirty way to do it is, (my way) -->
select * into yournewtable from youroldtable order by yourIdentityColumn;
Then, open up yournewtable's design, make sure yourIdentityColumn is Identity(1,1).
Then, drop youroldtable.
Then, rename yournewtable to youroldtable! ta-da!
Set identity_insert Table off;
Update Table set ID = 3 where ID = 4;
...
Set identity_insert Table on;
Where Table name is Table

How to organize CRUD for table with composite key

I have a table to store simple parameters in format param name / param value. But it primary key is composite key.
What is the best way to organize adding/updating/deleting parameters with locking. So for example if someone start putting into table next parameters
first_comp_id second_comp_id paramName paramValue
12 5 param_1 sdgfsdf
12 5 param_2 sdgfsdf
12 5 param_3 sdgfsdf
12 5 param_4 sdgfsdf
Anyone else cannot add/edit or delete any record with {first_comp_id:12, second_comp_id:5} ?
Below my table structure (I slightly changed names but idea the same):
CREATE TABLE [dbo].[SomeTable](
[first_comp_id] [int] NOT NULL,
[second_comp_id] [int] NOT NULL,
[paramName] [varchar](50) NOT NULL,
[paramValue] [varchar](200) NOT NULL,
PRIMARY KEY
(
[first_comp_id] ASC,
[second_comp_id] ASC,
[paramName] ASC
)
)
if i got this correctly you have user1 entering those records
and user1 can edit them and add new records with {first_comp_id:12, second_comp_id:5}
you would need to add a col containing a unique reference to the user
then you would in your code accessing the db add the check that the user has to be correct
update sometable (paramValue) where first = ee and second = bb and name = rr and user = current user
insert would become
IF not EXISTS (SELECT * FROM sometable WHERE first = ee and second = bb and name = rr and user <> current user)
BEGIN
insert into sometable (first,second, name, val, user)
values (ee,bb,rr,vv,uu)
END
or if you want it completely on db level you could use triggers
before update check if correct user
before insert check if first and second aren't used by another user

SQL Update: Cannot change one of composite key value of first record

In MSSQL Server, I have a table StudentCourse with a Composite Primary Key (StudentID, CourseID). I am trying to change the selected student to another course. One student record of each course group is preventing me to do UPDATE operation.
StudentID CourseID
1 1
1 2
1 3
2 2
2 3
2 4
I can update (1, 2), (1, 3) records' CourseID to 5, but I can't update (1, 1) record's CourseID to 5. Similary, I can update (2, 2), (2, 3) records' CourseID to 5, but I can't update (2,4) record's CourseID to 5.
Only one record of such CourseID group is preventing me to change its CourseID field. I am getting the following error.
Violation of PRIMARY KEY constraint
'PK_StudentCourse'. Cannot insert
duplicate key in object
'StudentCourse'. The statement has
been terminated.
I don't know it is first or last record of each group prohibits me to change CourseID. I am sure there is no record with CourseID = 5 in StudentCourse table, and I have a course record with CourseID of 5 in Course table.
Any help would be appreciated.
The error you are seeing means that you are trying to create a record with the same value of primary key as another existing record. You are making a mistake here, but you are not giving enough information, to understand what your mistake is.
When I have a problem, I find it useful to create a small repro, that can illustrate the problem, so that I can show it to other users. Sometimes, when I try to create a simple repro, the repro actually works without a problem. This lets me know that there is something different in this working "repro" and my problem case. Next step for me would be to bridge the gap between them, modify either of them to make them closer until the difference in behaviour disappears. The step that made it, usually reveals the culprit of the behaviour being investigated.
In your case I can make following simple steps, to prove, that SQL Server is operating as expected:
I create a table:
CREATE TABLE [dbo].[StudentCourse](
[StudentID] [int] NOT NULL,
[CourseID] [int] NOT NULL,
CONSTRAINT [PK_StudentCourse] PRIMARY KEY CLUSTERED
(
[StudentID] ASC,
[CourseID] ASC
))
I add test data in:
INSERT INTO [dbo].[StudentCourse] values (1,1)
INSERT INTO [dbo].[StudentCourse] values (1,2)
INSERT INTO [dbo].[StudentCourse] values (1,3)
INSERT INTO [dbo].[StudentCourse] values (2,2)
INSERT INTO [dbo].[StudentCourse] values (2,3)
INSERT INTO [dbo].[StudentCourse] values (2,4)
I perform the updates you have described:
UPDATE [dbo].[StudentCourse] SET CourseID = 5 where StudentId = 2 and CourseID = 4
UPDATE [dbo].[StudentCourse] SET CourseID = 5 where StudentId = 1 and CourseID = 1
I can see that these work just as they should.
Try to understand what you are doing differently, and you'll find the cause of your problem.
I found the problem. When I was building Conditions for query string, one condition wasn't adding GroupID criteria. The query string happens to miss that GroupID crieria when that record was included in query string. It was happening as follow.
UPDATE StudentCourse SET CourseID = 5 WHERE CourseID = 1 AND StudentID IN(2,3)
UPDATE StudentCourse SET CourseID = 5 WHERE StudentID IN(1,2,3)
UPDATE StudentCourse SET CourseID = 6 WHERE CourseID = 2 AND StudentID IN(2,3)
UPDATE StudentCourse SET CourseID = 6 WHERE StudentID IN(2,3,4)
Of course, my query was violating primary key rule without CourseID criteria. Thanks for your time, mates.