How to Insert a New Record Based on Previous Query - sql

I need to update some columns from the table (stats) & if that fails or succeeds (no matter what the result is) then create a new record in the table (notifications) without using a semicolon (;) Between the 1st & 2nd query
*First Query: *
UPDATE stats SET delivered = delivered + 1 WHERE id = 1
*Second Query: *
INSERT INTO notifications (text) VALUES ('table stats updated')
(The example above is not working.)
Please note that
I cannot use procedures, functions or triggers, only a simple query.
I cannot use a semicolon (;) between the two queries.
The update query needs to be executed first.
I've already tried some queries with no result, such asĀ 
UPDATE stats SET delivered = delivered + 1 WHERE id =
(INSERT INTO notifications (text) VALUES ('table stats updated') RETURNING id)

If you want all the commands in a single transaction statement then you can use multiple WITHs to execute many commands in a single transaction :
WITH query1 AS (
UPDATE stats SET delivered = delivered + 1 WHERE id = 1 RETURNING delivered
),
query2 AS (
INSERT INTO notifications (text)
SELECT delivered FROM query1 RETURNING id
)
SELECT * FROM query1 , query2 ;
Rules example :
CREATE RULE rul_delivered_insertnotification AS ON UPDATE
TO delivered WHERE true
DO (
INSERT INTO notifications (notification) VALUES (NEW.delivered) ;
) ;
CREATE RULE rul_notification_deliverednotification AS ON INSERT
TO notifications WHERE true
DO (
SELECT NEW.notification ;
);
The on delivered update below will go to the Rule , insert into notification and then notification will retrieve a select with the field notification inserted.
Table with rules return and no rules on second image:

Related

The following query use for check duplicate data in table then update or insert row

I have the following query use for check duplicate data in table. If match data then update row else insert new row. In my case I have already one matched row in att_log table where emp_id=19.1.0121 and where mp_pk_id='32' AND att_date='2021-10-01', so result should be SET holiday=H in the matched row. But the DECLARE statement run without error and in console show affected row:1, but no change occur in data base, holiday not set to "H".
DECLARE c_emp_id att_log.emp_id%type;
BEGIN
SELECT emp_id
INTO c_emp_id
FROM att_log
WHERE emp_id='19.1.0121'
AND emp_pk_id='32'
AND att_date='2021-10-01' ;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
UPDATE att_log
SET holiday = 'H',
updated_at = '2021-08-22'
WHERE emp_id='19.1.0121'
AND att_date='2021-10-01';
WHEN NO_DATA_FOUND THEN
INSERT INTO att_log (emp_id, emp_pk_id, att_date, holiday,login_time, logout_time)
VALUES ('19.1.0121', '32', '2021-10-01','H','','');
COMMIT WORK;
END;
If I run the query separately without DECLARE statement then data row change happen, but with the above DECLARE statement no change happen in data row in the ORACLE table. What is my fault! Sorry, I am new to ORACLE, and also sorry for poor English.
A MERGE operation can INSERT or UPDATE (and also DELETE) depending on whether the row exists or not.
Here's a working test case:
Test case / fiddle
Example of MERGE:
CREATE TABLE logs (
id NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL
, text VARCHAR2(20) UNIQUE
, q int DEFAULT 1
);
INSERT INTO logs (text) VALUES ('A');
INSERT INTO logs (text) VALUES ('B');
INSERT INTO logs (text) VALUES ('C');
MERGE INTO logs USING (SELECT 'B' AS text FROM dual) cte ON (cte.text = logs.text)
WHEN MATCHED THEN UPDATE SET logs.q = logs.q + 1
WHEN NOT MATCHED THEN INSERT (logs.text)
VALUES (cte.text)
;
Result:
and now we can do this for several existing rows and new rows at once:
MERGE INTO logs USING (
SELECT text FROM logs WHERE text > 'A' UNION
SELECT 'Z' FROM dual
) cte ON (cte.text = logs.text)
WHEN MATCHED THEN UPDATE SET logs.q = logs.q + 1
WHEN NOT MATCHED THEN INSERT (logs.text)
VALUES (cte.text)
;
New Result:
This example will either INSERT a new row when the rows in cte do not exist in logs, and UPDATE any existing rows in logs when matches are found, by incrementing q.

how to delete multiple records from a table (getting multiple values in input)

I want to delete multiple records from table at the same time.
Sample input:
{"INPUT":{"ID":"2200038,2200039,2200073,2200019"}}
Input will be provided from the application i.e.,
ID can be random - it gets changed based on requirements.
delete from mytable
where id = ....?
I want to delete multiple ID's coming from the input at the same time.
To delete multiple rows at once with different IDs, one approach is to use IN:
DELETE FROM mytable
WHERE ID IN (2200038,2200039,2200073,2200019)
Here's some documentation and further examples: http://www.postgresqltutorial.com/postgresql-in/
You may extract the ids from your json string as an array and delete those using ANY operator
WITH t AS
(
SELECT '{"INPUT":{"ID":"2200038,2200039,2200073,2200019"}}' AS input
)
DELETE FROM mytable
WHERE id = ANY ( SELECT unnest(
String_to_array(input::json->'INPUT'->>'ID',',')::int[])
FROM t );
Demo
Here's a demo using a Bind variable for input in psql. Note that UNNEST was not needed here.
\set input '{"INPUT":{"ID":"2200038,2200039,2200073,2200019"}}'
knayak=# DELETE FROM mytable WHERE
id = ANY( String_to_array(:'input'::json->'INPUT'->>'ID',',')::int[] )
DELETE 2
Maybe some dynamic sql will help
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "DELETE FROM tablename WHERE ID IN(?);";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
inputdata varchar; -- remove unwanted parts of the string
EXEC SQL EXECUTE mystmt USING inputdata;

Stored Procedure/SQL script that allows me to add a record to a table

I have 2 tables, one with all the email data, and another with all the specific member email data that one creates a row if an email has been read.
Once an email has been read by a member its added to the Member_email_read table (which is created and populated based on all read emails).
I am trying to set (on mass) all of the messages to read (this would populate the Member_email_read table) but whilst I can add them one at a time
(see the stored procedue below), I am unable to add them on mass.
The two tables are Email, which holds a record for every email into the system. The other table is a table of all email that the member has read. Each time an email
is read a record is added to the Member_email_read table. They are assiocited on the message_id (and both should use the same user_id). The two tables are as follows -
SELECT [member_email_id]
,[member_email_FK_message_id]
,[member_email_FK_user_id]
,[member_email_status]
,[member_email_read_datetime]
,[member_email_delete_datetime]
FROM [MemberData].[dbo].[Member_email_read]
SELECT[message_id]
,[email_catlogue_num]
,[email_FK_user_id]
,[Email_time]
,[email_content]
,[Email_created_date]
FROM [MemberData].[dbo].[Email]
To set all the messages (for a certain user) to unread all I would have to do is delete every record from that table for that user, which can be done with the following:
DELETE FROM [MemberData].[dbo].[Member_email_read]
WHERE [member_email_FK_message_id_FK_user_id] ='2';
I am basically looking for the reverse of this delete.
I have created a Stored procedure that allows for the setting of ONE specific email to be set to read, however this stored procedure (when executed) requires the member to enter a
email_id, message_id, user_id, status, read_datetime & delete_datetime.
CREATE PROCEDURE [dbo].[set_member_email_to_read]
#member_email_id int,
#member_email_FK_message_id int,
#member_email_FK_user_id int
#member_email_status varchar(1),
#member_email_read_datetime dateTime,
#member_email_delete_datetime dateTime
as
if not exists (Select * from [dbo].[Member_email_read] where [member_email_FK_message_id] = #member_email_FK_message_id) begin
insert into [dbo].[Member_email_read]
(
[member_email_FK_message_id]
,[member_email_FK_user_id]
,[member_email_status]
,[member_email_read_datetime]
,[member_email_delete_datetime]
)
values
(
#member_email_FK_message_id,
#member_email_FK_user_id
#member_email_status,
#member_email_read_datetime,
#member_email_delete_datetime
)
SELECT Convert(int,SCOPE_IDENTITY()) As InsertedID
end else begin
update [dbo].[Member_email_read] set
[member_email_FK_message_id] = #member_email_FK_message_id
,[member_email_FK_user_id] = #member_email_FK_user_id
,[member_email_status] = #member_email_status
,[member_email_read_datetime] = #member_email_read_datetime
,[member_email_delete_datetime] = #member_email_delete_datetime
where [member_email_FK_user_id] = #member_email_FK_user_id
if (##ERROR = 0) begin
SELECT Convert(int,#member_email_FK_user_id) As InsertedID
end
end
GO
I was hoping to create a stored procedure (or general SQL script) that would allow me to enter in
a user_id and then allow for all emails for that user to change from unread to read (populate the Member_email_read table).
You can try using MERGE to perform a bulk insert/update from your Email table into your Member_email_read table:
MERGE [MemberData].[dbo].[Member_email_read] AS tgt
USING (
SELECT message_id, user_id, 'R', null, CURRENT_TIMESTAMP
FROM [MemberData].[dbo].[Email]
WHERE user_id = #UserId
) AS src (MessageId, UserId, Status, ReadDate, DeleteDate)
ON (
tgt.member_email_FK_message_id = src.message_id
AND tgt.member_email_FK_user_id = src.user_id
)
WHEN MATCHED THEN
UPDATE SET tgt.member_email_status = src.Status,
tgt.member_email_read_datetime = src.ReadDate,
tgt.member_email_delete_datetime = src.DeleteDate
WHEN NOT MATCHED THEN
INSERT (member_email_FK_message_id, member_email_FK_user_id,
member_email_status, member_email_read_datetime, member_email_delete_datetime)
VALUES (src.MessageId, src.UserId, src.Status, src.ReadDate, src.DeleteDate)
;
This should work, though as I mentioned in my comment above, you should rethink your table design and simply add a read_datetime and delete_datetime (possibly a status column if you really need that as well) to your Email table, rather than having a whole separate table simply to hold records identifying a delete status.

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

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.

How to keep in a database the number of calls of each record?

For example, I want to know at any moment most popular records that usersĀ searched in the database.
I expect that for each record I need to introduce a new number field. Thus, the record will be like this:
key - value - counter
How I can to increase the value of counter inside a database?
I think it's something like calling a stored procedure while a query, but I'm not sure. Perhaps the question is quite simple, I'm just a beginner and I apologize in that case.
You should use a trigger for this. Triggers are commands that execute on events, everytime an INSERT, UPDATE or DELETE statement is executed, even if their calls do not modify any records. Due tot this, you can't directly create a trigger for updating the count field of a record when you SELECT (read) it.
But, you can try a workaround in which you also have a date field in your table, and update it everytime a record is called. Use your application to send this datetime value to the database, which will trigger an UPDATE.
By making an UPDATE statement, your trigger is called and this way you can add your code to modify the count column.
CREATE TRIGGER tafter AFTER INSERT OR DELETE ON tbl1 FOR EACH ROW UPDATE SET counter = counter + 1 where key = 'keyval';
Firstly, this sounds like an awful performance problem. Every time you select a record you have to update it if you're tracking the selects with a single number, which just stores total selects, otherwise you have to insert timestamped values into another table to be able to analyse when the rows were read.
Anyway, you can do this with a common table expression in which you update a counter in the table and return the results to the main query: http://sqlfiddle.com/#!1/1aa41/6
Code something like:
create table my_table(col1 varchar(30), col2 numeric, select_count numeric);
insert into my_table values ('A',1,0);
insert into my_table values ('B',2,0);
insert into my_table values ('C',3,0);
insert into my_table values ('D',4,0);
insert into my_table values ('E',5,0);
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'A'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'B'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'A'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select count(*)
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select sum(col2)
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select *
from upd;