Oracle SQL: merge with more conditions than matched/not matched - sql

I'm needing some help with a MERGE command in Oracle. Basically I have this command but I want to optimize it a little more:
MERGE INTO swap USING dual ON (SELECT id FROM student WHERE number = '123')
WHEN MATCHED THEN
UPDATE SET swapped = 1, last_swap = sysdate
WHEN NOT MATCHED THEN
INSERT (student_id, swapped, last_swap) VALUES ((SELECT id FROM student WHERE number= '123'), 1, sysdate)
Right now this will insert or update a register on SWAP table. However, I would like to protect it from inserting NULL on the student_id if there's no student with that number on STUDENT table (I don't want to simply not allow NULL values on sudent_id of SWAP table).
Other thing, I'm repeating SELECT id FROM student WHERE number = '123' two times, how can I change this to execute it only once (store the result in an alias or something)?
Thanks a lot in advance!

Your merge statement should use the "Select from student" as the table you are merging from.
Can you post some sample data as well?
The below query is assuming that ID is the column on which you are merging and it cannot be null.
MERGE INTO swap
USING (SELECT id FROM student WHERE number = '123' and id is not null) stu
on (stu.id = swap.id)
WHEN MATCHED THEN
UPDATE SET swapped = 1, last_swap = sysdate
WHEN NOT MATCHED THEN
INSERT (student_id, swapped, last_swap) VALUES (stu.id , 1, sysdate);

Related

Insert new row of data in SQL table if the 2 column values do not exist

I have a PostgreSQL table interactions with columns
AAId, IDId, S, BasicInfo, DetailedInfo, BN
AAID and IDId are FK to values referencing other tables.
There are around 1540 rows in the ID table and around 12 in the AA table.
Currently in the interactions table there are only around 40 rows for the AAId value = 12 I want to insert a row for all the missing IDId values.
I have searched, but cant find an answer to inserting rows like this. I am not overly confident with SQL, I can do basics but this is a little beyond me.
To clarify, I want to perform a kind of loop where,
for each IDId from 1-1540,
if (the row with AAId = 12 and IDId(current IDId in the loop does not exist)
insert a new row with,
AAId = 12,
IDId = current IDId in the loop,
S = 1,
BasicInfo = Unlikely
DetailedInfo = Unlikely
Is there a way to do this in SQL?
Yes, this is possible. You can use data from different tables when inserting data to a table in Postgres. In your particular example, the following insert should work, as long as you have the correct primary/unique key in interactions, which is a combination of AAId and IDId:
INSERT INTO interactions (AAId, IDId, S, BasicInfo, DetailedInfo, BN)
SELECT 12, ID.ID, 1, 'Unlikely', 'Unlikely'
FROM ID
ON CONFLICT DO NOTHING;
ON CONFLICT DO NOTHING guarantees that the query will not fail when it tries to insert rows that already exist, based on the combination of AAId and IDId.
If you don't have the correct primary/unique key in interactions, you have to filter what IDs to insert manually:
INSERT INTO interactions (AAId, IDId, S, BasicInfo, DetailedInfo, BN)
SELECT 12, ID.ID, 1, 'Unlikely', 'Unlikely'
FROM ID
WHERE NOT EXISTS (
SELECT * FROM interactions AS i
WHERE i.AAId = 12 AND i.IDId = ID.ID
);

Insert a new record with a field containing total record in the table

I wonder if it exists a function that allows me to get total rec count of a table to for new record insertion into the table?
I have this table:
CREATE TABLE "TEST"
( "NAME" VARCHAR2(20 BYTE),
"ID" NUMBER,
"FLAG" NUMBER
) ;
Insert into TEST (NAME,ID,FLAG) values ('Ahlahslfh',1,1);
Insert into TEST (NAME,ID, FLAG) values ('Buoiuop',2,1);
Insert into TEST (NAME,ID, FLAG) values ('UOIP',12,0);
My intention is to issue a statement that is equivalent to this:
INSERT INTO TEST( NAME, ID, FLAG )
VALUES( 'TST', 3,1 );
The statement I used below generated error:
INSERT INTO TEST ( NAME, ID, FLAG )
VALUES ( 'TST', SELECT COUNT(*)+1 FROM TEST WHERE FLAG=1,1 );
Below is the final result I am expecting:
Is there a way around it? Of course, I can put them in a script, count the records into a variable and insert that variable into the field. I just wonder if there is more elegant solution and do this in 1 statement.
Thanks!
This is likely to be a very bad way to set an id. In general, I think you should use sequences/identity/auto_increment and not worry about gaps.
But, you can do what you want using parentheses -- these are needed for subqueries:
INSERT INTO TEST(NAME, ID, FLAG)
VALUES ('TST',
(SELECT COUNT(*)+1 FROM TEST WHERE FLAG = 1),
1
);
Or, alternatively:
INSERT INTO TEST(NAME, ID, FLAG)
SELECT 'TST', COUNT(*) + 1, 1
FROM TEST
WHERE FLAG = 1;
I must emphasize that this seems dangerous. It is quite possible that you will get duplicate ids. You should really let the database insert a new value and not worry about gaps.

merge statement when not matched by source then insert to another table

I have created two tables customersrc and customertemp with the columns:
customertemp
ID name age addr cityid isactive
34 Gi 24 Chennai 1 1
customersrc
CustomerId CustomerName CustomerAge CustomerAddress
1 Gi 24 madurai
2 Pa 23 Tirupur
3 MI 27 Tirupur
Now I need to insert pa and mi data value to the temp table bcz it is not matched with the rows of customertemp. And the row gi data will be updated which was matched.
I used the following MERGE statement
DECLARE #cityid INT SET #cityid=1
MERGE Temp.dbo.customersrc as src_customer
USING ( SELECT CustomerName,CustomerAge,CustomerAddress FROM customertemp) as temp_customer
ON src_customer.name=temp_customer.CustomerName
AND
src_customer.cityid=#cityid
WHEN MATCHED THEN
UPDATE SET
src_customer.age=temp_customer.CustomerAge,
src_customer.addr=temp_customer.CustomerAddress,
src_customer.isactive=1
WHEN NOT MATCHED BY SOURCE THEN
UPDATE SET src_customer.isactive=0 ; -- here i need the insert statement to insert in another table
Questions:
is it possible to write insert statement inside the when not matched by source query?
if it is not possible then how to achieve this using merge?
in a simple set theory I need to put the customersrc(table_B)-customertemp (table_A). B-A value into the another or temp table.
One of the main usages of the MERGE statement is to perform so called "UPSERTS" (Update matching records, insert new records), so it is definitely possible to do what you want. Just add the following to the last part of your MERGE statement:
WHEN NOT MATCHED BY TARGET THEN
INSERT (name, age, addr, cityid, isactive)
VALUES (CustomerName, CustomerAge, CustomerAddress, #cityid, 1)
If you also need to insert data into a 3rd table, depending on whether rows are updated or inserted, you can use the OUTPUT clause of the merge statement. Check out the documentation: http://technet.microsoft.com/en-us/library/ms177564.aspx
Me: Why do you want to insert to another table?
You: To show the user who are not in the customertemp table.
So your requirement is not to insert into another table. Your requirement is to get the missing users.
You could do that with a dummy UPDATE (SET SomeCol = SomeCol) and OUTPUT. But that is a hack that I would try to avoid.
It is probably easier to do this in two statements. Here's how you'd get the missing rows:
SELECT temp_customer.*
FROM (SELECT CustomerName,CustomerAge,CustomerAddress FROM customertemp) as temp_customer
LEFT JOIN customersrc ON src_customer.name=temp_customer.CustomerName AND src_customer.cityid=#cityid
WHERE customersrc.cityid IS NULL

Add rows to a table then loop back and add more rows for a different userid

I have a table with 4 columns - ID, ClubID, FitnessTestNameID and DisplayName
I have another table called Club and it has ID and Name
I want to add two rows of data to the 1st table for each club
I can write a statement like this, but can someone tell me how to create a loop so that I can insert the two rows, set the #clubid + 1 and then loop back again?
declare #clubid int
set #clubid = 1
insert FitnessTestsByClub (ClubID,FitnessTestNameID,DisplayName)
values (#clubid,'1','Height (cm)')
insert FitnessTestsByClub (ClubID,FitnessTestNameID,DisplayName)
values (#clubid,'2','Weight (kg)')
You can probably do this with one statement only. No need for loops:
INSERT INTO FitnessTestsByClub
(ClubID, FitnessTestNameID, DisplayName)
SELECT
c.ID, v.FitnessTestNameID, v.DisplayName
FROM
Club AS c
CROSS JOIN
( VALUES
(1, 'Height (cm)'),
(2, 'Weight (kg)')
) AS v (FitnessTestNameID, DisplayName)
WHERE
NOT EXISTS -- a condition so no duplicates
( SELECT * -- are inserted
FROM FitnessTestsByClub AS f -- and the statement can be run again
WHERE f.ClubID = c.ID -- in the future, when more clubs
) -- have been added.
;
The Table Value Constructor syntax above (the (VALUES ...) construction) is valid from version 2008 and later.
There is a nice article with lots of useful examples of how to use them, by Robert Sheldon: Table Value Constructors in SQL Server 2008

SQL INSERT statement for TWO TABLES at time with INNER JOIN

I have two tables hello and login_table and below is their structure
user_info
-------
some_id | name | address
login_table
-------
id | username | password
some_id and id are autoincrement indexes.
Now how can i use INSERT statement with INNER JOIN in SQL
at present, i want add below data with same some_id and id
`name` = John
`address` = wall street
`username` = john123
`password` = passw123
below code shows, what i have tried so far.
insert into login_table lt
INNER JOIN user_info ui ON ui.some_id = lt.id
(ui.name, ui.address, lt.username, lt.password)
values
('John', 'wall street', 'john123', 'passw123')
And this is not the one value, i want to add more than one value at a time.. how can i achieve.
thanks for help.
If you need to perform the two INSERT operations atomically, use a transaction:
START TRANSACTION;
INSERT INTO login_table (username, password) VALUES ('john123', 'passw123');
INSERT INTO user_info (name, address) VALUES ('John', 'wall street');
COMMIT;
N.B. Your storage engine must support transactions for this to work (e.g. InnoDB).
To insert multiple values into a table at once, use the multiple rows form of INSERT. As stated in the manual:
INSERT statements that use VALUES syntax can insert multiple rows. To do this, include multiple lists of column values, each enclosed within parentheses and separated by commas. Example:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
The values list for each row must be enclosed within parentheses. The following statement is illegal because the number of values in the list does not match the number of column names:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3,4,5,6,7,8,9);
VALUE is a synonym for VALUES in this context. Neither implies anything about the number of values lists, and either may be used whether there is a single values list or multiple lists.
Insert to two tables is impossible. The second part of your question is possible: you can insert multiple rows in one statement like this:
insert into some_table(col1, col2) values (1,2), (3,4), (5,6);
USE [ERPDb]
GO
INSERT INTO [AAA].[UserRole] ([UserId], [RoleId])
SELECT u.Id, (SELECT Id FROM [AAA].[Role] WHERE Title = 'Employee') FROM [AAA].[User] u
INNER JOIN [dbo].[BaseDealer] bd ON u.Id = bd.Id
WHERE bd.DealerType = 0
GO