fields that don't match - sql

I have 3 fields per row in table 1 that I want to compare to the exact same fields per row in table 2
I have been playing around with NOT IN, but I am not having any luck. Can anyone help?
Basically I want to see all records from both tables where field 1, field 2, field 3 don't match in table 2
How would this be written?

SELECT *
FROM Table1 T1
FULL OUTER JOIN Table2 T2
ON T1.col1 = T2.col1
AND T1.col2 = T2.col2
AND T1.col3 = T2.col3
WHERE T1.col1 <> T2.col1
OR T1.col2 <> T2.col2
OR T1.col3 <> T2.col3

jyparask's answer is close, but it may not work if some of your columns are nullable and the where clause is off a bit. In that case you need to either coalesce the columns to a value they couldn't actually contain, or check for nulls manually.
I'm going to assume col1 is not nullable, and cols 2 and 3 are
SELECT *
FROM Table1 T1
FULL OUTER JOIN Table2 T2
ON T1.col1 = T2.col1
AND (T1.col2 = T2.col2 or (T1.col2 is null and T2.col2 is null))
AND (T1.col3 = T2.col3 or (T1.col3 is null and T2.col3 is null))
WHERE T1.col1 is null or T2.col1 is null # assuming col1 is not null
Or with a coalesce:
SELECT *
FROM Table1 T1
FULL OUTER JOIN Table2 T2
ON T1.col1 = T2.col1
AND COALESCE(T1.col2,-1) = COALESCE(T2.col2,-1)
AND COALESCE(T1.col3,-1) = COALESCE(T2.col3,-1)
WHERE T1.col1 is null or T2.col1 is null # assuming col1 is not null

Related

How to update all the value from one column to another

Table 1 -
col1
col2
1
443
2
449
3
479
Table 2 -
col1
col2
1
NULL
2
NULL
3
NULL
Note - col1 is not a PK
expected Output -
Table 2 -
col1
col2
1
443
2
449
3
479
This is my python code:
abc_df = pd.read_sql_query("UPDATE Table1 SET Table1_id = t2.col1 FROM Table1 t1
INNER JOIN Table2 t2 ON t1.col2 = t2.col2", con=connection)
But My Actual OUTPUT is -
Table 2 -
col1
col2
1
443
2
443
3
443
You need to update your UPDATE query -
UPDATE Table2 T2
SET col2 = T1.col2
FROM Table1 t1
WHERE T1.col1 = T2.col1
in your update query there is 2 part are wrong first one where set the table2 data, you used Table1_id = t2.col1 but you should used t2.col2 = t1.col2 and other wrong thing in join condition you used t1.col2 = t2.col2 but you should used t1.col1 = t2.col1
update your query as per following
Update Table2 T2 SET T2.col2 = T1.col2 FROM
Table1 t1 WHERE T1.col1 = T2.col1
The only thing I will add to existing answers is in general updates can be expensive operations, so put in a check to be sure you NEED to update it before you do it.
update table1 t1
set col2 = t2.col2
from table2 t2
where
t1.col1 = t2.col1 and
t1.col2 is distinct from t2.col2 -- added this check
Because of MVCC, it will actually create a new record and create a dead space where the previous record was (until the next vacuum), so it makes senses on many levels to not update a record if all you are doing is replacing the value.
I realize in your example you were replacing all nulls, but in general this is just a good practice.

SQL-conditional joins

I have a requirement to join two tables Conditionally.
For Ex:
T1
--Col1
--Col2
--Col3
T2
--Col1
--Col2
--Col3
Join T1 and T2 on Col1 from both tables, If Col1 is NULL then join T1 and T2 on Col2 from both tables.
How to achieve this?
you can use a query like below, which uses case condition in join crtieria
select *
from
t1 join t2
on
case
when (t1.col1 is NULL or t2.col1 is NULL)
then t1.col2
else t1.col1
end
=
case
when (t1.col1 is NULL or t2.col1 is NULL)
then t2.col2
else t2.col1
end
Your conditions are ambiguous. What if col1 is NULL in only one table?
In any case, you can specify the logic as something like:
select . . .
from t1 join
t2
on (t1.col1 = t2.col1) or
( (t1.col1 is null or t2.col1 is null) and t1.col2 = t2.col2)
The first condition will fail if either column is NULL.
SELECT
*
FROM
T1 INNER JOIN T2
ON T1.COL1=T2.COL1 OR T1.COL2=T2.COL2
I am using Inline view. Check this query.
select * from
(select t1.col1,t1.col2,t1.col3 from table1 t1)q1,
(select t2.col1,t2.col2,t2.col3 from table1 t2)q2
where t1.col1=t2.col1 or t1.col2=t2.col2;
Thanks All :)
I just tot of the below query, let me know if this works.
Select * from
(select col1, col2, col3, coalesce(col1, col2) as join_key)q1,
(select col1, col2, col3, coalesce(col1, col2) as join_key)q1,
where q1.join_key = q2.join_key

SQL - Compare all column's data from two tables efficiently

I have two tables say Table1 and Table2 with same columns and same schema structure.
Now I want to select data from Table1 which is present in Table2. However, when comparing data, I want to compare all the columns present in both these table. Like, entire record in Table1 should be present in Table2. What is the fastest and most efficient way to achieve this in SQL Server 2008? Both the tables contain around 1000-2000 records and these tables will get accessed very frequently.
The intersect operator does just that:
SELECT *
FROM table1
INTERSECT
SELECT *
FROM table2
With an " exists", you have a solution :
select * from Table1 t1 where exists (select 1 from Table2 t2
where t1.col1 = t2.col1
and t1.col2 = t2.col2
and t1.col3 = t2.col3
and ... // check here all columns ...
)
There is however a little problem in this solution in the case of null values, which can only be tested via a "IS NOT NULL" or "IS NULL", hence the complementary solution:
select * from Table1 t1 where exists (select 1 from Table2 t2
where (t1.col1 = t2.col1 or (t1.col1 IS NULL and t2.col1 IS NULL))
and (t1.col2 = t2.col2 or (t1.col2 IS NULL and t2.col2 IS NULL))
and (t1.col3 = t2.col3 or (t1.col3 IS NULL and t2.col3 IS NULL))
and ... // check here all columns ...
)

Compare Two SQL identical Table to find missing records

I am working on SQL 2008. I have two identical tables with same column names.
On Table2, i am missing some records. Some records got deleted in the Table2.
I have to compare Table1 and Table2 and retrieve only missing records from table1.
Use a LEFT JOIN and check for IS NULL like below. where t2.col2 is null will be TRUE only when there are records in table1 which are not present in table2. Which is what you are looking for. [This is a sample code and have no resemblance with your original query]
select t1.col1,t1.col2,t1.col3
from table1 t1
left join table2 t2 on t1.some_column = t2.some_column
where t2.col2 is null
You should use SQL Except. There is no Join involved.
Select * from dbo.TableA
Except
Select * from dbo.TableB
In set theory, the difference of sets A, B (A-B) is the set of elements that belong to A and do not belong to B.
With an " not exists", you have a solution :
select * from Table1 t1 where not exists (select 1 from Table2 t2
where t1.col1 = t2.col1
and t1.col2 = t2.col2
and t1.col3 = t2.col3
and ... // check here all columns ...
)
There is however a little problem in this solution in the case of null values, which can only be tested via a "IS NOT NULL" or "IS NULL", hence the complementary solution:
select * from Table1 t1 where not exists (select 1 from Table2 t2
where (t1.col1 = t2.col1 or (t1.col1 IS NULL and t2.col1 IS NULL))
and (t1.col2 = t2.col2 or (t1.col2 IS NULL and t2.col2 IS NULL))
and (t1.col3 = t2.col3 or (t1.col3 IS NULL and t2.col3 IS NULL))
and ... // check here all columns ...
)

Getting next row using SQL

I have 1 table with data thus:
Col1 Col2
------- --------
Admin001 A
Admin001 B
Admin002 C
Admin002 C
Admin003 A
Admin003 C
I need to find all instances of Col2 values with 'A' immediately followed by 'B'. 'A' followed by any other symbol does not count. Is there a way to use SQL to accomplish this?
Environment is DB2 LUW v9.5
Update:
How can I do this if I make the table like below?
Col1 Col2 Col3
---- ------- --------
1 Admin001 A
2 Admin002 C
3 Admin002 C
4 Admin003 A
5 Admin003 C
6 Admin001 B
7 Admin001 A
8 Admin001 C
9 Admin001 B
Given that there is no implicit ordering of a set, then no, there isn't any reliable way to do this. Your data will need to be ordered (perhaps by a third column, or by column 1) for this to make any sense.
SELECT DISTINCT T1.Col2
FROM Table T1 INNER JOIN Table T2
ON T2.Col2 = T1.Col2 AND T2.Col1 = (T1.Col1 + 1)
WHERE T1.Col3 = 'A' AND T2.Col3 = 'B'
Update: As mentioned by Peter Lang, below, this will not work if the sequence in Col1 is interrupted. This version handles that situation and is more guaranteed to produce the correct result although if you're 100% certain the sequence will not be interrupted (that is, if you generate the sequence yourself in the same transaction as the analysis) the first should be faster:
SELECT DISTINCT T1.Col2
FROM Table T1 INNER JOIN Table T2
ON T2.Col2 = T1.Col2
AND T2.Col1 = (SELECT MIN(Col1) FROM Table T3 WHERE T3.Col1 > T1.Col1)
WHERE T1.Col3 = 'A' AND T2.Col3 = 'B'
It looks like you're trying to find out who's grade dropped from A to B, so we'll also assume that you want the results where B follows A for the same admin.
SELECT DISTINCT t1.Col2
FROM table t1
INNER JOIN table t2 ON t1.Col2 = t2.Col2
LEFT OUTER JOIN table t3 ON t1.Col2 = t3.Col2
AND t3.Col1 < t2.Col1 AND t3.Col1 > t1.Col1
WHERE t1.Col3 = 'A'
AND t2.Col3 = 'B' AND t2.Col1 > t1.Col1
AND t3.Col1 IS NULL
This yields any admin who has 'A' followed by 'B'.
The INNER JOIN and the first two expressions in the WHERE clause finds all records where 'B' occurs after 'A'. The left OUTER join and the last expression in the WHERE clause finds all records where there are grades between the A and B, and only takes the records without.
You asked to get these results, one per row, like this:
Col1 Col2 Col3
---- ------- --------
1 Admin001 A
6 Admin001 B
I'm going to adapt the above query the easy way.
I'll simply get the A records, get the B records, and union them:
(SELECT t1.Col1, t1.Col2, t1.Col3
FROM table t1
INNER JOIN table t2 ON t1.Col2 = t2.Col2
LEFT OUTER JOIN table t3 ON t1.Col2 = t3.Col2
AND t3.Col1 < t2.Col1 AND t3.Col1 > t1.Col1
WHERE t1.Col3 = 'A'
AND t2.Col3 = 'B' AND t2.Col1 > t1.Col1
AND t3.Col1 IS NULL)
UNION
(SELECT t2.Col1, t2.Col2, t2.Col3
FROM table t1
INNER JOIN table t2 ON t1.Col2 = t2.Col2
LEFT OUTER JOIN table t3 ON t1.Col2 = t3.Col2
AND t3.Col1 < t2.Col1 AND t3.Col1 > t1.Col1
WHERE t1.Col3 = 'A'
AND t2.Col3 = 'B' AND t2.Col1 > t1.Col1
AND t3.Col1 IS NULL)
ORDER BY Col2, Col1
Notice that we're ordering by Col2 first, then Col1. You may also get more than one set of records for each user.
How are you sorting the columns? If you aren't sorting them, you could get different results each time, as sometimes A would follow B, and sometimes B would follow A. If you are sorting them, you may be able to use an 'exists' test with the sorting expression.
There is no general method of getting the next (or previous) row in SQL, but many implementations provide their own built-in functions to help with that kind of thing. Unfortunately I'm not familiar with DB2.
This query assumes that col1 is some sort of sequence or timestamp for each row. Without it, there's no way to determine if A happened before or after B.
WITH sorted AS
(SELECT col1, col2, col3, ROWNUMBER()
OVER (PARTITION BY col2 ORDER BY col1) AS col4
FROM sometable
)
SELECT a.col1, a.col2, a.col3, b.col1, b.col3
FROM sorted a INNER JOIN sorted b
ON a.col2 = b.col2
WHERE a.col3 = 'A' AND b.col3 = 'B'
AND b.col4 = a.col4 + 1
;
I think the following should work, assuming your updated table layout with 3 columns. (Otherwise it's impossible, because no ordering is available):
select t1.col2
from yourtable t1, yourtable t2
where t1.col3 = 'A'
and t2.col3 = 'B'
and t1.col1 + 1 = t2.col1;