How to find extra/different rows in table compared with another table? - sql

I am trying to find the rows that are extra or different when comparing two tables in SQL Server.
For example, #temp1 has 5000 rows and #temp2 has 5125 rows.
I've tried doing
SELECT * FROM #temp1
EXCEPT
SELECT * FROM #temp2
but it returns empty results.
So I tried
SELECT * FROM #temp1
INTERSECT
SELECT * FROM #temp2
and this returns 5000 rows, which is the amount of rows from #temp1, so that shows me that #temp2 contains all the rows that exist in #temp1, but it just has 125 extra rows. I am trying to write a query that shows just those extra 125 rows from #temp2.

You can try this
SELECT *
FROM #table2
WHERE NOT EXISTS
(SELECT *
FROM #table1
WHERE #table1.PK = #table2.PK)
Or
SELECT *
FROM #table2 t1
LEFT JOIN #table2 t2 ON t2.PK = t1.PK
WHERE t2.PK IS NULL
This would work on any of SQL databases

Your order is not correct
SELECT * FROM #temp2
EXCEPT
SELECT * FROM #temp1

One fun way is:
(SELECT * FROM #temp2
UNION
SELECT * FROM #temp2
) EXCEPT
(SELECT * FROM #temp1
INTERSECT
SELECT * FROM #temp2
)
This just shows the unique rows that are different. It doesn't show which table they were originally in.

Related

SQL: JOIN problem using temp tables and one column

I am created two temp tables in which TABLE1 contains all the items and TABLE2 only has the partial list of TABLE1. How can I find out which parts TABLE 1 has that TABLE2 doesn't have or vice versa? Please keep in mind, the temp table only has one column due to the DISTINCT statement.
I do have to use Joins but my thought is if I JOIN on the individual columns of each table and then in the Where clause state that e.g. column 1 is not equal column 2, it's contradicting.
IF EXISTS (
SELECT *
FROM tempdb.dbo.sysobjects
WHERE id = Object_id(N'tempdb..#TABLE1')
)
BEGIN
DROP TABLE #TABLE1
END
IF EXISTS (
SELECT *
FROM tempdb.dbo.sysobjects
WHERE id = Object_id(N'tempdb..#TABLE2')
)
BEGIN
DROP TABLE #TABLE2
END
------------------------------------------------
select distinct 1.parts as #TABLE1 from List1 1 --- MAIN LIST
select distinct 2.parts as #TABLE2 from List2 2 --- ADDITIONAL LIST
select *
from #TABLE2 left join
#TABLE1
on 2.parts = 1.parts
where 2.parts <> 1.parts
Your where clause is undoing the left join. I would recommend not exists:
select t1.*
from #table1 t1
where not exists (select 1 from #table2 t2 where t2.parts = t1.parts);

SQL - How to get duplicate records from one table with join on another table

I want to get records from Table1 where it has more than one record for same Access Number, but at the same time this Access Number should also be present in Table2.
Example
Table1
Access Number
- 1000
- 1000
- 1000
- 2000
- 3000
- 4000
- 5000
- 5000
Table2
AccessNumber Value
- 1000 -Value1000
- 1000 -Value9999
- 2000 -Value2000
- 3000 -Value3000
The result of the query should be 1000 - Value1000
This is what I've got so far, please suggest
SELECT a.AccessNumber, b.Valuefrom Table1 a
JOIN Table2 b on b.AccessNumber = a.AccessNumber
Group by a.AccessNumber, b.VAlue
HAVING COUNT(1) > 1;
The problem i am facing is the query returns duplicates from Table2.
1000 - Value1000
1000 - Value9999
Without much information as to why 1000 - Value1000 should be the result instead of 1000 - Value9999, we can just get the first record from the top:
select top 1 * from (
-- your original query
SELECT a.AccessNumber, b.Value from Table1 a
JOIN Table2 b on b.AccessNumber = a.AccessNumber
Group by a.AccessNumber, b.Value
HAVING COUNT(1) > 1
-- your original query
) as x;
If we are just looking for the AccessNumber that has duplicate records, you can remove the b.Value from the selected columns and remove that as well in the Group By clause.
SELECT a.AccessNumber from Table1 a
JOIN Table2 b on b.AccessNumber = a.AccessNumber
Group by a.AccessNumber
HAVING COUNT(1) > 1;
http://www.sqlfiddle.com/#!18/3f114/3
We might be able to get this working using SQL Server's EXCEPT operator. But one sensible way to go here would be to just aggregate both tables by access number, and then left join the first to the second, retaining only access numbers which appear in both tables and which appear in greater quantity in the first than the second.
SELECT t1.AccessNumber
FROM
(
SELECT AccessNumber, COUNT(*) AS cnt_1
FROM Table1
GROUP BY AccessNumber
) t1
LEFT JOIN
(
SELECT AccessNumber, COUNT(*) AS cnt_2
FROM Table2
GROUP BY AccessNumber
) t2
ON t1.AccessNumber = t2.AccessNumber
WHERE
t1.cnt_1 - COALESCE(t2.cnt_2, 0) > 0 AND t2.cnt_2 IS NOT NULL;
Demo
--Hope this query will help you
; with cte_accessnumber(Accessnumber, [value])
as
(
select
t1.Accessnumber, count(*)NoOfRecords
from table1 t1
where exists (
select top 1 1 from table2 t2 where t2.Accessnumber = t1.Accessnumber)
group by t1.Accessnumber
having count(*)>1
)
select t1.Accessnumber, t2.[value]
from cte_accessnumber t1
inner join table2 t2 on t2.Accessnumber = t1.Accessnumber and t2.[value] like '%' + convert(varchar(20),t1.Accessnumber) +'%'
you may try using nested for the criteria that AccessNumber rows > 1 and cross apply for criteria which only display 1 record
DECLARE #tblA AS TABLE
(
AccessNumber INT
)
DECLARE #tblB AS TABLE
(
AccessNumber INT,
colB NVARCHAR(50)
)
INSERT INTO #tblA SELECT 1000
INSERT INTO #tblA SELECT 1000
INSERT INTO #tblA SELECT 1000
INSERT INTO #tblA SELECT 1000
INSERT INTO #tblA SELECT 2000
INSERT INTO #tblA SELECT 3000
INSERT INTO #tblA SELECT 4000
INSERT INTO #tblB SELECT 1000,'hello'
INSERT INTO #tblB SELECT 1000,'hello2'
INSERT INTO #tblB SELECT 2000,'world'
-- Query --
SELECT tblB.* FROM (
SELECT AccessNumber,COUNT(1) AS cnt FROM #tblA GROUP BY AccessNumber ) ftblA
CROSS APPLY (SELECT TOP(1) * FROM #tblB itblB WHERE ftblA.AccessNumber = itblB.AccessNumber) tblB
WHERE ftblA.cnt >1
-- Only 1000 should be displayed only --
Use subquery with correlation approach
select access_no,
(select top 1 Value from table2 where access_no = t.access_no) as value
from Table1 t
where exists (
select 1 from table2 where access_no = t.access_no)
group by access_no
having count(*) > 1
However, you would need to specify order by clause in subquery in order to fetch the value from table2

Write subquery in xml.exist()

I have a table with one column:
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
CREATE TABLE #tmp
(
data XML
)GO
with one record:
INSERT INTO #tmp
VALUES ( N'<RelevanExpertXML><Tel><RelevanExpert>1</RelevanExpert></Tel><Tel><RelevanExpert>2</RelevanExpert></Tel></RelevanExpertXML>')
and another tale with one column
CREATE TABLE #tmp2
(
id int
)
GO
and i want to write this query:
select *
from #temp
where xml.exist('/RelevanExpertXML/Tel/RelevanExpert[(text()) = [select id from #temp2]]') = 1
in fact i want to write sub query in exist(), but i get error, and also i can't change from clause and select list, only i can change where clause.
thanks for help.
you can use this query:
select *
from #tmp
where exists
(
select *
from #tmp2 as t
where
#tmp.data.exist('/RelevanExpertXML/Tel/RelevanExpert[(text()) = sql:column("t.id")]') = 1
)
but it will return you the whole xml. If you want to split xml by rows, you have to use nodes() function in the from clause.
You can write
select *
from #tmp, #temp2
where data.exist('/RelevanExpertXML/Tel/RelevanExpert[text()] = sql:column("id")')=1
But I don't think it will give you the results you're after.
You may be after something more like
select t.r.value('.','int')
from #tmp temp
cross apply data.nodes('/RelevanExpertXML/Tel/RelevanExpert') t(r)
inner join #temp2 t2 on t.r.value('.','int') = t2.id

What's the best way to select data only appearing in one of two tables?

If I have two tables such as this:
CREATE TABLE #table1 (id INT, name VARCHAR(10))
INSERT INTO #table1 VALUES (1,'John')
INSERT INTO #table1 VALUES (2,'Alan')
INSERT INTO #table1 VALUES (3,'Dave')
INSERT INTO #table1 VALUES (4,'Fred')
CREATE TABLE #table2 (id INT, name VARCHAR(10))
INSERT INTO #table2 VALUES (1,'John')
INSERT INTO #table2 VALUES (3,'Dave')
INSERT INTO #table2 VALUES (5,'Steve')
And I want to see all rows which only appear in one of the tables, what would be the best way to go about this?
All I can think of is to either do:
SELECT * from #table1 except SELECT * FROM #table2
UNION
SELECT * from #table2 except SELECT * FROM #table1
Or something along the lines of:
SELECT id,MAX(name) as name FROM
(
SELECT *,1 as count from #table1 UNION ALL
SELECT *,1 as count from #table2
) data
group by id
HAVING SUM(count) =1
Which would return Alan,Fred and Steve in this case.
But these feel really clunky - is there a more efficient way of approaching this?
select coalesce(t1.id, t2.id) id,
coalesce(t1.name, t2.name) name
from #table1 t1
full outer join #table2 t2
on t1.id = t2.id
where t1.id is null
or t2.id is null
The full outer join guarantees records from both sides of the join. Whatever record that does not have in both sides (the ones you are looking for) will have NULL in one side or in other. That's why we filter for NULL.
The COALESCE is there to guarantee that the non NULL value will be displayed.
Finally, it's worth highlighting that repetitions are detected by ID. If you want it also to be by name, you should add name to the JOIN. If you only want to be by name, join by name only. This solution (using JOIN) gives you that flexibility.
BTW, since you provided the CREATE and INSERT code, I actually ran them and the code above is a fully working code.
You can use EXCEPT and INTERSECT:
-- All rows
SELECT * FROM #table1
UNION
SELECT * FROM #table2
EXCEPT -- except
(
-- those in both tables
SELECT * FROM #table1
INTERSECT
SELECT * FROM #table2
)
Not sure if this is any better than your EXCEPT and UNION example...
select id, name
from
(select *, count(*) over(partition by checksum(*)) as cc
from (select *
from #table1
union all
select *
from #table2
) as T
) as T
where cc = 1

SQL I want to duplicate record on insert

Without using a while or forloop, is there a way to insert a record two or more times on a single insert?
Thanks
INSERT INTO TABLE2 ((VALUE,VALUE)
SELECT VALUE,VALUE FROM TABLE1 )) * 2
You would need to CROSS JOIN onto a table with 2 rows. The following would work in SQL Server.
INSERT INTO TABLE2 ((VALUE,VALUE)
SELECT VALUE,VALUE
FROM TABLE1, (SELECT 1 UNION ALL SELECT 2) T(C)
If you have an auxilliary numbers table you could also do
SELECT VALUE,VALUE
FROM TABLE1 JOIN Numbers ON N <=2
--first create a dummy table with 2 records
INSERT INTO TABLE2 ((VALUE,VALUE)
SELECT VALUE,VALUE FROM TABLE1, dummytable ))
This is not an elegant way, but could work easily.
If you have a table with an high enough number of records you can do the cross join with a TOP clause
INSERT INTO TABLE2
SELECT VALUE,VALUE FROM TABLE1
cross join (select top 2 TABLE_DUMMY) as DUMMY
This works for MQ SqlServer, to let it work in other DBMS you should change the TOP with the keyword needed by your DBMS