VBA - Insert in two table where id from first table is a field in second - sql

I have huge amount of data structured in two excel sheets with the following columns:
EXCEL DATA
Sheet1 [pod, client, address, ...etc] -one record per [pod]
Sheet2 [pod, pointofmeasure, typepct, ...etc] -one-to-many records per [pod]
-relationship is between sheet1.pod and sheet2.pod (one-to-many relationship)
I need a sql to insert data from that excel sheets into a access database with the following tables structure:
ACCESS DATABASE
Table1 [id, pod, client, address, ...etc]
Table2 [id, pod_id, pod, pointofmeasure, typepct, ...etc]
Where table2.pod_id = table1.id
Can be do it in one sql insert?
I came up with this mass insert...
cn.Open scn
ssql = "INSERT INTO table1 (pod, client, address, ...etc) "
ssql = ssql & "SELECT * FROM [Excel 8.0;HDR=YES;DATABASE=" & dbWb & "].[sheet1$]"
cn.Execute ssql, cAffectedRows
cn.Close
Now how the hell i get id's to insert data to table2 ?

Assuming your pod-column in Table 1 is unique, you have to insert, select again with condition SELECT id FROM table1 WHERE pod = 'myCurrentPod' and there is your ID.
If pod is NOT unique and even pod with other known values is not unique, there is no chance to do it like that.
If pod with other known values is unique, you can expand the upper statement with more Conditions.
In all Cases, you cannot do a mass-Insert, but insert one Row from sheet1, insert linked rows from sheet2, next sheet1-row and so on.
If you can design the tables, you should not go with an autoincrement-ID, but with an application-set-ID. Or, if your values in pod are really unique, use this as pk.

It can be done in one insert when your columns are named correctly (i.e. each attribute is uniquely and consistently named throughout the schema) and foreign keys are set up as expected.
Here's a quick sketch using SQL DDL for Access's ANSI-92 Query Mode (you can create the same objects using the Access UI):
CREATE TABLE TableA
( ID IDENTITY NOT NULL UNIQUE,
a_col INTEGER NOT NULL );
CREATE TABLE TableB
( ID INTEGER NOT NULL UNIQUE
REFERENCES TableA ( ID ),
b_col INTEGER NOT NULL );
CREATE VIEW TestAB
( a_ID, a_col, b_ID, b_col ) AS
SELECT A1.ID, A1.a_col, B1.ID, B1.b_col
FROM TableA AS A1
INNER JOIN TableB AS B1 ON A1.ID = B1.ID;
If you then insert via the view, omitting the ID column (thus allowing it to be auto-generated) like this:
INSERT INTO TestAB ( a_col, b_col ) VALUES ( 55, 99 );
then a row will be inserted into each table for the single SQL command.
That said, it doesn't look like you have been strict enough when naming your attributes e.g. Table1.id changes name to pod_id in Table2 and id is not unique in your schema.

i found a this...
INSERT INTO [table1] ([data])
OUTPUT [inserted].[id], [external_table].[col2]
INTO [table2] SELECT [col1]
FROM [external_table]
and on last SELECT ... to do a JOIN between sheets to retrive all the data?
It's working that way?

Related

How to copy parent and child data from one database to another while assigning the newly created parent ids in the foreign key column of child tables?

Suppose,
I have a table tblClasses and tblStudents
Now each class have multiple student.
tblClass
ID Name
1 ClassA
2 ClassB
3 ClassC
tblStudents
ID Name Class_ID
1. john 1
2. Mathew 1
3. Imran 2
4. Jenny 3
now, I have another server having exact same db and tables and I am copying data from server 1 to server 2 from same tables using the Select and Insert e.g.
insert into server2.dbo.tblClass (Name)
select Name from server1.dbo.tblClass
and for tblStudents
insert into server2.dbo.tblStudents (Name, Class_ID)
select Name, Class_ID from server1.dbo.tblStudents
now this is ok but the real problem is that in server2 after copying the data, how to populate the tblStudents fk Class_ID with the actual IDs of tblClass which are generated after inserting the data into tblStudents in server2 since PKs are Identity and autoincremented and cannot change the design.
What to do in this case?
In simple words, when a parent and child data are copied then in the child table the foreign key field needs to be populated with the actual IDs of the parent not the one from where it is copied which would be obviously different.
I am not allowed to change the table design or properties and have to do it using the queries.
Any suggestions?
The way is to create a ClassId mapping table on class records insertion and use this mapping table to translate OldClassId to NewClassId for the new Student table:
declare #ClassIds table (OldClassId int, NewClassId int);
merge into newDB.dbo.tblClasses as target
using
(
select
Id = Id * (-1),
[Name]
from
oldDB.dbo.tblClasses
)
as source on source.Id = target.Id
when not matched by target then
insert ([Name])
values (source.[Name])
output source.Id * (-1), inserted.Id -- ← the trick is here
into #ClassIds (OldClassId, NewClassId);
insert into newDB.dbo.tblStudents
select
s.Id,
s.[Name],
ClassId = ids.NewClassId
from
oldDB.dbo.tblStudents s
inner join #ClassIds ids on ids.OldClassId = s.ClassId;
The major trick is that the MERGE statement may work with not not only inserted and deleted columns (as INSERT, UPDATE, DELETE statements do) but with the source columns as well.
There are two ways to go that I can think:
The first is to use SET IDENTITY_INSERT tblClass ON. This is not a design change, so you should be able to do it. After that, you can insert your own values in tblClass.ID (although you will need the select list parenthesis):
insert tblClass(ID,Name) values (1, 'ClassA')....
Alternatively, you could make a query that connects student ids to class names, and then use that back to create the respective connections:
-- export/save this in temp table
select s.Name,c.Name as className
into #a
from tblStudents s
left join tblClass c on s.Class_ID=c.ID
--now use this to fill db2 tblStudents
insert tblStudents(Name,Class_ID)
select #a.Name,c.ID
from
#a
inner join tblClass c on #a.className=c.Name

Automatically remove a row without foreign references

I am using sqlite3.
I have one "currencies" table, and two tables that reference the currencies table using a foreign key, as follows:
CREATE TABLE currencies (
currency TEXT NOT NULL PRIMARY KEY
);
CREATE TABLE table1 (
currency TEXT NOT NULL PRIMARY KEY,
FOREIGN KEY(currency)
REFERENCES currencies(currency)
);
CREATE TABLE table2 (
currency TEXT NOT NULL PRIMARY KEY,
FOREIGN KEY(currency)
REFERENCES currencies(currency)
);
I would like to make sure that rows in the "currencies" table that are not referenced by any row from "table1" and "table2" will be removed automatically. This should behave like some kind of ref-counted object. When the reference count reaches zero, the relevant row from the "currencies" table should be erased.
What is the "SQL way" to solve this problem?
I am willing to redesign my tables if it could lead to an elegant solution.
I prefer to avoid solutions that require extra work from the application side, or solutions that require periodic cleanup.
Create an AFTER DELETE TRIGGER in each of table1 and table2:
CREATE TRIGGER remove_currencies_1 AFTER DELETE ON table1
BEGIN
DELETE FROM currencies
WHERE currency = OLD.currency
AND NOT EXISTS (SELECT 1 FROM table2 WHERE currency = OLD.currency);
END;
CREATE TRIGGER remove_currencies_2 AFTER DELETE ON table2
BEGIN
DELETE FROM currencies
WHERE currency = OLD.currency
AND NOT EXISTS (SELECT 1 FROM table1 WHERE currency = OLD.currency);
END;
Every time that you delete a row in either table1 or table2, the trigger involved will check the other table if it contains the deleted currency and if it does not contain it, it will be deleted from currencies.
See the demo.
There is no automatic way of doing this. The reverse can be handling using cascading delete foreign key references. The reverse is that when a currency is deleted all related rows are.
You could schedule a job daily running something like:
delete from currencies c
where not exists (select 1 from table1 t1 where t1.currency = c.currency) and
not exists (select 1 from table2 t2 where t2.currency = c.currency);
If you need an automatic way for doing that, then most dbms provide a trigger mechanism. You can create a trigger on update and delete operations that run the folowing query:
you can use a left join for that:
https://www.w3schools.com/sql/sql_join_left.asp
It return a row for all rows from the left table, even if there is no corresponding row in the right table, replacing the rows form the right with null. You can then check a not null right table field for null with is null. This will filter for the rows the have no counterpart in the right table.
For example:
SELECT currencies.currency FROM currencies LEFT JOIN table1 WHERE table1.currency IS NULL
will show the relevant rows for table1.
You can do the same with table two.
This will give you two queries, that shows which rows have no couterpart.
You can then use intersect on the result, so that you have the rows that have not couterpart in either:
SELECT * FROM query1 INTERSECT SELECT * FROM query2
Now you have the list of currencies to be deleted.
You can finish this by using a subqueried delete:
DELETE FROM currencies WHERE currency IN (SELECT ...)

How to insert data from one table to another table in oracle

I have a two table 'Inventory' and 'Tendor' where Inventory has primary key pk_id ,i updated 'Inventory' table when Inventory.ti_name = Tendor.ki_name by using following query
Update Inventory A set (Used_NAME, ACCOUNT_NUMBER, ti_STATUS)
= (Select B.Using_NAME, B.ACCOUNT_NO, B.ki_STATUS from
Tendor B where A.ti_name = B.ki_name and a.pk_id is not null);
Anything wrong in this query or any optimized one??
After updation for those who is not satisfying the condition Inventory.ti_name = Tendor.ki_name i want to insert it as new rows in 'Inventory' table with primary key pk_id should be changed
how to do this? for pk_id do i need to do some logic like 'SEQ.NEXTVAL FROM DUAL'
Can anybody suggest a query
This query updates searches matching rows in tables inventory and tendor. When both tables contain row with the same value of ki_name, it updates row in table inventory. All rows from tendor, that was not found in inventory, will be inserted there:
merge into Inventory a
using Tendor b
on (A.ti_name = B.ki_name)
when matched then update
set a.Used_NAME = B.Using_NAME,
a.ACCOUNT_NUMBER = B.ACCOUNT_NO,
a.ti_STATUS = B.ki_STATUS
when not matched then
insert (pk_id, Used_NAME, ACCOUNT_NUMBER, ti_STATUS)
values (your_seq.nextval, B.Using_NAME, B.ACCOUNT_NO, B.ki_STATUS)
Also you can use sequence in insert statement.

Using output to capture auto increment identity, plus past identity mapping table

I am trying to make exact copies of data in SQL, with new clientIDs, but keep the existing data in the old client as well. I will be inserting the data into a table with an auto incrementing integer primary key ID. I need to retain the ID's of the Old records and the new records together so I can continue using this mapping as I copy the different table data so I can maintain relationships.
At this point I have the following:
INSERT INTO [dbo].[Driver]
OUTPUT inserted.ID
inserted.Name
inserted.ClientID
SELECT Name,
1234 AS clientID
FROM dbo.Driver
I am wondering if there is a way to also select the old ID of the driver in the Output so I can then insert all of this into a holding table using the OUTPUT. So I need to end up with the following after I perform the insert into the dbo.Driver table so I can also insert these values into a temp table:
NewID
OldID
Name
ClientID
At this point I don't know of a way to pull the Original ID from the original record.
I ended up using MERGE INTO to keep track of the old ID as per the following SO post:
How to use OUTPUT to capture new and old ID?
you can try...
INSERT INTO dbo.Driver (oldID, Name, clientID)
SELECT
B.ID,
A.Name,
1234 AS clientID
FROM dbo.Driver A
LEFT JOIN dbo.Driver B ON A.Name = B.Name AND A.clientID = b.clientID
or maybe just
INSERT INTO dbo.Driver (oldID, Name, clientID)
SELECT
ID,
Name,
1234 AS clientID
FROM dbo.Drive

SQL insert data into a table from another table

I'm having a problem trying to insert some values into a table. I made an empty table with the fields
id(primary key)
association_id
resource_id
I have another table with
resource_id
association_id
and another one with
id(coresponding to the association_id in the former one)
image
I want to insert the resource_id and association_id from the first populated table, where the image field of the coresponding id from the last table is not empty.
I tried this:
INSERT IGNORE INTO `logo_associations` (``,`association_id`,`resource_id`)
SELECT
``,
`a`.`association_id`,
`a`.`resource_id`
FROM doc24_associations_have_resources a
Join doc24_associations An on a.association_id = An.id
WHERE An.image<>''
but it does not work
Try this:
INSERT INTO logo_associations (association_id, resource_id)
SELECT a.association_id
,a.resource_id
FROM doc24_associations_have_resources a
LEFT JOIN doc24_associations an ON a.association_id = an.id
WHERE an.image IS NULL -- check for null with left join
This is valid for SQL Server. You do not need to select and insert the first column as it is an identity as you mention.
My experience is based on SQL Server but the SQL may be very similar
INSERT INTO DestinationTable
(association_id, resource_id)
SELECT LNK.assocication_id,
LNK.resource_id
FROM LinkTable AS LNK
INNER JOIN ImageTable AS IMG ON IMG.id = LNK.association_id
AND IMG.image IS NOT NULL
Above I assume the following:
Tables are named DestinationTable, LinkTable, and ImageTable respectively
In DestinationTable the primary key (id) is auto generated