How to combine three tables into a new table - sql

all with the same column headings and I would like to create one singular table from all three.
I'd also, if it is at all possible, like to create a trigger so that when one of these three source tables is edited, the change is copied into the new combined table.
I would normally do this as a view, however due to constraints on the STSrid, I need to create a table, not a view.
Edit* Right, this is a bit ridiculous but anyhow.
I HAVE THREE TABLES
THERE ARE NO DUPLICATES IN ANY OF THE THREE TABLES
I WANT TO COMBINE THE THREE TABLES INTO ONE TABLE
CAN SOMEONE HELP PROVIDE THE SAMPLE SQL CODE TO DO THIS
ALSO IS IT POSSIBLE TO CREATE TRIGGERS SO THAT WHEN ONE OF THE THREE TABLES IS EDITED THE CHANGE IS PASSED TO THE COMBINED TABLE
I CAN NOT CREATE A VIEW DUE TO THE FACT THAT THE COMBINED TABLE NEEDS TO HAVE A DIFFERENT STSrid TO THE SOURCE TABLES, CREATING A VIEW DOES NOT ALLOW ME TO DO THIS, NOR DOES AN INDEXED VIEW.
Edit* I Have Table A,Table B and Table C all with columns ORN, Geometry and APP_NUMBER. All the information is different so
Table A (I'm not going to give an example geometry column)
ORN ID
123 14/0045/F
124 12/0002/X
Table B (I'm not going to give an example geometry column)
ORN ID
256 05/0005/D
989 12/0012/X
Table C (I'm not going to give an example geometry column)
ORN ID
043 13/0045/D
222 11/0002/A
I want one complete table of all info
Table D
ORN ID
123 14/0045/F
124 12/0002/X
256 05/0005/D
989 12/0012/X
043 13/0045/D
222 11/0002/A
Any help would be greatly appreciated.
Thanks

If the creation of the table is a one time thing you can use a select into combined with a union like this:
select * into TableD from
(
select * from TableA
union all
select * from TableB
union all
select * from TableC
) UnionedTables
As for the trigger, it should be easy to set up a after insert trigger like this:
CREATE TRIGGER insert_trigger
ON TableA
AFTER INSERT AS
insert TableD (columns...) select (columns...) from inserted
Obviously you will have to change the columns... to match your structure.
I haven't checked the syntax though so it might not be prefect and it could need some adjustment, but it should give you an idea I hope.

If IDs are not duplicated it ill be easy to achieve it, in another case you can must add a OriginatedFrom column. You also can create a lot of instead off triggers (not only for insert but for delete and update) but that a lazy excuse for not refactoring the app.
Also you must pay attention for any reference for the data, since its a RELATIONAL model is likely to other tables are related to the table you are about to drop.

This is the code for create the table D
drop table D;
Select * into D from (select * from A Union all select* from B Union all select * from C);

Its rather simple Just Create Table_D First
CREATE TABLE_D
(
ORN INT,
ID VARCHAR(20),
Column3 Datatype
)
GO
Use INSERT statement to insert records into this table SELECTing and using UNION ALL operator from other three table.
INSERT INTO TABLE_D (ORN , ID, Column3)
SELECT ORN , ID, Column3
FROM Table_A
UNION ALL
SELECT ORN , ID, Column3
FROM Table_B
UNION ALL
SELECT ORN , ID, Column3
FROM Table_C
Trigger
You will need to create this trigger on all of the tables.
CREATE TRIGGER tr_Insert_Table_A
ON TABLE_A
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO TABLE_D (ORN , ID, Column3)
SELECT i.ORN , i.ID, i.Column3
FROM Inserted i LEFT JOIN TABLE_D D
ON i.ORN = D.ORN
WHERE D.ORN IS NULL
END
Read here to learn more about SQL Server Triggers

Related

Insert data from Table A to Table B, then Update Table A with Table B ID

I currently have two tables like the following:
Table A
TableAId
TableAPrivateField
CommonField1
CommonField2
CommonField..
TableBGeneratedId
1
datadatadata
datadatadata2
datadatadata3
d...
NULL
2
datadatadata5
datadatadata6
datadatadata7
d...
NULL
...
Table B
TableBId
CommonField1
CommonField2
CommonField..
...
What i want to do is insert into TableB some record fetched from TableA, and then update the column [TableBGenerateId] of TableA with the corresponding new Id from the inserted record in TableB.
I tried with declaring a Table Value Parameter and then use it with the OUTPUT clause, but i can't find a way to relate back to the original TableAId of the row that acted as the source for the insert
something like that:
DECLARE #InsertedTableB TABLE (
TableBId INT PRIMARY KEY
);
INSERT INTO TableB
OUTPUT inserted.TableBId INTO #InsertedTableB
SELECT CommonField1, CommonField2,..
FROM TableA
WHERE TableAPrivateField = 'MyCondition';
WITH NumberedTableA AS(
SELECT TableAId, ROW_NUMBER() OVER(ORDER BY TableAId) AS RowNum
FROM TableA
WHERE TableAPrivateField = 'MyCondition'
),
NumberedInsert AS(
SELECT TableBId, ROW_NUMBER() OVER(ORDER BY TableBId) AS RowNum
FROM #InsertedTableB
)
UPDATE TableA
SET GeneratedTableBId = NumberedInsert.TableBId
FROM TableA
JOIN NumberedTableA ON Table.TableAId = NumberedTableA.TableAId
JOIN NumberedInsert ON NumberedTableA.RowNum = NumberedTable.RowNum
My problem is that even thought the query works i have no guaranties that the order of the fetched records will be the same, so i would risk linking back the wrong Ids. I tried to figure out some different solutions, but the closest one i found was to temporarily add a column to TableB containing TableAId and then perform the update, but i disliked it because this operation needs to be executed frequently and it would be too performance demanding. Adding the column permanently also isn't an acceptable solution sadly.
Anyone has any suggestion on how solve this?
If you use MERGE rather than INSERT (but still only ever insert with the MERGE by using a condition that will never be met e.g. 1=0), you can capture both the ID from TableA, and the new ID from tableB in the OUTPUT clause and insert this to your table variable. Then use this table variable to update tableA:
DECLARE #InsertedTableB TABLE (TableBId INT PRIMARY KEY, TableAId INT NOT NULL);
MERGE INTO dbo.TableB AS b
USING
( SELECT TableAId, CommonField1, CommonField2
FROM dbo.TableA
WHERE TableAPrivateField = 'MyCondition'
) AS a
ON 1 = 0 -- <<<< Always false so will never match and only ever insert
WHEN NOT MATCHED THEN
INSERT (CommonField1, CommonField2)
VALUES (a.CommonField1, a.CommonField2)
OUTPUT inserted.TableBId, a.TableAId INTO #InsertedTableB (TableBId, TableAId);
UPDATE a
SET GeneratedTableBId = b.TableBId
FROM dbo.TableA AS a
INNER JOIN #InsertedTableB AS b
ON b.TableAId = a.TableAId;
Example on db<>fiddle
Whenever I post any answer that in anyway condones the use of MERGE it is met with at least one comment highlighting all of the bugs with it, so to pre-empt that: There are a lot of issues with using MERGE in SQL Server - I do not believe that any of those risks will apply in this scenario if you are (a) forcing an insert and (b) using a table as the target. So while I will always avoid MERGE where I can by using multiple statements, this is one scenario where I don't avoid it because I don't think there is a cleaner solution available without using MERGE. It is anecdotal, but I have used this method for years and have never once encountered an issue.

Copy data from one table to another - Ignore duplicates Postgresql

I am using Postgresql db. I have data in two tables. Table A has 10 records and Table B 5 records.
I would like to copy Table A data to Table B but only copy the new entries (5 records) and ignore the duplicates/already existing data
I would like to copy data from Table A to Table B where Table B will have 10 records (5 old records + 5 new records from Table A)
Can you please help me as to how can this be done?
Assuming id is your primary key, and table structures are identical(both table has common columns as number of columns and data type respectively), use not exists :
insert into TableB
select *
from TableA a
where not exists ( select 0 from TableB b where b.id = a.id )
If you are looking to copy rows unique to A that are not in B then you can use INSERT...SELECT. The SELECT statement should use the union operator EXCEPT:
INSERT INTO B (column)
SELECT column FROM A
EXCEPT
SELECT column FROM B;
EXCEPT (https://www.postgresql.org/docs/current/queries-union.html) compares the two result sets and will return the distinct rows present in result A but not in B, then supply these values to INSERT. For this to work both the columns and respective datatypes must match in both SELECT queries and your INSERT.
INSERT INTO Table_A
SELECT *
FROM Table_B
ON CONFLICT DO NOTHING
Here, the conflict will be taken based on your primary key.

compare primary/alias groups across two tables

Gday,
We have two tables that contain exactly the same structure. There are two columns "PrimaryAddress" and "AliasAddress". These are for email addresses and aliases. We want to find any records that need to be added to either side to keep the records in sync. The catch is that the primary name in one table might be listed as an alias in the other. The good news is that an address wont appear twice in the "AliasAddress" column.
TABLE A
PrimaryAddress~~~~~AliasAdress
chris#work~~~~~~~~~chris#home
chris#work~~~~~~~~~c#work
chris#work~~~~~~~~~theboss#work
chris#work~~~~~~~~~thatguy#aol
bob#test~~~~~~~~~~~test1#test
bob#test~~~~~~~~~~~charles#work
bob#test~~~~~~~~~~~chuck#aol
sally#mars~~~~~~~~~sally#nasa
sally#mars~~~~~~~~~sally#gmail
TABLE B
PrimaryAddress~~~~~AliasAdress
chris#home~~~~~~~~~chris#work
chris#home~~~~~~~~~c#work
chris#home~~~~~~~~~theboss#work
chris#home~~~~~~~~~thatguy#aol
bob#test~~~~~~~~~~~test1#test
bob#test~~~~~~~~~~~charles#work
sally#nasa~~~~~~~~~sally#mars
sally#nasa~~~~~~~~~sally#gmail
sally#nasa~~~~~~~~~ripley#nostromo
The expected result is to return the following missing records from both tables:
bob#test~~~~~~~~~~~chuck#aol
sally#nasa~~~~~~~~~ripley#nostromo
Note that the chris#* block is a total match because the sum of all the aliases (plus primary) is still the same regardless of which address is considered primary. It doesnt matter which address is primary as along as the sum of the entire primary group contains all entries in both tables.
I dont mind if this is run in two passes A->B and B->A but I just cant get my head around a solution.
Any help appreciated :)
drop TABLE #TABLEA
CREATE TABLE #TABLEA
([PrimaryAddress] varchar(10), [AliasAdress] varchar(12))
;
INSERT INTO #TABLEA
([PrimaryAddress], [AliasAdress])
VALUES
('chris#work', 'chris#home'),
('chris#work', 'c#work'),
('chris#work', 'theboss#work'),
('chris#work', 'thatguy#aol'),
('bob#test', 'test1#test'),
('bob#test', 'charles#work'),
('bob#test', 'chuck#aol'),
('sally#mars', 'sally#nasa'),
('sally#mars', 'sally#gmail')
;
drop TABLE #TABLEB
CREATE TABLE #TABLEB
([PrimaryAddress] varchar(10), [AliasAdress] varchar(15))
;
INSERT INTO #TABLEB
([PrimaryAddress], [AliasAdress])
VALUES
('chris#home', 'chris#work'),
('chris#home', 'c#work'),
('chris#home', 'theboss#work'),
('chris#home', 'thatguy#aol'),
('bob#test', 'test1#test'),
('bob#test', 'charles#work'),
('sally#nasa', 'sally#mars'),
('sally#nasa', 'sally#gmail'),
('sally#nasa', 'ripley#nostromo')
;
try the following
select a.PrimaryAddress,a.AliasAdress from #TABLEA a left join #TABLEB b on a.AliasAdress=b.AliasAdress or b.PrimaryAddress=a.AliasAdress
where b.PrimaryAddress is null
union all
select a.PrimaryAddress,a.AliasAdress from #TABLEB a left join #TABLEA b on a.AliasAdress=b.AliasAdress or b.PrimaryAddress=a.AliasAdress
where b.PrimaryAddress is null
So you want to compare table A and B, and find rows which are unqiue in either table. How about an outer join, followed by looking for NULL values:
SELECT ta.*, tb.*
FROM table_a ta
FULL OUTER JOIN table_b tb ON tb.PrimaryAddress = ta.PrimaryAddress
AND tb.AliasAddress = ta.AliasAddress
WHERE ta.PrimaryAddress IS NULL
OR tb.PrimaryAddress IS NULL
If I understand the question correctly, this should return what you ask for.
Here's how I did it, with a bit of throwing-hands-up-in-the-air at the end.
Step one, identify the sets of items to be compared. This is:
For a “primary” value, all values found in Alias
Including the “primary” value as well (to cover that nasa/nostromo case)
A set in a table (A or B) is identified by its primary value. What really makes it hard is that the primary value is not shared across the two tables (sally#mars, sally#nasa). So we can compare sets, but we have to be able to “go back” to the primary on each table separately (e.g. the stand-out from table B may be sally#nasa / ripley#nostroomo, but we have to add sally#mars / ripley#nostromo to table A)
Major problems arise if, in a table, a primary value appears as an alias for a different primary value (e.g. in table A, chris#work appears as an alias for bob#test). For the sake of sanity, I am going to assume this will not happen… but if it does, the problem becomes even harder.
This query works to add missing items in B that are not in A, where the PrimaryAddress is the same for both A and B:
;WITH setA (SetId, FullSet)
as (-- Complete sets in A
select PrimaryAddress, AliasAdress
from A
union select PrimaryAddress, PrimaryAddress
from A
)
,setB (SetId, FullSet)
as (-- Complete sets in B
select PrimaryAddress, AliasAdress
from B
union select PrimaryAddress, PrimaryAddress
from B
)
,NotInB (Missing)
as (-- What's in A that's not in B
select FullSet
from setA
except select FullSet -- This is the secret sauce. Definitely worth your time to read up on how EXCEPT works.
from setB
)
-- Take the missing values plus their primaries from A and load them into B
INSERT B (PrimaryAddress, AliasAdress)
select A.PrimaryAddress, nB.Missing
from NotInB nB
inner join A
on A.AliasAdress = nb.Missing
Run it again with the tables reversed (from “NotInB” on) to do the same for A.
HOWEVER
Doing so with your sample data for "in B not in A" will add (sally#nasa, ripley#nostromo) to A, and as that’s a different primary, it’d create a new set, and so does not solve the problem. It gets ugly quickly. Talking it out from here:
Takes two passes, one for A not in B, one for B not in A
For each pass, have to do two checks
First check is what’s above: what’s in A not in B where primary addresses match, and add it
Second check is ugly: what’s in A not in B where the primary addresses from A is NOT a primary address in B and, thus, must be an alias. Here, find A’s primary address in B’s alias list, get the primary key used for this set in B, and create the row(s) in B using those values.
OK, This is how we did it... As it was becoming a pain, we ran a procedure that added the primary address of each entry as an alias: xx#xx -> xx#xx so that all addresses were listed as aliases for each user. This is similar to what #Phillip Kelly did above. Then we ran the following code: (its messy but it works; in one pass too)
SELECT 'Missing from B:' as Reason, TableA.[primary] as APrimary, TableA.[alias] as AAlias, TableB.[primary] as BPrimary,TableB.[alias] as BAlias into #A FROM dbo.TableA LEFT OUTER JOIN TableB ON TableB.alias = TableA.alias
SELECT 'Missing from A:' as Reason,TableA.[primary] as APrimary, TableA.[alias] as AAlias, TableB.[primary] as BPrimary,TableB.[alias] as BAlias into #B FROM dbo.TableB LEFT OUTER JOIN TableA ON TableA.alias = TableB.alias
select * from #A
select * from #B
UPDATE #A
SET #A.APrimary = #B.BPrimary
FROM #B INNER JOIN #A ON #A.APrimary = #B.BPrimary
WHERE #A.BPrimary IS NULL
UPDATE #B
SET #B.BPrimary = #A.APrimary
FROM #B INNER JOIN #A ON #B.BPrimary = #A.BPrimary
WHERE #B.APrimary IS NULL
select * from #A
select * from #B
select * into #result from (
select Reason, BPrimary as [primary], BAlias as [alias] from #B where APrimary IS NULL
union
select Reason, APrimary as [primary], AAlias as [alias] from #A where BPrimary IS NULL
) as tmp
select * from #result
drop table #A
drop table #B
drop table #result
GO

SQL: How to dynamically loop & add N number of column with NULL value into temp table

Due to a certain requirement, I need to create two temp tables in Stored Procedure, after processing some data into them, I need to combine the two temp tables to show as one result set and generate into excel. So I'm thinking to use UNION when I want to show the final result set.
The issue is, the first temp table (Table A) is fixed to 20 columns, and the second temp table has 50 columns (Table B). My plan is, before processing data for Table A, I want to add 30 nullable columns and insert data for first 20 columns, and the rest is all NULL
After I process the data for Table B, I use UNION to combine Table A & B so that they will show as one result set.
What I can think of right now is to hard code some columns that are destined to have null values when I declare the temp table:
Declare #tmpTableA table (
....
ProcessDate datetime,
Mode int,
Col21 varchar(10)
Col22 varchar(10)
....
Col50 varchar(50)
)
When I insert data into Table A, I have to manually add null from Col21 onwards
Insert into(.... Col21, Col22, Col23....)
Values (.... NULL, NULL, NULL....)
After I complete processing data for Table A & B, I use UNION to merge Table A and B
Select *....Col49,Col50 From Table A
Union
Select *....CompleteDate,ContactPerson From Table B
Instead of hard-coding Col21 to Col50 into Table A, is there any elegant way to achieve that like using while loop to dynamically add N number of columns into Table A?
EDIT:
According to latest requirement, Table B has not only 50 columns but 100 columns! I really need a way to dynamically loop those columns rather than hard-coding for over 80 columns
I think you can just do
select * into #tableA from #tableB where 1=2
with this both tables will have same columns
You don't need to add columns to table A, just add 30 NULLs to select from Table A.
Select *,NULL,...,NULL,NULL From Table A
Union
Select * From Table B
You could add aliases to make the result a bit cleaner
Select *,...,NULL CompleteDate, NULL ContactPerson From Table A
Union
Select * From Table B

Deleting at most one record for each unique tuple combination

I want to delete at most one record for each unique (columnA, columnB)-tuple in my following delete statement:
DELETE FROM tableA
WHERE columnA IN
(
--some subqueryA
)
AND columnB IN
(
--some subqueryB
)
How is this accomplished? Please only consider those statements that work when used against MSS 2000 (i.e., T-SQL 2000 syntax). I can do it with iterating through a temptable but I want to write it using only sets.
Example:
subqueryA returns 1
subqueryB returns 2,3
If the original table contained
(columnA, columnB, columnC)
5,2,5
1,2,34
1,2,45
1,3,86
Then
1,2,34
1,3,86
should be deleted. Each unique (columnA, columnB)-tuple will appear at most twice in tableA and each time I run my SQL statement I want to delete at most one of these unique combinations - never two.
If there is one record for a given unique (columnA, columnB)-tuple,
delete it.
If there are two records for a given unique (columnA,
columnB)-tuple, delete only one of them.
Delete tabA
from TableA tabA
Where tabA.columnC in (
select max(tabAA.columnC) from TableA tabAA
where tabAA.columnA in (1)
and tabAA.columnB in (2,3)
group by tabAA.columnA,tabAA.columnB
)
How often are you going to be running this that it matters whether you use temp tables or not? Maybe you should consider adding constraints to the table so you only have to do this once...
That said, in all honesty, the best way to do this for SQL Server 2000 is probably to use the #temp table as you're already doing. If you were trying to delete all but one of each dupe, then you could do something like:
insert the distinct rows into a separate table
delete all the rows from the old table
move the distinct rows back into the original table
I've also done things like copy the distinct rows into a new table, drop the old table, and rename the new table.
But this doesn't sound like the goal. Can you show the code you're currently using with the #temp table? I'm trying to envision how you're identifying the rows to keep, and maybe seeing your existing code will trigger something.
EDIT - now with better understood requirements, I can propose the following query. Please test it on a copy of the table first!
DELETE a
FROM dbo.TableA AS a
INNER JOIN
(
SELECT columnA, columnB, columnC = MIN(columnC)
FROM dbo.TableA
WHERE columnA IN
(
-- some subqueryA
SELECT 1
)
AND columnB IN
(
-- some subqueryB
SELECT 2 UNION SELECT 3
)
GROUP BY columnA, columnB
) AS x
ON a.columnA = x.columnA
AND a.columnB = x.columnB
AND a.columnC = x.columnC;
Note that this doesn't confirm that there are exactly one or two rows that match the grouping on columnA and columnB. Also note that if you run this twice it will delete the remaining row that still matches the subquery!