SQL Server : how to insert into 2 different tables - sql

This seems like a really simple answer, but for some reason I can't wrap my head around how I should accomplish this...
I've got two different tables:
Campaign
Campaign_Customer
Where dbo.Campaign is all of the details of the campaign, and dbo.Campaign_Customer is basically a summary table that contains the CampaignID, CustomerID, CreationDate, and DeletionDate (if there is one)
So, when I go to insert a new record into my dbo.Campaign table, I need to be able to use the CampaignID that it generates to create a record into my dbo.Campaign_Customer table.
I know this is possible, but I don't know the proper way to do so. Any help?

You will need to have 2 separate insert statements within transaction. If your campaign Id is identity column you can get its value using scope_identity function in select after first insert
BEGIN TRANSACTION
INSERT INTO dbo.Campain (x, y, z) VALUES (x, y, z)
DECLARE #id int
SELECT #id = scope_identity()
INSERT INTO dbo.Campaign_Customer (CampaignId, x, y) VALUES (#id, x, y)
COMMIT

You could create a trigger to do this:
CREATE TRIGGER [Campaign_Trigger] on dbo.Campaign
AFTER INSERT AS
INSERT INTO dbo.Campaign_Customer (CampaignId, x, y)
VALUES (t.CampaignID, t.x, t.y)
FROM Inserted t

Related

Replace value by fkey after moving data to related table

A numeric column should be extended to hold multiple values, i.e. reference some different entity. SQL only (Postgres specifically if no standard solution available).
Schema now:
Table X with columns ID, VAL, STUFF
Table Y with columns ID, VAL1, VAL2
What I want to achieve:
Table X with columns ID, YID, STUFF
Table Y won't be altered (neither existing data touched)
Table Y gets inserts for all rows of table X where X.VAL should be inserted as Y.VAL1. Y.ID auto-incremented, Y.VAL2 may remain NULL. Table X should then be updated to hold Y's ID as foreign key X.YID instead of the actual value X.VAL that is now stored in Y.VAL1.
Somehow I think it has to be possible to achieve that with a clean SQL-only solution. What I've found so far:
create some PG/SQL script: just loop over table X, insert the stuff to table Y row by row returning the ID and updating table X
plain SQL: get the number of entries in table Y, INSERT INTO Y with SELECT FROM X ... ORDER BY ID, INSERT INTO X with SELECT FROM Y ... skipping the number of entries that have been there before so the order should remain stable. I really don't like that solution. Sounds dirty to me.
Any suggestions? Or better go with PG/SQL?
TIA
There is a third option: a single SQL statement. Postgres allows DML within a CTE. So create a CTE that performs the insert and returns the generated id. Use the returned id in the main query which updates the original table. This then does what you are looking for in a single SQL statement.
with cte as
( insert into y(val1)
select val
from x
returning y.id, y.val1
)
update x
set val = cte.id
from cte
where x.val = cte.val1;
Then assuming you want to maintain referential integrity:
alter table x
add constraint x2y_fk
foreign key (val)
references y(id) ;
See Demo: Note: The demo copies both val and stuff from table x into table y. This was strictly for demonstration purposes and is not necessary.

Oracle - How to bulk insert values in one query with an array of values to insert

I have these tables:
A {
id,
name
}
B {
id,
name,
aId
}
aId is a foreign key to table A
So I have a list of strings that need to be inserted into table B.
List: names['name1', 'name2']
Also I have a select statement that fetches me a list of A.id's.
List: id's[1, 2]
The problem is that Table B needs to have rows inserted to it for every A.id that was queried and at the same time the string list should be taken into consideration.
End result would be that I get id's (1, 2) for instance, so 4 rows are created (2 for each id because every id needs to insert all the string in the list):
insert into b (id, name, aId) values (b_id_seq.nextval, name1, 1)
insert into b (id, name, aId) values (b_id_seq.nextval, name2, 1)
insert into b (id, name, aId) values (b_id_seq.nextval, name1, 2)
insert into b (id, name, aId) values (b_id_seq.nextval, name2, 2)
What I have gotten to work is write the select statement which returns an array of id's. I also tried to implement a select with these 2 lists but without luck.
First attempt was to use IN clause inside of specific column value part and second attempt was to use INSERT ALL. Could not figure out how to generate the insert statements dynamically in the last case tough.
How would one solve this kind of INSERT statement?
This sounds like a good candidate to use some PL/SQL. You can do dynamic queries with this... but truth be told you probably don't need to and can get away with some cursor usage.
create proc myproc(inListName in customType)
as
--this cursor would get you your list of IDs...
cursor1 is
select id from table;
c1row cursor1%rowtype;
begin
--this cursor opens and is effectively your "outer loop"
open cursor1;
LOOP
-- Retreive one row.
FETCH cursor1 INTO c1row;
EXIT WHEN cursor1%NOTFOUND;
--not exact syntax here.. but you get the idea...
FOR name IN inListNames
LOOP
insert into b (id, name, aId) values (b_id_seq.nextval, name, c1row.id);
END LOOP;
END LOOP;
close cursor1;
end
/
NOTE: This is untested and just off the top of my head... but it would work with something along these lines. If you're doing this with a lot of data, I'd recommend you instead do a BULK INSERT (just google Oracle bulk insert...) and use that instead, as performance wise it works better.
NOTE2: This is using a custom datatype to PASS in a parameter that would contain your list of names. Here is a link to show you how you can set up a user defined type to do that.
Passing an array of data as an input parameter to an Oracle procedure
Alternatively, if that's too much over-kill for you or you feel it's to over-engineered, maybe you can somehow run a query that would get you those list of names? If that's the case, you could just create a second cursor to get that list of names and iterate over that.

Shorthand for inserting row into history table with SQL

I've got a trigger which copies a row whenever updated or deleted into a history table.
As of now I'm doing:
INSERT INTO history (column_x, column_y, column_z) VALUES (X, Y, Z);
Is it possible to shorthand it with:
INSERT INTO history VALUES (OLD)
The above does not work, but it gives an idea of what I'm looking for.
The columns match exactly as I've created the history table with:
CREATE TABLE history (LIKE original)
You should have some primary key defined in the table. Then you can do the insert select statement:
INSERT INTO history
SELECT * FROM notHistory WHERE ID = #ID

Insert into a row at specific position into SQL server table with PK

I want to insert a row into a SQL server table at a specific position. For example my table has 100 rows and I want to insert a new row at position 9. But the ID column which is PK for the table already has a row with ID 9. How can I insert a row at this position so that all the rows after it shift to next position?
Relational tables have no 'position'. As an optimization, an index will sort rows by the specified key, if you wish to insert a row at a specific rank in the key order, insert it with a key that sorts in that rank position. In your case you'll have to update all rows with a value if ID greater than 8 to increment ID with 1, then insert the ID with value 9:
UPDATE TABLE table SET ID += 1 WHERE ID >= 9;
INSERT INTO TABLE (ID, ...) VALUES (9, ...);
Needless to say, there cannot possibly be any sane reason for doing something like that. If you would truly have such a requirement, then you would use a composite key with two (or more) parts. Such a key would allow you to insert subkeys so that it sorts in the desired order. But much more likely your problem can be solved exclusively by specifying a correct ORDER BY, w/o messing with the physical order of the rows.
Another way to look at it is to reconsider what primary key means: the identifier of an entity, which does not change during that entity lifetime. Then your question can be rephrased in a way that makes the fallacy in your question more obvious:
I want to change the content of the entity with ID 9 to some new
value. The old values of the entity 9 should be moved to the content
of entity with ID 10. The old content of entity with ID 10 should be
moved to the entity with ID 11... and so on and so forth. The old
content of the entity with the highest ID should be inserted as a new
entity.
Usually you do not want to use primary keys this way. A better approach would be to create another column called 'position' or similar where you can keep track of your own ordering system.
To perform the shifting you could run a query like this:
UPDATE table SET id = id + 1 WHERE id >= 9
This do not work if your column uses auto_increment functionality.
No, you can't control where the new row is inserted. Actually, you don't need to: use the ORDER BY clause on your SELECT statements to order the results the way you need.
DECLARE #duplicateTable4 TABLE (id int,data VARCHAR(20))
INSERT INTO #duplicateTable4 VALUES (1,'not duplicate row')
INSERT INTO #duplicateTable4 VALUES (2,'duplicate row')
INSERT INTO #duplicateTable4 VALUES (3,'duplicate rows')
INSERT INTO #duplicateTable4 VALUES (4,'second duplicate row')
INSERT INTO #duplicateTable4 VALUES (5,'second duplicat rows')
DECLARE #duplicateTable5 TABLE (id int,data VARCHAR(20))
insert into #duplicateTable5 select *from #duplicateTable4
delete from #duplicateTable4
declare #i int , #cnt int
set #i=1
set #cnt=(select count(*) from #duplicateTable5)
while(#i<=#cnt)
begin
if #i=1
begin
insert into #duplicateTable4(id,data) select 11,'indian'
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
end
else
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
set #i=#i+1
end
select *from #duplicateTable4
This kind of violates the purpose of a relational table, but if you need, it's not really that hard to do.
1) use ROW_NUMBER() OVER(ORDER BY NameOfColumnToSort ASC) AS Row to make a column for the row numbers in your table.
2) From here you can copy (using SELECT columnsYouNeed INTO ) the before and after portions of the table into two separate tables (based on which row number you want to insert your values after) using a WHERE Row < ## and Row >= ## statement respectively.
3) Next you drop the original table using DROP TABLE.
4) Then you use a UNION for the before table, the row you want to insert (using a single explicitly defined SELECT statement without anything else), and the after table. By now you have two UNION statements for 3 separate select clauses. Here you can just wrap this in a SELECT INTO FROM clause calling it the name of your original table.
5) Last, you DROP TABLE the two tables you made.
This is similar to how an ALTER TABLE works.
INSERT INTO customers
(customer_id, last_name, first_name)
SELECT employee_number AS customer_id, last_name, first_name
FROM employees
WHERE employee_number < 1003;
FOR MORE REF: https://www.techonthenet.com/sql/insert.php

How can I associate values in a table with ranges in another table?

Consider the following simple two table setup:
drop table ranges;
drop table entries;
create table ranges (x integer, y integer, label varchar);
create table entries (v integer);
insert into ranges values(1, 5, "range1");
insert into ranges values(8, 10, "range2");
insert into ranges values(20, 30, "range3");
insert into entries values(0);
insert into entries values(3);
insert into entries values(8);
insert into entries values(12);
insert into entries values(23);
Now, the query
select * from entries as a, ranges as b
where a.v between b.x and b.y;
will give me
v|x|y|label
3|1|5|range1
8|8|10|range2
23|20|30|range3
That is, values in entries which do not fall into any of the ranges will not appear in the results.
How can I write a query that will return a row for each entry in entries so that I get
v|x|y|label
0|NULL|NULL|NULL
3|1|5|range1
8|8|10|range2
12|NULL|NULL|NULL
23|20|30|range3
preferably in generic SQL (I am using PROC SQL in SAS).
select *
from entries as a
left outer join ranges as b
on (a.v between b.x and b.y);
And a +1 from me for providing sensible and usable DDL and INSERT statements. It's a pleasure to help you.