Merging two tables where unique key comprises multiple columns - mysql

I have two tables A and B (with the same schema), and I want to merge them by inserting all entries from A into B. If table B already has data associated with a key from A, I want to silently drop those data.
The problem is that table B has a unique key index that consists of three columns, so I can't just say "WHERE A.key <> B.key".
I can't seem to formulate an SQL statement along the lines of:
INSERT INTO B
VALUES ((SELECT * FROM A WHERE ... NOT IN ...))
Is there a way to INSERT those rows from A into B where the corresponding three-column-key doesn't exist in B yet?

INSERT INTO B
(Col1, Col2, Col3, ColN)
SELECT
A.Col1, A.Col2, A.Col3, COlN
FROM A
LEFT JOIN B
ON A.COL1 = B.Col1
AND A.COL2 = B.Col2
AND A.COL3 = B.Col3
WHERE B.Col1 IS NULL
Essentially Join the 2 tables with a left join, and insert all from A where B is null (No corresponding value in B table for a Join on the 3 Key columns)

You could use NOT EXISTS instead of NOT IN
INSERT B
SELECT *
FROM A
WHERE NOT EXISTS
( SELECT 1
FROM B
WHERE A.Key1 = B.Key1
AND A.Key2 = B.Key2
)
Although according to this MySQL optimises LEFT JOIN/IS NULL better than not exists:
INSERT B
SELECT A.*
LEFT JOIN B
ON A.Key1 = B.Key1
AND A.Key2 = B.Key2
WHERE B.Key1 IS NULL

Related

SQL to fetch data where Unique key matches but the data is different in some other columns between different tables

I have two tables of same structure as below. I am trying to write a query to compare both the tables using the Unique key which is the first column and trying to return values when there is a mismatch in the second column.
If the key is not present then no need to consider that data. only if the key is present in both the table then we have compare it.
Table A
ColumnA ColumnB
A 1
B 2
C 2
D 8
Table B
ColumnC ColumnD
A 1
B 3
C 5
F 4
For example the output of the above table when comparing Table A with B should be
B 2
C 2
and when comparing Table B with A it should be
B 3
C 5
Ideally the difference in the base table should come.
I have tried Joins and Unions but I am not able to fetch the data as mentioned above.
Since you want only those rows which has matching FK values in both the tables, we simply need to use INNER JOIN.
Now, we can simply consider the unmatching rows by using WHERE .. <> ..
When comparing Table A against Table B, we can get Table A rows only:
SELECT
tA.*
FROM tableA AS tA
JOIN tableB AS tB
ON tB.ColumnC = tA.ColumnA
WHERE tB.ColumnD <> tA.ColumnB
When comparing Table B against Table A, simply fetch the rows from Table B only:
SELECT
tB.*
FROM tableA AS tA
JOIN tableB AS tB
ON tB.ColumnC = tA.ColumnA
WHERE tB.ColumnD <> tA.ColumnB
I would do :
SELECT t.*
FROM tablea t
WHERE EXISTS (SELECT 1 FROM tableb t1 WHERE t1.cola = t.cola AND t1.colb <> t.cold);
Same would be for second version just need to swipe the table names.
use EXISTS and union all
SELECT t.*
FROM tablea t
WHERE EXISTS (SELECT 1 FROM tableb t1 WHERE t1.cola = t.cola AND t1.colb <> t.colb)
union all
SELECT t.*
FROM tableb t
WHERE EXISTS (SELECT 1 FROM tablea t1 WHERE t1.cola = t.cola AND t1.colb <> t.colb)

replacing rows with no primary key mysql

I have 2 tables. table A and table B as shown in the picture along with desired result:
The combination of id1 and id2 is unique. I want to replace the contents of the rows of table A with those in table B ( as seen in the desired result, the amount in row 1 and row 4 has been changed to reflect that in table B while retaining other rows of table A). What would be the best way to achieve this? Can REPLACE be used here ( with no single column as primary key).
If you want result by SELECT, try this:
SELECT
a.id1, a.id2,
COALESCE(b.name1, a.name1) AS name1,
COALESCE(b.name2, a.name2) AS name2,
COALESCE(b.amount, a.amount) AS amount
FROM tablea a
LEFT JOIN tableb b
ON a.id1 = b.id1 AND a.id2 = b.id2
If you want it by UPDATE, try this:
UPDATE tablea a
LEFT JOIN tableb b
ON a.id1 = b.id1 AND a.id2 = b.id2
SET
a.name1 = COALESCE(b.name1, a.name1),
a.name2 = COALESCE(b.name2, a.name2),
a.amount = COALESCE(b.amount, a.amount)

Join table on itself without two queries

I've seen people recommending cross joining a table on itself by doing this:
SELECT *
FROM tbl AS A, tbl AS B
WHERE A.col1 = 1 AND B.col1 = 1
But here, the engine needs to iterate through all of the rows in tbl twice to match the two queries to the results of A and B, despite the fact that the queries (and therefore the results) are the same.
Assuming that the WHERE on A and B will always be the identical for the two, this is a waste. Is there any way to query for something once, and then cross join the result of that query on itself? I'd like to avoid temp tables, which would require disk writing instead of performing this entire thing in RAM.
I am using MySQL, although any SQL answer would help a lot.
EXAMPLE:
Suppose that tbl looks as follows:
COL1 COL2
1 A
1 B
1 C
2 D
2 E
When I run my where clause of col1 = 1, it returns the first three rows from the above table. What I want is the following table, but with only one execution of the where statement, since the two tables A and B are identical:
A.COL1 A.COL2 B.COL1 B.COL2
1 A 1 A
1 A 1 B
1 A 1 C
1 B 1 A
1 B 1 B
1 B 1 C
1 C 1 A
1 C 1 B
1 C 1 C
You are basically asking for an intentional Cartesian join
select
a.col1,
a.col2,
b.col1,
b.col2
from
tbl a
join tbl b
on a.col1 = b.col1
where
a.col1 = 1
order by
a.col2,
b.col2
To exactly hit your output order sequence, you need the order by by the "a" column 2 then "b" column 2
I really recommend avoiding that JOIN syntax... it can be very difficult to read.
Your explanation of what you are trying to do is a bit cryptic. The query as written offers no value for a JOIN operation. Generally speaking, when you want to JOIN a table to itself, it's on different columns:
select *
from tbl as a
inner join
table as b
on a.col1 = b.col2
where
a.col1 = 1;
This allows you to query against the table, and also collect related information organized in a hierarchical fashion in the same table. For example:
create table tbl (
person_id int,
parent_id int
);
In this case, a parent is a person too. If you wanted to get a list of the parents related to the person with an ID of 1, you could write:
select
person.person_id as OriginalPerson,
parent.person_id as Parent
from
tbl as person
inner join
tbl as parent
on parent.person_id = person.person_id
where
person.person_id = 1;
UPDATE Upon reading your further explanation, you want a cartesian product:
select a.*, b.*
from tbl as a
inner join tbl as b
on 1=1
where a.col1 = 1
and b.col1 = 1

How do I do a diff between rows in a table

I need to compare the values of rows between similar tables in different databases. How do I know which rows are different.
e.g. db1.foo (contains 2000 rows) and db2.foo (contains 2003 rows). These 2 tables are supposed to be similar and I would like to know how to find the rows which are supposed to be additions. Note: Even the primary key ids in these tables are supposed to be the same.
I don't have access to any GUI tools and would like to know if there is any SQL command which I can use to perform this diff?
Depending the check on the Primary Keys only:
SELECT
'a' AS DataOnlyInTable
, a.*
FROM
db1.foo AS a
LEFT JOIN
db2.foo AS b
ON b.PK = a.PK
WHERE b.PK IS NULL
UNION ALL
SELECT
'b' AS DataOnlyInTable
, b.*
FROM
db1.foo AS a
RIGHT JOIN
db2.foo AS b
ON b.PK = a.PK
WHERE a.PK IS NULL
Depending the check on the whole rows:
SELECT
'a' AS DataOnlyInTable
, a.*
FROM
db1.foo AS a
LEFT JOIN
db2.foo AS b
ON (b.PK, b.column2, b.column3, ...)
= (a.PK, a.column2, a.column3, ...)
WHERE b.PK IS NULL
UNION ALL
SELECT
'b' AS DataOnlyInTable
, b.*
FROM
db1.foo AS a
RIGHT JOIN
db2.foo AS b
ON (b.PK, b.column2, b.column3, ...)
= (a.PK, a.column2, a.column3, ...)
WHERE a.PK IS NULL

Inserting distinct entries into the database

I have two tables with exactly the same fields. Table A contains 7160 records and table B 7130 records.Now I want to insert distinct records from table A into table B such that B should not have any duplicate entry in it. How should I go about doing this?
This basically selects records that are in A that are not in B. It would work, but you might have to tweak the field you use to uniquely identify a record. In this example I used field 'ID' but you might have to change that to A.field1 = B.field1 AND A.field2 = B.field2 etc.
INSERT INTO TABLEB
(
SELECT A.*
FROM TABLEA A
LEFT JOIN TABLEB B ON A.ID = B.ID
WHERE B.ID IS NULL
)
You can use a "union" query to combine the results from multiple tables into a single result set. "union" will only return distinct rows from all tables.
See this page for more info:
http://www.tutorialspoint.com/mysql/mysql-union-keyword.htm
insert into tableB (id)
select t1.id from tableA t1
where t1.id not in (select t2.id from tableB t2)