TSQL Copy New Contents to Archive Table Only - sql-server

I have an ArchiveTable that I want to periodically copy any new records from OriginalTable. This is something I thought may work.
INSERT INTO OriginalTable
SELECT *
FROM ArchiveTable
WHERE NOT EXISTS (SELECT *
FROM OriginalTable ot
INNER JOIN ArchiveTable at ON ot.email = at.email)
Simply doing something like..
INSERT INTO ArchiveTable
SELECT * FROM OriginalTable
Of course, only works for the initial copy.

Your current query:
INSERT INTO OriginalTable
SELECT * FROM ArchiveTable
WHERE NOT EXISTS
(SELECT * FROM OriginalTable ot
INNER JOIN ArchiveTable at
ON ot.email = at.email)
Uses an EXISTS subquery that isn't related to the outer query. So it's saying, "if no row exists in the original table that has the same email as any row in the archive table, then insert everything in the archive table into the Original table."
Probably not what you want. You probably want to insert the specific rows that do not already exist in the original table. So you would want to correlate the subquery to the outer query:
INSERT INTO OriginalTable
SELECT * FROM ArchiveTable at
WHERE NOT EXISTS
(SELECT * FROM OriginalTable ot
WHERE ot.email = at.email)
This query says, "insert into the original table, any rows in the archive table where I don't already have the Email in the Original table".

Related

SQL Server : AFTER INSERT trigger and INSERT INTO

I want to create a trigger that will fill up my sales history base after firing in the ORDER table.
I am creating a specific order in regular data base and after that this order automatically goes to sales_history database.
Below part works properly.
When I create a new order in regular database my sales_history database is growing with new ID_ORDERS, hooray! :)
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order)
SELECT i.ID_ORDER FROM inserted i
END
The problem arises when I want join another table. The trigger stops working
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
inserted
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = inserted.ID_ORDER
END
I also tried this way, and still nothing :(
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
DECLARE #xyz AS numeric(18, 0)
SET #xyz = (SELECT inserted.ID_ORDER FROM inserted)
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
ORDER.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
ORDER
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = ORDER.ID_ORDER
WHERE
ORDER.ID_ORDER = #xyz
END
I want to create a trigger that will automatically fill up my sales history base after firing in ORDER table.
Your trigger is on the Order table, meaning SQL Server fires it after you insert records into the Order table. At which point, the relevant records in the Order_Details table couldn't have been inserted yet, because they have a foreign key to the Order table.
This is why an inner join between your inserted table and the Order_details table returns 0 rows.
If you want your sales_history from the order_details table, you have to populate it after you insert the records to the order_details table.
CREATE OR ALTER TRIGGER [dbo].[OrderDetails_AfterInsert]
ON [dbo].[ORDER_DETAILS]
AFTER INSERT
AS
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, inserted.ID_ITEM
FROM
inserted
INNER JOIN
[ORDER] ON [ORDER].ID_ORDER = inserted.ID_ORDER
WHERE [ORDER].ID_TYPE = 1 -- specific order type
As a side note: InsertTrig is bad name. Note the name of the trigger in my answer - it tells you exactly what this trigger is for, and on what table.

What is the "lifespan" of a postgres CTE expression? e.g. WITH... AS

I have a CTE I am using to pull some data from two tables then stick in an intermediate table called cte_list, something like
with cte_list as (
select pl.col_val from prune_list pl join employees.employee emp on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id' limit 100
)
Then, I am doing an insert to move records from the cte_list to another archive table (if they don't exist) called employee_arch_test
insert into employees.employee_arch_test (
select * from employees.employee where id in (select col_val::uuid from cte_list)
and not exists (select 1 from employees.employee_arch_test where employees.employee_arch_test.id=employees.employee.id)
);
This seems to work fine. The problem is when I add another statement after, to do some deletions from the main employee table using this aforementioned cte_list - the cte_list apparently no longer exists?
SQL Error [42P01]: ERROR: relation "cte_list" does not exist
the actual delete query:
delete from employees.employee where id in (select col_val::uuid from cte_list);
Can the cte_list CTE table only be used once or something? I'm running these statements in a LOOP and I need to run the exact same calls for about 2 or 3 other tables but hit a sticking point here.
A CTE only exists for the duration of the statement of which it's a part. I gather you have an INSERT statement with the CTE preceding it:
with cte_list
as (select pl.col_val
from prune_list pl
join employees.employee emp
on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id'
limit 100
)
insert into employees.employee_arch_test
(select *
from employees.employee
where id in (select col_val::uuid from cte_list)
and not exists (select 1
from employees.employee_arch_test
where employees.employee_arch_test.id = employees.employee.id)
);
The CTE is part of the INSERT statement - it is not a separate statement by itself. It only exists for the duration of the INSERT statement.
If you need something which lasts longer your options are:
Add the same CTE to each of your following statements. Note that because data may be changing in your database each invocation of the CTE may return different data.
Create a view which performs the same operations as the CTE, then use the view in place of the CTE. Note that because data may be changing in your database each invocation of the view may return different data.
Create a temporary table to hold the data from your CTE query, then use the temporary table in place of the CTE. This has the advantage of providing a consistent set of data to all operations.

Copy data from one table into another new table where columns are unknown

I'm trying to copy all data from Table1 into Table2.
I don't know what and how many columns are their in table 1. I mean I want to copy even column names from table 1 to table 2.
There is option like
insert *
into #table2
from Table1
but I even can't use this because there are many select query which has already been written at past. So I have to do something like this.
insert *
into #table2
from (select * from Table1)
This is throwing an error
Incorrect syntax near )
Try This:
Select * into #table2 from (select * from table1 ) as X
---To copy along with data..
select * into newtable from oldtable
--to copy only schema..
select * into newtable from oldtable where 1=2

SQL Delete clears the table instead of erroring

I have a piece of SQL which (you would think) wouldn't compile, but which instead deletes all rows from the target table.
Consider this setup:
create table TableA (ColumnA varchar(200));
create table TableB (ColumnB varchar(200));
insert TableA values ('A'),('B'),('C');
insert TableB values ('A');
Then the following sql:
--Returns all rows from TableA
select * from TableA;
--Does not error (ColumnA does not exist on TableB)
delete TableA where ColumnA in (select ColumnA from TableB)
--No Rows are returned
select * from TableA;
The delete statement above causes all rows to be removed from TableA, rather than erroring that ColumnA doesn't exist in TableB
There's a SQL Fiddle demontrating this here: http://www.sqlfiddle.com/#!3/9d883/6
It seems that the ColumnA from TableA is being picked up, but expected it to be "out of scope".
Why is this?
That works as expected, due to the correlation between ColumnA in the inner query to the outer.
This commonly used correlated query pattern is valid
DELETE TableA WHERE NOT EXISTS (select * from TableB where TableB.ID=TableA.ID)
It removes TableA entries that don't have a dependent record in TableB.
It shows that you can reference TableA columns in a correlated query. In your query
delete TableA where ColumnA in (select ColumnA from TableB)
The inner query is producing
one row for each record in TableB
one column for each row, whose value is ColumnA from outer query
So the DELETE goes through
While I understand the confusion, it is behaving as it should. ColumnA is still "in scope". In fact you could join on it in your subquery if you wanted. The brackets don't limit the scope, but from a readability standpoint I can see the confusion that it creates.
This is another example of why it's a good idea to always prefix your column names with the table name (or alias).

How to get the latest inserted identity from a table?

I would like to retrieve the latest inserted or even updated identity from a table. But whenever I write the following code there is no results at all.
select *
from PersonHowEducation prh
inner join HowzeEducation he on prh.HowzeEducationId = he.HowzeEducationId
where he.HowzeEducationId = ##IDENTITY
What shall I really do ?
If this were part of a batch script, and you have only just inserted it in the same session, then you would use SCOPE_IDENTITY() to retrieve the last inserted HowzeEducationId value.
select * from PersonHowEducation prh inner join HowzeEducation he on
prh.HowzeEducationId=he.HowzeEducationId
where he.HowzeEducationId=SCOPE_IDENTITY()
However, if this is from a completely different session, one that DID NOT JUST perform the insert, then you can get the last record inserted using
select * from PersonHowEducation prh inner join HowzeEducation he on
prh.HowzeEducationId=he.HowzeEducationId
where he.HowzeEducationId=(select top(1) HowzeEducationId from HowzeEducation
order by HowzeEducationId desc)
Note: There could be rare cases where an identity insert does not create the largest id in the table, for example if someone decided a table should have 1 record of id= 1,000,000,001 and manually reseeded the identity back to 10,001.
Another alternative, which may fizz if it is possible for the record to be deleted after insertion.
select * from PersonHowEducation prh inner join HowzeEducation he on
prh.HowzeEducationId=he.HowzeEducationId
where he.HowzeEducationId=(SELECT IDENT_CURRENT('HowzeEducation'))

Resources