I have one process which executes copy statement, which loads data to 'table1' from a file. I have another process which is executing alter table statement on 'table2'. But the 2nd query gets blocked until copy statement is running. How come postgreSQL blocks the alter table statement? Checked using a query
SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS current_statement_in_blocking_process
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
The output was:
blocked_pid,blocked_user,blocking_pid,blocking_user,blocked_statement,current_statement_in_blocking_process
4669,user91,4182,user91,ALTER TABLE "part_me***" DETACH PARTITION "part_met***11";DROP TABLE IF EXISTS "part_meta****11";
COPY table2_name
(customer_**,**_customer_id,part_**,price,quantity,valid_from,valid_to,last_updated)
FROM
STDIN
WITH
(
HEADER TRUE,
FORMAT csv
)
"
Why does postgesql block alter table query?
Related
From below script, I have obtained the list of tables that need to be updated the statistics.
SELECT [sch].[name] + '.' + [so].[name] AS [TableName] ,
[ss].[name] AS [Statistic],
[sp].[last_updated] AS [StatsLastUpdated] ,
[sp].[rows] AS [RowsInTable] ,
[sp].[rows_sampled] AS [RowsSampled] ,
[sp].[modification_counter] AS [RowModifications]
FROM [sys].[stats] [ss]
JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id]
JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id]
OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id],
[ss].[stats_id]) sp
WHERE [so].[type] = 'U'
AND [sp].[modification_counter] > 0
The above query returns the table "dbo.LoginHistory" table as a result.
To perform the stat update I have executed below query.
UPDATE STATISTICS [dbo.LoginHistory] WITH FULLSCAN;
But it returns an error saying Table 'dbo.LoginHistory' does not exist.
When I execute SELECT * FROM dbo.LoginHistory then it returns data (Which means the table exists).
Any idea why I see the error when running stats?
Remove brackets [].
Use dbo.LoginHistory instead of [dbo.LoginHistory]
UPDATE STATISTICS dbo.LoginHistory WITH FULLSCAN;
I am trying to write a stored procedure to compute the differences between two input tables.
Stored procedure is used to calculate differences between two tables (both tables have the same predefined table structure), the stored procedure will provide records added, removed or updated when comparing table 1 to table 2.
Example:
table 1 New has 3 records: A, B and C
table 2 has 3 records: B', C and D
B' denotes a change to one or multiple fields within the record B
The output of this stored procedure call will be
A-addition
B-update
D-Removal
I have written a query to compute the difference between two tables, but finding it hard to translate to stored procedure.
Table structure:
X varchar (10)
Y int
Z datetime
SELECT
table1.*, ChangeType = 'Addition'
FROM
table1
WHERE
NOT EXISTS (SELECT *
FROM table2
WHERE table1.x = table2.x)
UNION ALL
SELECT
table2.*, ChangeType = 'Removal'
FROM
table2
WHERE
NOT EXISTS (SELECT *
FROM table1
WHERE table1.x = table2.x)
UNION ALL
SELECT
table1, ChangeType = 'Update'
FROM
table2
INNER JOIN
table1 ON table1.x = table2.x
WHERE
table1.Y <> table2.Y OR table1.Z <> table2.Z
Please also include the stored procedure execution script as well.
I think you are looking for the MERGE sentence. You can put table1 as target and table2 as source based on certain values and decide what to do in case the match or not: https://msdn.microsoft.com/en-us/library/bb510625.aspx
In your case it would be something like:
MERGE table1 AS target
USING table2 AS source (x, y, z)
ON (target.x= source.x)
WHEN MATCHED
--do something
WHEN NOT MATCHED BY TARGET
--do something different
WHEN NOT MATCHED BY SOURCE
--something else
As to how to receive a table as a parameter in a SP, you need to follow the next steps:
Create a DATA TYPE
CREATE TYPE tableExample (X varchar (10),
Y int,
Z datetime)
Pass it to the SP:
CREATE PROC sp_mysp #table1 tableExample, #table2 tableExample
AS ...
I prefer a single pass, using a case statement to classify the action.
CREATE PROCEDURE CompareTables
AS
BEGIN
SELECT ChangeType = CASE
WHEN table2.x IS NULL THEN
'Addition'
WHEN table1.x IS NULL THEN
'Removal'
WHEN table1.Y <> table2.Y
OR table1.Z <> table2.Z THEN
'Update'
ELSE
'No Change'
END,
table1.*,
table2.*
FROM table2
FULL OUTER JOIN table1
ON table1.x = table2.x
WHERE table2.x IS NULL
OR table1.x IS NULL
OR NOT ( table1.Y = table2.Y
AND table1.Z = table2.Z
);
END;
The below code is getting error in SQL
Incorrect syntax near the keyword 'Select'.
This is my code for merge where InstalledSoftwareList is a used defined table.
MERGE [dbo].[TableName] AS TargetTable
USING UDTableName AS SourceTable
ON (TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
AND TargetTable.PackageId = (SELECT Id FROM [PackagesDummyTable] SP WHERE SP.[Version] = SourceTable.[Version] AND SP.[Name] = SourceTable.[Name])
)
WHEN NOT MATCHED BY TARGET -- If the records in the Customer table is not matched?-- then INSERT the record
THEN INSERT ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
Select SourceTable.Guid,SP.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId
FROM [PackagesDummyTable] SP WHERE SP.[Version] = SourceTable.[Version] AND SP.[Name] = SourceTable.[Name];
I was referring this https://msdn.microsoft.com/en-us/library/bb510625.aspx. And my syntax seems to be right.
Can anyone help me on this. I am using SQL Azure.
As #gotqn said, If you only need to prcess the new data, you can just you insert into statement.
If it's required that you must you MERG INTo, you can change your script to below
MERGE [dbo].[TableName] AS TargetTable
USING (
SELECT UN.[EId],UN.[MId],SP.ID ,UN.Guid,SP.PackageId,UN.CName,UN.UUID,UN.MAC,UN.Date,UN.isUninstalled
,UN.DataCapturedTime,UN.SGuid, UN.UniqueId
FROM UDTableName AS UN AS
INNER JOIN [PackagesDummyTable] SP ON SP.[Version] = UN.[Version] AND SP.[Name] = UN.[Name]
) SourceTable
ON TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
AND TargetTable.PackageId = SourceTable.Id
WHEN NOT MATCHED BY TARGET -- If the records in the Customer table is not matched?-- then INSERT the record
THEN INSERT ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
VALUES( SourceTable.Guid,SourceTable.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId)
;
MERGE is nice if you want to do more than one CRUD operations. In this case, we only need to insert new records. Could you try something like this:
INSERT INTO ([Guid], [PackageId], [CName], [UUID], [MAC], [Date], [isUninstalled], [LastUpdatedDateTime], [DataCapturedTime], [SGuid], [UniqueId], [MId], [EId])
SELECT SourceTable.Guid,SP.PackageId,SourceTable.CName,SourceTable.UUID,SourceTable.MAC,SourceTable.Date,SourceTable.isUninstalled,GETUTCDATE(),SourceTable.DataCapturedTime,SourceTable.SGuid, SourceTable.UniqueId, SourceTable.MId, SourceTable.EId
-- we need these two tables in order to import data
FROM UDTableName AS SourceTable
INNER JOIN [PackagesDummyTable] SP
ON SP.[Version] = SourceTable.[Version]
AND SP.[Name] = SourceTable.[Name]
-- we are joing this table in order to check if there is new data for import
LEFT JOIN [dbo].[TableName] AS TargetTable
ON TargetTable.[EId] = SourceTable.[EId]
AND TargetTable.[MId] = SourceTable.[MId]
-- we are importing only the data that is new
WHERE TargetTable.PackageId IS NULL;
I currently have a stored procedure that compares my target table (Ticket_Report) to my data source table (New_Tickets).
I am using a MERGE INTO statement to compare these two. When it finds a match between the two tables, it updates the current row in the target table with the corresponding info from the source table. If it dosent find a match, it inserts that data from the source table into the target table.
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN
UPDATE SET
T1.TICKET_NO = T2.TICKET_NO,
T1.ASSIGNED_GROUP = T2.ASSIGNED_GROUP,
T1.ASSIGNEE = T2.ASSIGNEE,
T1.FNAME = T2.FNAME,
T1.LNAME = T2.LNAME
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
I need to change this, so that when match is found on the Ticket Number, instead up just updating it, I need to A.)replace the current row in the Target table by deleting it, then B.)inserting the corresponding Row from the source table.
I currently have
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.Ticket_NO=T2.Ticket_NO
WHEN MATCHED THEN DELETE
//Now I need to replace what I deleted with the row from the source table
Which will delete the row from the Target Table. Now I want to Insert the corresponding Row from the Source Table. I am having trouble trying to do multiple things inside the WHEN MATCHED clause. Does anyone know how I can accomplish this?
*Side note: When matched, I could Insert the row from the Source, but then I how would I delete the original?
Strictly solution For your boss
First put the matched records into one temp table
Next use Merge query to delete the matched records from target table and Insert the unmatched records
Finally insert the records from temp table to target table
Try something like this
select * into #temp
from #New_Tickets T2
where exists(select 1
from Ticket_REPORT T1
where T1.Ticket_NO=T2.Ticket_NO)
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
insert into Ticket_REPORT (col1,col2,..)
select col1,col2,..
from #temp
Note :
What has to be done is delete the matched records from target table and insert all then records from source table to target table
One approach is to subquery your MERGE statement. You can filter the output based on the action (INSERT, UPDATE or DELETE). The filtered records can then be INSERTED, UPDATED or DELETED.
This is a common technique for loading slowly changing dimensions into a data warehouse.
My example uses the follow temp tables:
SETUP
/* Create and populate sample tables.
*/
CREATE TABLE #NewTicket
(
Id INT
)
;
CREATE TABLE #TicketReport
(
Id INT
)
;
INSERT INTO #NewTicket
(
Id
)
VALUES
(1),
(2),
(3)
;
INSERT INTO #TicketReport
(
Id
)
VALUES
(3),
(4),
(5)
;
In the example 1, 2 and 3 appear in new ticket. 3, 4 and 5 appear in ticket report. 3 is deleted by the MERGE statement and INSERTED by the outer query.
EXAMPLE
/* Filter the results from the sub query
* for deleted records.
* These are then appended in the main
* outer query.
*/
INSERT INTO #TicketReport
(
Id
)
SELECT
Id
FROM
(
/* MERGE statments can be used as a sub query.
* You'll need the OUTPUT clause for this to work.
* The column $action describes what happened to each record.
*/
MERGE
#TicketReport AS t
USING #NewTicket AS s ON s.Id = t.Id
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
Id
)
VALUES
(
Id
)
OUTPUT
$action,
s.*
) AS r
WHERE
[$Action] = 'DELETE'
;
/* View the final result.
*/
SELECT
*
FROM
#TicketReport
ORDER BY
Id
;
Sorry for my english.
I have 2 tables:
Table1
id
table2_id
num
modification_date
and
Table2
id
table2num
I want to make a trigger which after insert or delete in Table1 updates the last value num in Table2.table1lastnum.
My trigger:
CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
AFTER INSERT OR DELETE ON table1
FOR EACH ROW
BEGIN
IF INSERTING then
UPDATE table2
SET table2num = :new.num
WHERE table2.id = :new.table2_id;
ELSE
UPDATE table2
SET table2num = (SELECT num FROM (SELECT num FROM table1 WHERE table2_id = :old.table2_id ORDER BY modification_date DESC) WHERE ROWNUM <= 1)
WHERE table2.id = :old.table2_id;
END IF;
END TABLE1_NUM_TRG;
But after delete in Table1 I have error:
ORA-04091: table BD.TABLE1 is mutating, trigger/function may not see it
ORA-06512: at "BD.TABLE1_NUM_TRG", line 11
ORA-04088: error during execution of trigger 'BD.TABLE1_NUM_TRG'
What am I doing wrong?
What you've run into is the classic "mutating table" exception. In a ROW trigger Oracle does not allow you to run a query against the table which the trigger is defined on - so it's the SELECT against TABLE1 in the DELETING part of the trigger that's causing this issue.
There are a couple of ways to work around this. Perhaps the best in this situation is to use a compound trigger, which would look something like:
CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
FOR INSERT OR DELETE ON TABLE1
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblTABLE2_IDS NUMBER_TABLE;
BEFORE STATEMENT IS
BEGIN
tblTABLE2_IDS := NUMBER_TABLE();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF INSERTING THEN
UPDATE TABLE2 t2
SET t2.TABLE2NUM = :new.NUM
WHERE t2.ID = :new.TABLE2_ID;
ELSIF DELETING THEN
tblTABLE2_IDS.EXTEND;
tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
IF tblTABLE2_IDS.COUNT > 0 THEN
FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
UPDATE TABLE2 t2
SET t2.TABLE2NUM = (SELECT NUM
FROM (SELECT t1.NUM
FROM TABLE1 t1
WHERE t1.TABLE2_ID = tblTABLE2_IDS(i)
ORDER BY modification_date DESC)
WHERE ROWNUM = 1)
WHERE t2.ID = tblTABLE2_IDS(i);
END LOOP;
END IF;
END AFTER STATEMENT;
END TABLE1_NUM_TRG;
A compound trigger allows each timing point (BEFORE STATEMENT, BEFORE ROW, AFTER ROW, and AFTER STATEMENT) to be handled. Note that the timing points are always invoked in the order given. When an appropriate SQL statement (i.e. INSERT INTO TABLE1 or DELETE FROM TABLE1) is executed and this trigger is fired the first timing point to be invoked will be BEFORE STATEMENT, and the code in the BEFORE STATEMENT handler will allocate a PL/SQL table to hold a bunch of numbers. In this case the numbers to be stored in the PL/SQL table will be the TABLE2_ID values from TABLE1. (A PL/SQL table is used instead of, for example, an array because a table can hold a varying number of values, while if we used an array we'd have to know in advance how many numbers we would need to store. We can't know in advance how many rows will be affected by a particular statement, so we use a PL/SQL table).
When the AFTER EACH ROW timing point is reached and we find that the statement being processed is an INSERT, the trigger just goes ahead and performs the necessary UPDATE to TABLE2 as this won't cause a problem. However, if a DELETE is being performed the trigger saves the TABLE1.TABLE2_ID into the PL/SQL table allocated earlier. When the AFTER STATEMENT timing point is finally reached, the PL/SQL table allocated earlier is iterated through, and for each TABLE2_ID found the appropriate update is performed.
Documentation here.
You have to define a before trigger for delete.Try using two triggers
CREATE OR REPLACE TRIGGER INS_TABLE1_NUM_TRG
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table2
SET table2num = :new.num
WHERE table2.id = :new.table2_id;
END INS_TABLE1_NUM_TRG;
CREATE OR REPLACE TRIGGER DEL_TABLE1_NUM_TRG
BEFORE DELETE ON table1
FOR EACH ROW
BEGIN
UPDATE table2
SET table2num = (SELECT num FROM
(SELECT num FROM table1 WHERE table2_id = :old.table2_id
ORDER BY modification_date DESC)
WHERE ROWNUM <= 1)
WHERE table2.id = :old.table2_id;
END DEL_TABLE1_NUM_TRG;
#psaraj12 answer is the best IMHO, but in the DELETE trigger I would use the :OLD notation as the inside query is unnecessary and will slow trigger significantly:
...
BEFORE DELETE ON table1
FOR EACH ROW
UPDATE table2
SET table2num = :OLD.num
WHERE table2.id = :OLD.table2_id;
...