Sql SubSelect With Multiple Keys - sql

I am trying to copy rows from 1 table to a copy of itself, but only including rows for accounts that exist in a 2nd table.
This works fine with just one key field (account) as below:
insert into newlibr.acpmstpf
select * from oldlibr.acpmstpf as source
where not exists
(select *
from newlibr.acpmstpf as target
where target.acpno = source.acpno
and target.acpbrn = source.acpbrn
and target.acpitm = source.acpitm)
and source.acpno in (select account from accinfo)
In this case I am trying to insert rows from the original table acpmstpf in schema oldlibr to a copy of itself in newlibr, matching rows on the 2 keys account/branch (acpno/acpbrn) and ONLY inserting those rows where the account is in 2nd table accinfo.
What I REALLY want to do is to ONLY insert those rows where the account & branch are in accinfo, because if only 2 branches are in accinfo and there are 100 on acpmstpf, it copies all 100 rows.
I know I could do this with a join, but then I would have to specify all the columns (which could be many - I have this scenario for a few tables).
Is there a way I could do this and still use a sub-select?

You want to replace
and source.acpno in (select account from accinfo)
and look for tuples (account, branch) instead. Many DBMS support this:
and (source.acpno, source.acpbrn) in (select account, branch from accinfo)
For those DBMS that don't, you'd have to resort to EXISTS:
and exists
(
select *
from accinfo
where accinfo.account = source.acpno
and accinfo.branch = source.branch
)

Use exists:
insert into newlibr.acpmstpf
select *
from oldlibr.acpmstpf as source
where not exists (select 1
from newlibr.acpmstpf as target
where target.acpno = source.acpno and
target.acpbrn = source.acpbrn
target.acpitm = source.acpitm
) and
exists (select 1
from accinfo a
where source.acpno = a.accinfo and
source.acpbrn = a.acpbrn
);

Related

How to make sure if the both source and target tables have same number of records after modification? How to sync?

I am copying data from one database to another having similar tables using Insert....select.
Now, it's ok but if the source db has edited something and I want it to be updated in the target database table then that would be a problem.
I have few solutions:
If record exists (would verify using a GUID we have) then
Delete it and its child data and Insert all again hence new updated data.
Taking union of the tables
SELECT * FROM SourceDb.dbo.Table1
UNION
SELECT * FROM TargetDb.dbo.Table2
If you get records greater than any of two tables, they don't have same data and needs to be deleted and re-add.
But problem is I have around 1 parent and around 7 child table so taking and checking using union would take me some extra coding.
what should I do?
The general query to verify that set A = set B is :
A - B U B - A = ∅ (set theory)
You can use the above query :
WITH
TA AS (SELECT * FROM databaseA.SchemaA.TableA),
TB AS (SELECT * FROM databaseB.SchemaB.TableB)
SELECT * FROM TA
EXCEPT
SELECT * FROM TB
UNION ALL
SELECT * FROM TB
EXCEPT
SELECT * FROM TA;

SQL Finding codes

I want to see all my customers who have received a document code Eg. [RF001], however I also want to see that they haven't received a number of document codes E.G [RF002, RF005, RF006, RF009].
All the document codes sit in 1 column, and the customer references are repeated.
Table
Customer_Ref | Doc_Code
CS001 | RF001
CS002 | RF003
CS001 | RF002
Code thats been tried is;
SELECT *
FROM Test_Data
WHERE Doc_Code = RF001
You can use NOT EXISTS to exclude unwanted results :
SELECT *
FROM Table1 tbl
WHERE
Doc_Code='RF001'
AND NOT EXISTS (SELECT 1
FROM Table1 unwanted
WHERE
tbl.Customer_Ref=unwanted.Customer_Ref
AND Doc_Code IN ('RF002', 'RF005', 'RF006', 'RF009'))
SELECT *
FROM Test_Data t1
WHERE t1.Doc_Code = 'RF001'
and not exists (select 1 from Test_Data t2
where t1.Customer_Ref = t2.Customer_Ref
and t2.Doc_Code in ('RF002', 'RF005', 'RF006', 'RF009')
There's several ways to accomplish this.
This is but one way using a join
SELECT Distinct A.CUSTOMER_REF
FROM TABLE A
LEFT JOIN (SELECT DISINTCT CUSTOMER_REF
FROM TABLE B
WHERE B.DOC_CODE IN ('RF002', 'RF005', 'RF006', 'RF009') B
ON A.CUSTOMER_REF = B.CUSTOMER_REF
WHERE A.DOC_CODE = 'RF001'
and B.Customer_REF is null
Another way is to use not exists (I'm sure someone else will do)
Another way is to use a not in (I'm sure someone else will do)
Another way is to use set logic and EXCEPT (Similar to a Union; but subtracts once set from another)
.
SELECT A.Customer_REF FROM table where DOC_CODE = 'RF001'
EXCEPT
SELECT A.Customer_REF FROM Table where DOC_CODE IN ('RF002', 'RF005', 'RF006', 'RF009')

Find if a string is in or not in a database

I have a list of IDs
'ACE', 'ACD', 'IDs', 'IN','CD'
I also have a table similar to following structure
ID value
ACE 2
CED 3
ACD 4
IN 4
IN 4
I want a SQL query that returns a list of IDs that exists in the database and a list of IDs that does not in the database.
The return should be:
1.ACE, ACD, IN (exist)
2.IDs,CD (not exist)
my code is like this
select
ID,
value
from db
where ID is in ( 'ACE', 'ACD', 'IDs', 'IN','CD')
however, the return is 1) super slow with all kinds of IDs 2) return multiple rows with the same ID. Is there anyway using postgresql to return 1) unique ID 2) make the running faster?
Assuming no duplicates in table nor input, this query should do it:
SELECT t.id IS NOT NULL AS id_exists
, array_agg(ids.id)
FROM unnest(ARRAY['ACE','ACD','IDs','IN','CD']) ids(id)
LEFT JOIN tbl t USING (id)
GROUP BY 1;
Else, please define how to deal with duplicates on either side.
If the LEFT JOIN finds a matching row, the expression t.id IS NOT NULL is true. Else it's false. GROUP BY 1 groups by this expression (1st in the SELECT list), array_agg() forms arrays for each of the two groups.
Related:
Select rows which are not present in other table
Hmmm . . . Is this sufficient:
select ids.id,
(exists (select 1 from table t where t.id = ids.id)
from unnest(array['ACE', 'ACD', 'IDs', 'IN','CD']) ids(id);

Postgresql, Copy data to a new table from a foreign table

I'm trying to do a job that will copy data from a foreign table called "m_aduana" of the schema "nathalia" to my schema "publico" and my table "mae_aduana".
I need to do a query that copies all the values from the table "m_aduana" avoiding duplicates.
I got something like this for now but the result sends me an Insert 0 0, which means nothing is inserted.
insert into publico.mae_aduana(cod_aduana,nom_aduana,des_aduana,cod_aduana1,cod_aduana2,cod_aduana3,est_aduana)
select cod_aduana,nom_aduana,des_aduana,cod_aduana1,cod_aduana2,cod_aduana3,est_aduana
from nathalia.m_aduana
where not exists (
select * from publico.mae_aduana ma_ad, nathalia.m_aduana m_ad
where ma_ad.cod_aduana = m_ad.cod_aduana)
I think you have an error in the inner select. You don't need to use again the table nathalia.m_aduana. If should be something like:
insert into publico.mae_aduana(cod_aduana,nom_aduana,des_aduana,cod_aduana1,cod_aduana2,cod_aduana3,est_aduana)
select cod_aduana,nom_aduana,des_aduana,cod_aduana1,cod_aduana2,cod_aduana3,est_aduana
from nathalia.m_aduana
where not exists (
select * from publico.mae_aduana ma_ad
where ma_ad.cod_aduana = nathalia.m_aduana.cod_aduana)
You might want to change the where exists part like below
from nathalia.m_aduana m
where not exists (
select 1 from publico.mae_aduana
where cod_aduana = m.cod_aduana)

SQL With... Update

Is there any way to do some kind of "WITH...UPDATE" action on SQL?
For example:
WITH changes AS
(...)
UPDATE table
SET id = changes.target
FROM table INNER JOIN changes ON table.id = changes.base
WHERE table.id = changes.base;
Some context information: What I'm trying to do is to generate a base/target list from a table and then use it to change values in another table (changing values equal to base into target)
Thanks!
You can use merge, with the equivalent of your with clause as the using clause, but because you're updating the field you're joining on you need to do a bit more work; this:
merge into t42
using (
select 1 as base, 10 as target
from dual
) changes
on (t42.id = changes.base)
when matched then
update set t42.id = changes.target;
.. gives error:
ORA-38104: Columns referenced in the ON Clause cannot be updated: "T42"."ID"
Of course, it depends a bit what you're doing in the CTE, but as long as you can join to your table withint that to get the rowid you can use that for the on clause instead:
merge into t42
using (
select t42.id as base, t42.id * 10 as target, t42.rowid as r_id
from t42
where id in (1, 2)
) changes
on (t42.rowid = changes.r_id)
when matched then
update set t42.id = changes.target;
If I create my t42 table with an id column and have rows with values 1, 2 and 3, this will update the first two to 10 and 20, and leave the third one alone.
SQL Fiddle demo.
It doesn't have to be rowid, it can be a real column if it uniquely identifies the row; normally that would be an id, which would normally never change (as a primary key), you just can't use it and update it at the same time.