How do I do a diff between rows in a table - mysql

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

Related

Check if table a primary key is exist in table b

Table A:
ID, Name, etc.
Table B:
ID, TableA-ID.
SELECT * FROM A;
and I want to return a boolean value in the same result for this condition ( if A.ID Exists in Table B).
There are several ways of achieving what you need. Below are three possibilities. These all differ in execution plans and how database actually wants to execute them so depending on your record count one may be more efficient than the other. It's better if you see it for yourself.
1) Use LEFT JOIN and check if a non-null field from B is not null to ensure the record exists. Then apply DISTINCT clause if relationship is 1:N to only show rows from A without duplicates.
select distinct a.*, b.id is not null as exists_b
from a
left join b on
a.id = b.tablea-id
2) Use exists() function, which will be evaluated for each row being returned from table A.
select a.*, exists(select 1 from b where a.id = b.tablea-id) as exists_b
from a
3) Use a combination of subquery expression EXISTS and it's contradiction in two queries to check if a record has or has not a match within table B. Then UNION ALL to combine both results into one.
select *, true as exists_b
from a
where exists (
select 1
from b
where a.id = b.tablea-id
)
union all
select *, false as exists_b
from a
where not exists (
select 1
from b
where a.id = b.tablea-id
)
select A.*, IFNULL((select 1 from B where B.TableA-ID = A.ID limit 1),0) as `exists` from A;
The above statement will result in a 1, if the key exists, and a 0 if that key does not exist. Limit 1 is important if there are multiple records in B

SQL : Multiple left joins with similar tables

I have the following tables:
TABLE A
id
info
TABLE B
f_id
question
choices
TABLE C
f_id
question
lines
The id from the Table A always match a f_id from either Table B or C, but never both. I want to join Table B and Table C on table A only when it matches so I would get a table with the following columns :
id | info | question | choices | lines
where all rows are filled in the question column, some are NULL in the column choices and some are NULL in the column lines.
What I tried is to do two consecutive left joins, but the second one overrides the first so all the rows that doesn't match in Table C (second left join) get a NULL value in the question column.
Is there a way to do a query that will not override previously joined data with NULL values? I'm working with Laravel Eloquent, so any of raw SQL or Eloquent Query would help me.
UNION B and C and then INNER JOIN A to those results.
SELECT s1.f_id, s1.question, s1.choices, s1.lines
FROM
(
SELECT f_id, question, choices, lines = null
FROM B
UNION
SELECT f_id, question, choices = null, lines
FROM C
) s1
INNER JOIN A ON s1.f_id = A.id
but never both
Good luck with that.
id | info | question | choices | lines
SELECT a.id, a.info, b.question, b.choices, '' AS lines
FROM tableA as A
LEFT JOIN tableB AS b
ON a.id=b.f_id
UNION
SELECT a.id, a.info, c.question, '', c.lines
FROM tableA as A
INNER JOIN tableC AS c
ON a.id=c.f_id
You could use UNION to combine two different queries.
SELECT
`id`, `info`, `question`, `choices` AS `lines`
FROM
`TABLE_A` INNER JOIN
`TABLE_B` ON `TABLE_A`.`id` = `TABLE_B`.`f_id`
UNION
SELECT
`id`, `info`, `question`, `lines`
FROM
`TABLE_A` INNER JOIN
`TABLE_C` ON `TABLE_A`.`id` = `TABLE_C`.`f_id`
Make sure to use JOIN or INNER JOIN (JOIN defaults to INNER JOIN on MySQL) AND not LEFT JOIN, RIGHT JOIN, OUTER JOIN otherwise you'll end up with partially filled results.

SQL Query to compare two tables for names

I am building a SQL query which compares two tables A and B by a [name] column and returns the names from table A that are not in table B
Example
Table A
ID Name Address
1 A ABC
2 B XYZ
3 C PQR
Table B
ID Name Gender
1 A F
2 B M
3 D F
The query I wrote should return third row from table A as it is not in table B and should exclude all other rows
Following is the query I built
Select * from A oa left join B gp ON oa.name!=gp.name
the above doesn't return the results I was expecting.
Can this be corrected?
Easiest way:
select * from A where name not in (select name from B)
Better way:
select * from A where not exists (select 1 from B where B.name = A.name)
"A left join B" means keeping everything in A, and associating records in B if the condition is satisfied.
In your case, if you really wanna use left join, here is what it should be ('=', not '!='):
Select * from A oa left join B gp ON oa.name=gp.name where gp.name is null
Better way would be using 'not exists' performance-wise, or 'except' if null values are not an issue.
Using excpet operator will help
select * from TableA
except
select * from TableB
SELECT a.*
FROM A a
LEFT JOIN B b
ON a.name = b.name
WHERE b.name IS NULL

pulling data from two different tables with common row elements

I have the following two tables:
Table a:
name qty
a 10
b 20
c 30
d 40
and table b
name qty
a 10
b 20
d 20
e 60.
I want to merge there two tables and create a new table like this
name qty
a 20
b 40
c 30
d 60
e 60
The objective is to add the values if there is have the same value in name or else just append the values in table two to table 1.
Unfortunately, MySQL does not support full outer join. Here is a method using union all and group by:
select name, sum(qty) as qty
from ((select name, qty from a) union all
(select name, qty from b)
) ab
group by name;
To simulate a full outer join, just execute a left outer join (gives all the rows of Table A with all matching rows of Table B or NULL) and a right outer join where Table A is NULL (gives all the rows of Table B that have no match in Table A -- matches are already provided in first query).
In the first query, there will always be a Qty value from Table A with either a Qty value or NULL from Table B. In the second query, there will only be a Qty value from Table B.
See Fiddle results.
select a.Name, a.Qty + IsNull( b.Qty, 0 ) as Qty
from #TableA a
left outer join #TableB b
on b.Name = a.Name
union all
select b.Name, b.Qty
from #TableA a
right outer join #TableB b
on b.Name = a.Name
where a.Name is null;
You may use union or union all with the same results. Since there is less processing required with union all, that's what I chose.

What is wrong with my SQL query which uses Joins and Unions?

What's wrong with my sql query? I am trying to use a Join and at the same time a UNION to get all table from another table while joining other tables together based on a relationship ..
However I get the following error:
"The used SELECT statements have a different number of columns"
My query:
SELECT a.ESN, a.UnixTime, a.Payload, a.Timestamp
,b.AlarmingStatus
,b.STxModel
,c.GroupID
FROM STxMessage a
JOIN STx b ON b.ESN = a.ESN
JOIN GroupInfo c ON b.GroupID = c.GroupID
WHERE b.STxModel = 190
AND a.AlarmsChecked="y"
AND c.AlertsMasterSwitch="on"
UNION ALL
SELECT d.ESN , d.UnixTime, d.Payload, d.Timestamp FROM STxMessageArchive d
The error message says it all.
When using UNION, the columns return by the combined SELECT statement must be the same, eg.
SELECT col1, col2, col3 FROM table1
UNION
SELECT col1, col2, col3 FROM table2
if the columns do not match, you can still combine it provided that you have to provide dummy data for the column, eg
SELECT col1, col2, col3 FROM table1
UNION
SELECT col1, col2, '' AS col3 FROM table2
so in your query, it should look like this
SELECT a.ESN, a.UnixTime, a.Payload, a.Timestamp ,
b.AlarmingStatus, b.STxModel, c.GroupID
FROM STxMessage a
INNER JOIN STx b
ON b.ESN = a.ESN
INNER JOIN GroupInfo c
ON b.GroupID = c.GroupID
WHERE b.STxModel = 190 AND
a.AlarmsChecked="y" AND
c.AlertsMasterSwitch="on"
UNION ALL
SELECT d.ESN, d.UnixTime, d.Payload, d.Timestamp,
NULL AS AlarmingStatus, NULL AS STxModel, NULL AS GroupID
FROM STxMessageArchive d
hi this are the extra columns in your first query
,b.AlarmingStatus
,b.STxModel
,c.GroupID
you need this same columns in second query to do union or you need to remove this column to do union operation
Your first query is selecting 7 columns where as the second query is only selecting 4. You need to make sure the second query is selecting the same number of columns as the first to make the Union All work.
SELECT a.ESN, a.UnixTime, a.Payload, a.Timestamp
,b.AlarmingStatus
,b.STxModel
,c.GroupID
FROM STxMessage a
JOIN STx b ON b.ESN = a.ESN
JOIN GroupInfo c ON b.GroupID = c.GroupID
WHERE b.STxModel = 190
AND a.AlarmsChecked="y"
AND c.AlertsMasterSwitch="on"
UNION ALL
SELECT d.ESN , d.UnixTime, d.Payload, d.Timestamp, 'null' as AlarmingStatus,
'null' as STxModel, 'null' as GroupID FROM STxMessageArchive d
As the error says, the first part has 7 columns, and the second part has only 4. An unions needs to have the same columns on both sides. Either remove
b.AlarmingStatus ,b.STxModel ,c.GroupID
from the first part, or add (even bogus) columns in the second part.