Bigquery, with 3 conditions, update, update and insert and insert - google-bigquery

I need to solve a problem here, but I'm not finding a solution. I need to do an update if the record already exists and the sim is the same. If the record already exists and the sim is different, I also need to update the existing record, but I also need to insert this complete record with the new sim. And if the record does not exist, then I do a normal insert. Do you know how to do something like this in bigquery?
I tried this but it didn't work
MERGE `TABLE_TEST1` fn
USING `TABLE_TEST2` stg
ON stg.register = fn.register
WHEN MATCHED AND(
stg.sim = fn.sim
and fn.end_date is null
)
THEN
UPDATE SET
connection = stg.connection,
updated = current_timestamp(),
ds = stg.ds
WHEN MATCHED AND(
stg.sim != fn.sim
and fn.end_date is null
)
THEN
UPDATE SET
updated_at = current_timestamp(),
end_date = current_timestamp()
**this is wrong**
INSERT (
register,
site,
sim,
level,
events,
connection,
created,
ds
)
VALUES(
stg.register,
stg.site,
stg.sim,
level,
events,
connection,
current_timestamp(),
stg.ds
)
WHEN NOT MATCHED
THEN
INSERT (
register,
site,
sim,
level,
events,
connection,
created,
ds
)
VALUES(
stg.register,
stg.site,
stg.sim,
level,
events,
connection,
current_timestamp(),
stg.ds
);

Related

How to Insert a New Record Based on Previous Query

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:

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.

Trigger creation/modification to ensure field equals insertion date

I have a table named Customer and the column in question is dbupddate. This column should contain the datetime of the query that resulted in the record bein inserted.
I have already made a default constraint to getdate():
CREATE TABLE [dbo].[customer]
(
[dbupddate] [DATETIME] NOT NULL
CONSTRAINT [DF_customer_dbupddate] DEFAULT (GETDATE()),...
but this does not prevent someone ofaccidentally entering an irrelevant value.
How can I ensure the column dbupddate has the insert datetime?
I guess the answer will contain a trigger. In this case, consider the following already existing trigger, that should not have its effects lost/modified in any way:
CREATE TRIGGER [dbo].[customer_ins_trig]
ON [dbo].[customer]
AFTER INSERT
AS
BEGIN
DELETE u
FROM transfer_customer_unprocessed u
WHERE EXISTS (SELECT 1 FROM inserted i WHERE i.code = u.code)
INSERT INTO transfer_customer_unprocessed (code, dbupddate)
SELECT code, dbupddate
FROM inserted
END
Maybe I could add some lines to that one to suit my needs? Or maybe create another one?
In the procedure which is inserting the data, just don't provide a variable for that column. Granted someone could open SSMS if they have the rights and update it, but you could restrict this with access too.
Additionally, you may want to look into rowversion if this is part of a larger initiative to track changes.
Here's a trigger that does what you want, I think. Note that the user cannot control content going into InsertDate.
This is a reasonable approach for keeping "last updated" info for your data. However, #scsimon if you are doing this for other reasons, ROWVERSION is worth exploring, does not require a trigger, and will be much more performant.
DROP TABLE IF EXISTS Test;
GO
CREATE TABLE Test (
Id INT NOT NULL ,
Content NVARCHAR(MAX) NOT NULL ,
InsertDate DATETIME NULL
);
GO
CREATE TRIGGER TR_Test
ON Test
AFTER INSERT, UPDATE
AS BEGIN
UPDATE t SET t.InsertDate = GETDATE() FROM Test t INNER JOIN inserted i ON i.Id = t.Id;
END;
GO
INSERT Test VALUES (1, '1', NULL), (2, '2', NULL), (3, '3', NULL);
SELECT * FROM Test;
GO
UPDATE Test SET Id = 4, Content = 4 WHERE Id = 1;
UPDATE Test SET Id = 5, Content = 5, InsertDate = NULL WHERE Id = 2;
SELECT * FROM Test;
GO

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 Insert trigger to update INSERTED table values

I want to create an Insert trigger that updates values on all the inserted rows if they're null, the new values should be taken from a different table, according to another column in the inserted table.
I tried:
UPDATE INSERTED
SET TheColumnToBeUpdated =
(
SELECT TheValueCol FROM AnotherTable.ValueCol
WHERE AnotherTable.ValudCol1 = INSERTED.ValueCol1
)
WHERE ValueCol IS NULL
But I get this error:
Msg 286, Level 16, State 1, Procedure ThisTable_INSERT, Line 15
The logical tables INSERTED and DELETED cannot be updated.
How should I do that?
You need to update the destination table, not the logical table. You join with the logical table, though, to figure out which rows to update:
UPDATE YourTable
SET TheColumnToBeUpdated =
(
SELECT TheValueCol FROM AnotherTable.ValueCol
WHERE AnotherTable.ValudCol1 = INSERTED.ValueCol1
)
FROM YourTable Y
JOIN Inserted I ON Y.Key = I.Key
WHERE I.ValueCol IS NULL
You could change the trigger to an INSTEAD OF INSERT. This will let you check the incoming values and, if needed replace them with the values from your other table.
CREATE TRIGGER CoolTrigger
ON MyAwesomeTable
INSTEAD OF INSERT
AS
BEGIN
INSERT MyAwesomeTable (TheValueCol)
SELECT ISNULL(INSERTED.TheValueCol, AnotherTable.TheValueCol) AS TheValueCol
FROM INSERTED
JOIN AnotherTable ON INSERTED.ValueCol1 = AnotherTable.ValueCol1
END
NOTE: INSTEAD OF triggers do NOT cause recursion.
insert into output
(SELECT t1.ts - INTERVAL (SECOND(t1.ts)%10) SECOND,
t1.ts - INTERVAL (SECOND(t1.ts)%10) SECOND + INTERVAL 10 SECOND ,sum(t1.data),
FROM (select * from input
where unix_timestamp(ts) >= unix_timestamp('2000-01-01 00:00:10')
and unix_timestamp(ts) < unix_timestamp('2000-01-01 00:01:20')
)
as t1
GROUP BY UNIX_TIMESTAMP(t1.ts) DIV 10 );
This is where my output table is coming from.
So the insertion is not by values.
Im so sorry but I can't access my account from here (office),