Duplicating records efficiently in tsql - sql

I've a scenario where I have a parent table which has '1 to many' relationships with two or three tables. These child tables again have '1 to many' relationships with more tables and so on. This goes up to 5 to 6 levels of hierarchy.
Now, based on single primary key value of the parent table, I want to duplicate all information related to it in database. I wrote a stored procedure which uses cursors and inserts child rows one by one and sets new foreign key values with each insert. But it is consuming some time because number of records in child tables is high.
Is there any other efficient way to do this?

In SQL Server 2008:
CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY IDENTITY, value VARCHAR(100))
CREATE TABLE t_child (id INT NOT NULL PRIMARY KEY IDENTITY, parent INT NOT NULL, value VARCHAR(100))
CREATE TABLE t_grandchild (id INT NOT NULL PRIMARY KEY IDENTITY, child INT NOT NULL, value VARCHAR(100))
INSERT
INTO t_parent (value)
VALUES ('Parent 1')
INSERT
INTO t_parent (value)
VALUES ('Parent 2')
INSERT
INTO t_child (parent, value)
VALUES (1, 'Child 2')
INSERT
INTO t_child (parent, value)
VALUES (2, 'Child 2')
INSERT
INTO t_grandchild (child, value)
VALUES (1, 'Grandchild 1')
INSERT
INTO t_grandchild (child, value)
VALUES (1, 'Grandchild 2')
INSERT
INTO t_grandchild (child, value)
VALUES (2, 'Grandchild 3')
DECLARE #tt TABLE (oid INT, nid INT)
MERGE
INTO t_parent
USING (
SELECT id, value
FROM t_parent
) p
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (value)
VALUES (value)
OUTPUT p.id, INSERTED.id
INTO #tt;
MERGE
INTO t_child
USING (
SELECT c.id, p.nid, c.value
FROM #tt p
JOIN t_child c
ON c.parent = p.oid
) c
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (parent, value)
VALUES (nid, value)
OUTPUT c.id, INSERTED.id
INTO #tt;
INSERT
INTO t_grandchild (child, value)
SELECT c.nid, gc.value
FROM #tt c
JOIN t_grandchild gc
ON gc.child = c.oid
In earlier versions of SQL Server, you will have to do a SELECT followed by an INSERT to find out the new values of the PRIMARY KEY.

You'll have to insert one table at a time, but you can do it by inserting sets instead of rows if you allow the FK values in the new parent's child tables to be the same as the FK values of the original parent.
Say you have a view of your parent table and in your sp you limit it to the row to copy from (pk=1, say).
Then insert that row into the parent table substituting PK=2 for the PK val.
Now use a second view of one of the child tables. In your sp, limit the set of rows to those with PK=1. Again, insert all those rows into that same child table substituting PK=2 for the PK field val.

Related

How to delete from 3 tables with one query using where exists

I have four tables Monitoring, Participant, Participant validation group, Validation group,
I would like to delete all monitorings, and there particpants also each particpant possibly (not always) has participant validation group.
Blockquote
but i have the error sql command not properly ended.
NOTE: I am on oracle database and inner joins would not work here.
I would like to achieve an optimized query
that could take only the application number of monitoring and delete all monitorings, participants for each monitoring and participant validation group.
I am not sure if its actually possible in a single query if its not then what could be the optimized way to do this. and I cant use jpa for this at the moment.
Expected results, both monitorings are deleted, and there particpants as well as participant validation groups
You can define foreign keys on child tables to "ON DELETE CASCADE". So when you delete a parent table it will delete associated rows from child tables.
create table parent (
id NUMBER(10),
value varchar2(30),
constraint parent_pk primary key (id)
);
CREATE TABLE child
( id NUMBER(10) not null,
value NUMBER(10) not null,
constraint child_pk primary key (id,value),
CONSTRAINT parent_child_fk
FOREIGN KEY (id)
REFERENCES parent(id)
ON DELETE CASCADE
);
CREATE TABLE grandchild
( id NUMBER(10) not null,
value NUMBER(10) not null,
constraint grandchild_pk primary key (id,value),
CONSTRAINT child_grandchild_fk
FOREIGN KEY (id,value)
REFERENCES child(id,value)
ON DELETE CASCADE
);
insert into parent values (1,'a');
insert into parent values (2,'b');
insert into parent values (3,'c');
insert into child values (1,1);
insert into child values (1,2);
insert into child values (1,3);
insert into child values (2,1);
insert into child values (2,2);
insert into child values (2,3);
insert into child values (3,1);
insert into child values (3,2);
insert into child values (3,3);
insert into grandchild values (1,1);
insert into grandchild values (1,2);
insert into grandchild values (1,3);
insert into grandchild values (2,1);
insert into grandchild values (2,2);
insert into grandchild values (2,3);
insert into grandchild values (3,1);
insert into grandchild values (3,2);
insert into grandchild values (3,3);
SELECT (
SELECT COUNT(*)
FROM parent
) AS parent_cnt,
(
SELECT COUNT(*)
FROM child
) AS child_cnt,
(
SELECT COUNT(*)
FROM grandchild
) AS grandchild_cnt
FROM dual
DELETE from parent where value = 'a';
SELECT (
SELECT COUNT(*)
FROM parent
) AS parent_cnt,
(
SELECT COUNT(*)
FROM child
) AS child_cnt,
(
SELECT COUNT(*)
FROM grandchild
) AS grandchild_cnt
FROM dual
PARENT_CNT CHILD_CNT GRANDCHILD_CNT
2 6 6

UNIQUE constraint failed SQL

I want to create two Tables, but it does not work because of "UNIQUE constraint failed".
Can someone explain to me what is wrong with my querees?
CREATE TABLE A (
ka INT PRIMARY KEY,
a2 VARCHAR(1)
);
INSERT INTO A VALUES (1,'s');
INSERT INTO A VALUES (2,'s');
INSERT INTO A VALUES (3,'s');
INSERT INTO A VALUES (4,'m');
INSERT INTO A VALUES (5,'m');
INSERT INTO A VALUES (6,'b');
INSERT INTO A VALUES (7,'b');
INSERT INTO A VALUES (8,'b');
INSERT INTO A VALUES (9,'b');
CREATE TABLE B (
a2 VARCHAR(1),
b2 INT,
PRIMARY KEY (a2),
FOREIGN KEY (a2) REFERENCES A(a2)
);
INSERT INTO B VALUES ('s',12);
INSERT INTO B VALUES ('s',23);
INSERT INTO B VALUES ('s',34);
INSERT INTO B VALUES ('m',45);
INSERT INTO B VALUES ('m',56);
INSERT INTO B VALUES ('b',67);
INSERT INTO B VALUES ('b',78);
INSERT INTO B VALUES ('b',89);
INSERT INTO B VALUES ('b',90);
(Edited: there is more than one issue here.)
A primary key for table B must be unique. In your case, it needs to be something other than a2. If you need a primary key (usually you do just as a part of good design), you likely need a separate dedicated column.
Something like:
CREATE TABLE B (
kb INT,
a2 VARCHAR(1),
b2 INT,
PRIMARY KEY (kb),
--FOREIGN KEY (a2) REFERENCES A(a2) -- This is also an issue
);
INSERT INTO B VALUES (101, 's',12);
INSERT INTO B VALUES (102, 's',23);
INSERT INTO B VALUES (103, 's',34);
INSERT INTO B VALUES (104, 'm',45);
INSERT INTO B VALUES (105, 'm',56);
INSERT INTO B VALUES (106, 'b',67);
INSERT INTO B VALUES (107, 'b',78);
INSERT INTO B VALUES (108, 'b',89);
INSERT INTO B VALUES (109, 'b',90);
EDIT:
The second issue is that your foreign key from table B is referencing a non-unique column in table A. Foreign keys define a many-to-one relationship, but your data indicates a potentially many-to-many relationship. The first three "s" rows in table B each match all three "s" rows in table A. While you can define a JOIN in a query that matches ON B.a2 = A.a2, you can't use a foreign key to define this relationship.

SQL Server, self referencing foreign compound key

I have a table with columns task_id (pk), client_id, parent_task_id, title. In other words, tasks are owned by clients, and some tasks have child tasks.
For example, client 7 may have a task "wash the car," with child tasks "vacuum carpet" and "wipe dashboard."
I want a constraint so that a task and its children are always owned by the same client.
Through a bit of experimentation, to do this, I created a self-referencing foreign key (client_id, parent_task_id) referencing (client_id, task_id). At first I received an error (There are no primary or candidate keys in the referenced table that match the referencing column list in the foreign key.) So I added a unique key for columns task_id, client_id. Now it seems to work.
I am wondering if this is the best solution (or at least reasonable one) to enforce this constraint. Any thoughts would be appreciated. Thanks much!
A 'parent' record would not need a [parent_task_id]
TASK ID | CLIENT ID | PARENT TASK ID | TITLE
1 | 7 | NULL | wash the car
(To find all of your parent records, SELECT * FROM TABLE WHERE [parent_task_id] is null)
A 'child' record would need a [parent_task_id], but not a [client_id] (because, as you stipulate, a child has the same client as it's parent).
TASK ID | CLIENT ID | PARENT TASK ID | TITLE
2 | NULL | 1 | vacuum carpent
3 | NULL | 1 | wipe dashboard
In this way, your self-referencing foreign key is all the constraint you need. No constraint / rule concerning [client_id] on child records is necessary, because all [client_id] values on child records will be ignored, in favor of the [client_id] on the parent record.
For example, if you want to know what the [client_id] is for a child record:
SELECT
c.task_id,
p.client_id,
c.title
FROM
table p --parent
INNER JOIN table c --child
ON p.task_id = c.parent_task_id
UPDATE
(How to query for the client ID of a grand-child)
--Create and populate your table (using a table var in this sample)
DECLARE #table table (task_id int, client_id int, parent_task_id int, title varchar(50))
INSERT INTO #table VALUES (1,7,NULL,'wash the car')
INSERT INTO #table VALUES (2,NULL,1,'vacuum carpet')
INSERT INTO #table VALUES (3,NULL,1,'wipe dashboard')
INSERT INTO #table VALUES (4,NULL,2,'Step 1: plug-in the vacuum')
INSERT INTO #table VALUES (5,NULL,2,'Step 2: turn-on the vacuum')
INSERT INTO #table VALUES (6,NULL,2,'Step 3: use the vacuum')
INSERT INTO #table VALUES (7,NULL,2,'Step 4: turn-off the vacuum')
INSERT INTO #table VALUES (8,NULL,2,'Step 5: empty the vacuum')
INSERT INTO #table VALUES (9,NULL,2,'Step 6: put-away the vacuum')
INSERT INTO #table VALUES (10,NULL,3,'Step 1: spray cleaner on the rag')
INSERT INTO #table VALUES (11,NULL,3,'Step 2: use the rag')
INSERT INTO #table VALUES (12,NULL,3,'Step 3: put-away the cleaner')
INSERT INTO #table VALUES (13,NULL,3,'Step 4: toss the rag in the laundry bin')
--Determine which grandchild you want the client_id for
DECLARE #task_id int
SET #task_id = 8 -- grandchild's ID to use to find client_id
--Create your CTE (this is the recursive part)
;WITH myList (task_id, client_id, parent_task_id, title)
AS
(
SELECT a.task_id, a.client_id, a.parent_task_id, a.title
FROM #table a
WHERE a.task_id = #task_id
UNION ALL
SELECT a.task_id, a.client_id, a.parent_task_id, a.title
FROM #table a
INNER JOIN myList m
ON a.task_id = m.parent_task_id
)
--Query your CTE
SELECT task_id, client_id, title FROM myList WHERE client_id is not null
In this example, I used a granchild's task_id (8 -- 'empty the vacuum') to find it's highest-level parent, which holds the client_id.
You can remove the WHERE clause from the last step if you want to see each parent, parent's parent, and so on up to the first-parent's record.

Order guarantee for identity assignment in multi-row insert in SQL Server

When using a Table Value Constructor (http://msdn.microsoft.com/en-us/library/dd776382(v=sql.100).aspx) to insert multiple rows, is the order of any identity column populated guaranteed to match the rows in the TVC?
E.g.
CREATE TABLE A (a int identity(1, 1), b int)
INSERT INTO A(b) VALUES (1), (2)
Are the values of a guaranteed by the engine to be assigned in the same order as b, i.e. in this case so they match a=1, b=1 and a=2, b=2.
Piggybacking on my comment above, and knowing that the behavior of an insert / select+order by will guarantee generation of identity order (#4: from this blog)
You can use the table value constructor in the following fashion to accomplish your goal (not sure if this satisfies your other constraints) assuming you wanted your identity generation to be based on category id.
insert into thetable(CategoryId, CategoryName)
select *
from
(values
(101, 'Bikes'),
(103, 'Clothes'),
(102, 'Accessories')
) AS Category(CategoryID, CategoryName)
order by CategoryId
It depends as long as your inserting the records in one shot . For example after inserting if you delete the record where a=2 and then again re insert the value b=2 ,then identity column's value will be the max(a)+1
To demonstrate
DECLARE #Sample TABLE
(a int identity(1, 1), b int)
Insert into #Sample values (1),(2)
a b
1 1
2 2
Delete from #Sample where a=2
Insert into #Sample values (2)
Select * from #Sample
a b
1 1
3 2

Insert values or ignore row if a constraint fails

In SQL Server 2008, is there a way to insert rows while omitting those rows that cause a foreign key constraint to fail?
E.g. I have an insert statement similar to this:
insert into tblFoo(id, name, parent_id, desc) values
(1, 'a', 1, null),
(2, 'c', 3, 'blah'),
....;
parent_id is a fk to another table. How can I then get sql server to skip rows on which the fk column is invalid?
Update I would like to get this to work automatically, without first having to filter out those rows that violates the fk constraint. The reason for that is because the insert statements are generated by a program so it is not known beforehand which foreign keys exist on each table.
Is a weird situation you got there but you can insert the values to a temporary table and then select only the values with a valid FK.
something like:
declare #tempTable table (
id int,
name nvarchar(50) ,
parent_id int ,
[desc] nvarchar(50)
)
insert into #tempTable values
(1, 'a', 1, null),
(2, 'c', 3, 'blah')
insert into tblFoo(id, name, parent_id, [desc])
select tempTable.* from #tempTable as tempTable
inner join parent_id on parent_id.id = tempTable.parent_id
One way would be to use an Instead Of trigger on Inserts and Updates. You could then evaluate each row being updated before the actual write to the DB takes place. I'm generally not a huge fan of triggers, but you seem to have an unusual requirement here.