SQL update if exist and insert else and return the key of the row - sql

I have a table named WORD with the following columns
WORD_INDEX INT NOT NULL AUTO_INCREMENT,
CONTENT VARCHAR(255),
FREQUENCY INT
What I want to do is when I try to add a row to the table if a row with the same CONTENT exits, I want to increment the FREQUENCY by 1. Otherwise I want to add the row to the table. And then the WORD_INDEX in the newly inserted row or updated row must be returned.
I want to do this in H2 database from one query.
I have tried 'on duplicate key update', but this seems to be not working in H2.
PS- I can do this with 1st making a select query with CONTENT and if I get a empty result set, makeing insert query and otherwise making a update query. But as I have a very large number of words, I am trying to optimize the insert operation. So what I am trying to do is reducing the database interactions I am making.

Per your edited question .. you can achieve this using a stored procedure like below [A sample code]
DELIMITER $$
create procedure sp_insert_update_word(IN CONTENT_DATA VARCHAR(255),
IN FREQ INT, OUT Insert_Id INT)
as
begin
declare #rec_count int;
select #rec_count = count(*) from WORD where content = CONTENT_DATA;
IF(#rec_count > 0) THEN
UPDATE WORD SET FREQUENCY = FREQUENCY + 1 where CONTENT = CONTENT_DATA;
SELECT NULL INTO Insert_Id;
else
INSERT INTO WORD(CONTENT, FREQUENCY) VALUES(CONTENT_DATA, FREQ);
SELECT LAST_INSERT_ID() INTO Insert_Id;
END IF;
END$$
DELIMITER ;
Then call your procedure and select the returned inserted id like below
CALL sp_insert_update_word('some_content_data', 3, #Insert_Id);
SELECT #Insert_Id;
The above procedure code essentially just checking that, if the same content already exists then perform an UPDATE otherwise perform an INSERT. Finally return the newly generated auto increment ID if it's insert else return null.

First try to update frequency where content = "your submitted data here". If the affected row = 0 then insert a new row. You also might want make CONTENT unique considering it will always stored different data.

Related

Do While loop based on conditions in SQL Server [duplicate]

This question already has answers here:
How to loop statements in SQL Server
(2 answers)
Closed 4 years ago.
How to implement a while loop in SQL server based on the below condition.
Then I need to execute select statement which returns ITEM_CODE ,It may have multiple rows too.
What I want to do inside the while loop is for each ITEM_CODE I need to get data from other tables (including joins ) and insert those data to a table .This loop will get end the count based on the first statements return.
Sample query structure:
SELECT ITEM_CODE //While loop must execute the count of this rows
FROM 'TABLE_1'
WHERE ITEM_AVAILABILITY = 'TRUE'
This statement will return a single row or may return multiple rows .
I need to pass this ITEM_CODE to the while loop each time .Inside the while loop I will get the values from multiple tables and insert it to another table.
WHILE (#COUNT <>0){
//Need to have the ITEM_CODE while looping each time.
//Get data from multiple tables and assign to variables (SET #VARIABLES)
//Insert statement
IF (#COUNT = #COUNT)
BREAK;
}
Is it possible with SQL server ,If Yes,please help me to fix this .
Try this:
DECLARE #DataSource TABLE
(
[ITEM_CODE] VARCHAR(12)
);
INSER INTO #DataSource ([ITEM_CODE])
SELECT ITEM_CODE //While loop must execute the count of this rows
FROM 'TABLE_1'
WHERE ITEM_AVAILABILITY = 'TRUE';
DECLARe #CurrentItemCode VARCHAR(12);
WHILE(EXISTS (SELECT 1 FROM #DataSource))
BEGIN;
SELECT TOP 1 #CurrentItemCod = [ITEM_CODE]
FROM #DataSource
--
--
DELETE FROM #DataSource
WHERE [ITEM_CODE] = #CurrentItemCod;
END;
The idea is to perform a loop while there is data in our buffer table. Then in each iteration of the loop, get one random item code value, perform your internal operations and delete the value from the buffer.
Some people are using row ID column in the buffer table, for example INT IDENTITY(1,1) and are selecting the first element, doing the internal operations and then deleting by id. But you need to know the count of the records and to increment the id with each iteration - something like for cycle.

batch update a column using sql

I have a database table (db2) with a very large number of rows (a couple million). I need to change the datatype of one of the columns.
In DB2 LUW there does not seem to be a way to directly change the datatype of a column (ALTER TABLE ALTER COLUMN SET DATA TYPE does not work). So I am creating a new column, copying data to it and dropping the old column.
Since its not a good idea to do a direct update on the table, I'm creating a procedure which will update and commit 10000 rows at a time.
Given this, I have the following questions:
What is the best way to carry out the update here? - as far as I can tell, the cursor allows iteration over 1 row at a time. Is updating 10000 rows one at a time, then committing, and repeating until the table is updated the correct way to do it?
Is there any better way to handle the original issue of changing the data type of a column in a simpler way?
DB2 has a feature called LOAD FROM CURSOR that allows for fast migration of data.
The following is an example of this using LOAD FROM CURSOR:
-- this is the original table
CREATE TABLE TEST (
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
TEXT VARCHAR(50)
)#
CREATE TABLE TEST_NEW (
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
TEXT CLOB(5000000)
)#
DECLARE C1 CURSOR FOR SELECT ID, TEXT FROM TEST#
LOAD FROM C1 OF CURSOR INSERT INTO TEST_NEW (ID,TEXT)#
DROP TABLE TEST#
RENAME TABLE TEST_NEW TO TEST#
In addition, the following procedure can also be used (commit after every 10000 records):
-- this is the original table
CREATE TABLE TEST (
ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
TEXT VARCHAR(50)
)#
CREATE OR REPLACE PROCEDURE MIGRATE_TEST()
LANGUAGE SQL
BEGIN
DECLARE EOF INT DEFAULT 0;
DECLARE CUR_COUNT INT DEFAULT 0;
DECLARE CUR_ID INT DEFAULT 0;
DECLARE C CURSOR WITH HOLD FOR
SELECT ID FROM TEST WHERE TEXT_NEW IS NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET EOF = 1;
OPEN C;
FETCH_LOOP: LOOP
FETCH FROM C INTO CUR_ID;
IF EOF <> 0 THEN
LEAVE FETCH_LOOP;
END IF;
UPDATE TEST
SET TEXT_NEW = TEXT
WHERE ID = CUR_ID ;
SET CUR_COUNT = CUR_COUNT + 1;
IF CUR_COUNT >= 10000 THEN
CALL DBMS_OUTPUT.PUT_LINE('COMMITTING');
COMMIT WORK;
SET CUR_COUNT = 0;
END IF;
END LOOP FETCH_LOOP;
COMMIT WORK;
CLOSE C;
END#

Retrieve initial table in AFTER INSERT trigger SQL

I use this code to check if an element of the new entry is equal to an element of previously inserted data.
CREATE TRIGGER trig1 ON Table1
AFTER INSERT
AS
DECLARE trigcursor CURSOR FOR
SELECT Name FROM INSERTED
DECLARE #Name1 varchar(80)
OPEN trigcursor;
FETCH NEXT FROM trigcursor INTO #Name1
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM Table1 WHERE Name= #Name1)
BEGIN
...
END
FETCH NEXT FROM trigcursor INTO #Name1
END
The problem is that for some reason the new entry exists also in the table Table1, not only in INSERTED. So the condition is always true. Can you help me why this happens? Is there a way to retrieve only the initial table without the new entry in it? Thanks!
Your trigger is AFTER INSERT on table Table1. It should be BEFORE INSERT if you expect not to find the record in the table.
Alternative: use INSTEAD OF INSERT trigger.
OR
Add another column that accepts null. Make it a number column so that it will be fast. Do not insert any value in it on the insert. Then, in the AFTER INSERT TRIGGER, the rows that have that column empty are the new ones. The ones that have the column filled with something are the old ones.
Then update empty columns with value.
eg: add column mark
After insert, look for the name:
SELECT * FROM Table1 WHERE Name= #Name1 and mark is not null
Once you found out whether or not it existed before, update everything with something:
update table1 set mark = 1 where mark is null

How to apply Update if an item exists and Insert otherwise

How to create a procedure that goes from the top of the table and compares the value to null
- If a match is found, insert an element in this position.
- If not, the element is inserted into a new row
I need to correct a second row which contains null values in 4 last columns regardless of values in the Id and PropertyId columns
Here is a screenshot of my DB
Here is a samples of data:
Now it works so, which is not suitable to me, instead it should update the row with null values like on the last screenshot
But the next entry should overwrite the value of NULL for Item, ItemId, InstanceId and Instance
Write a stored procedure like:
create procedure INSERT_OR_UPDATE as
begin
if exists ( select * from Numerations where <your condition> )
begin
update Numerations set < ... > where < ... >
end
else
begin
insert into Numerations values <...>
end
end
You have to check the syntax because I cannot test my code right now.

SQL Table Locking

I have an SQL Server locking question regarding an application we have in house. The application takes submissions of data and persists them into an SQL Server table. Each submission is also assigned a special catalog number (unrelated to the identity field in the table) which is a sequential alpha numeric number. These numbers are pulled from another table and are not generated at run time. So the steps are
Insert Data into Submission Table
Grab next Unassigned Catalog
Number from Catalog Table
Assign the Catalog Number to the
Submission in the Submission table
All these steps happen sequentially in the same stored procedure.
Its, rate but sometimes we manage to get two submission at the same second and they both get assigned the same Catalog Number which causes a localized version of the Apocalypse in our company for a small while.
What can we do to limit the over assignment of the catalog numbers?
When getting your next catalog number, use row locking to protect the time between you finding it and marking it as in use, e.g.:
set transaction isolation level REPEATABLE READ
begin transaction
select top 1 #catalog_number = catalog_number
from catalog_numbers with (updlock,rowlock)
where assigned = 0
update catalog_numbers set assigned = 1 where catalog_number = :catalog_number
commit transaction
You could use an identity field to produce the catalog numbers, that way you can safely create and get the number:
insert into Catalog () values ()
set #CatalogNumber = scope_identity()
The scope_identity function will return the id of the last record created in the same session, so separate sessions can create records at the same time and still end up with the correct id.
If you can't use an identity field to create the catalog numbers, you have to use a transaction to make sure that you can determine the next number and create it without another session accessing the table.
I like araqnid's response. You could also use an insert trigger on the submission table to accomplish this. The trigger would be in the scope of the insert, and you would effectively embed the logic to assign the catalog_number in the trigger. Just wanted to put your options up here.
Here's the easy solution. No race condition. No blocking from a restrictive transaction isolation level. Probably won't work in SQL dialects other than T-SQL, though.
I assume their is some outside force at work to keep your catalog number table populated with unassigned catalog numbers.
This technique should work for you: just do the same sort of "interlocked update" that retrieves a value, something like:
update top 1 CatalogNumber
set in_use = 1 ,
#newCatalogNumber = catalog_number
from CatalogNumber
where in_use = 0
Anyway, the following stored procedure just just ticks up a number on each execution and hands back the previous one. If you want fancier value, add a computed column that applies the transform of choice to the incrementing value to get the desired value.
drop table dbo.PrimaryKeyGenerator
go
create table dbo.PrimaryKeyGenerator
(
id varchar(100) not null ,
current_value int not null default(1) ,
constraint PrimaryKeyGenerator_PK primary key clustered ( id ) ,
)
go
drop procedure dbo.GetNewPrimaryKey
go
create procedure dbo.GetNewPrimaryKey
#name varchar(100)
as
set nocount on
set ansi_nulls on
set concat_null_yields_null on
set xact_abort on
declare
#uniqueValue int
--
-- put the supplied key in canonical form
--
set #name = ltrim(rtrim(lower(#name)))
--
-- if the name isn't already defined in the table, define it.
--
insert dbo.PrimaryKeyGenerator ( id )
select id = #name
where not exists ( select *
from dbo.PrimaryKeyGenerator pkg
where pkg.id = #name
)
--
-- now, an interlocked update to get the current value and increment the table
--
update PrimaryKeyGenerator
set #uniqueValue = current_value ,
current_value = current_value + 1
where id = #name
--
-- return the new unique value to the caller
--
return #uniqueValue
go
To use it:
declare #pk int
exec #pk = dbo.GetNewPrimaryKey 'foobar'
select #pk
Trivial to mod it to return a result set or return the value via an OUTPUT parameter.