August 29, 2007 at 7:07 pm
Hi,
We are exploring the least time consuming approach to be able to compare data between two fairly large tables.
The table to be compared ( table1 in this case) has about 800 Million rows and the table to be compared against (table2 in this case) will have at least 1 billion rows. The data in table is being populated by importing from about 250 flat files provided to us. This means that we have an option of importing the 250 flat files into several smaller tables instead of just one single table.
Both tables (1 and 2) have only one single column which is to be compared. The column to be compared in both the tables will be indexed. The data type of the columns are BIGINT. This will however be just one time process or doesn’t need to be scheduled.
Following are couple of approaches we have thought of:
1. Import the 250 flat files into one singe table (table2). Compare table1 with table2 which will be comparison of close to 1 billion rows against 1 billion single column rows.
2. Import the 250 flat files into multiple smaller tables, let’s say 20 tables. Write a script which will loop through and will compare table1 with one of the 20 tables at a time, if it finds the matching data in any of the tables, it will mark that value as matched and it will exit and will not do any further checks. We hope that this will in average might minimize the comparisons that will otherwise will be done in the 1st approach.
Any feedback or alternative least time consuming approach would be appreciated. Please let me know if further detail or clarification is needed.
Thanks
August 29, 2007 at 10:48 pm
If you're looking for matches, just index them and do an INNER JOIN between the two tables on that single column. If that's not what you're looking for, then more info would be helpful, such as are these numbers unique in each table, what exactly is the comparison methodology, etc.
August 30, 2007 at 12:41 am
Thanks for the response. I just need to get the values (rows) from table1 that does not exist in table2. The values in table1 will be unique, however in table2 there might be some duplicate values.
Thanks
August 30, 2007 at 2:48 am
SELECT t1.column
FROM table1 t1
LEFT JOIN table2 t2
ON t1.column = t2.column
WHERE t2.column IS NULL
..or..
SELECT t1.column
FROM table1 t1
WHERE NOT EXISTS ( SELECT * FROM table2 t2 WHERE t1.column = t2.column )
Both ways return t1.column where that value doesn't exist in table2.
Which one is most effective depends. Sometimes it's the first, sometimes the other.
You have to test it on the particular tables involved to know for sure.
/Kenneth
August 30, 2007 at 6:14 am
The only thing I can add is, that in my experience the first way - with LEFT JOIN - is better in most situations (not always).
So, regarding your original question, I would try the first method, and just two tables - no splitting. It's true that I've never worked with such huge datasets, biggest table in our DB had about 100 million rows, but LEFT JOIN performs well with large tables as far as I know.
August 30, 2007 at 6:56 am
Actually the left join will return all the rows that have matching records in the second table, not the missing records. You pretty much have to go with the not exists in the where clause.
James.
August 30, 2007 at 7:04 am
Oops! sorry didn't pay attention to the where clause. Joins are better then sub-selects under most circumstances. Thats what I get for not really looking at something before shooting my mouth off. My apologies.
James.
August 30, 2007 at 7:13 am
It happens
As to which would be best in this case, one has to try both and also preferrably look at the plans produced in order to choose. Other than that, which one to prefer is largely a personal preference.
/Kenneth
August 30, 2007 at 7:39 am
Are you sure that you have no duplicates in these tables? If you do, you could end up with the mother of all cross joins using a join.
This is a safe approach if you don’t know for sure if you have duplicates.
select
a.MY_COLUMN
from
(
select MY_COLUMN, T1 = 1, T2 = 0 from TABLE_1 group by MY_COLUMN
union all
select MY_COLUMN, T1 = 0, T2 = 1 from TABLE_2 group by MY_COLUMN
) a
group by
a.MY_COLUMN
having
max(T1) = 1 and max(T2) = 0
order by
a.MY_COLUMN
August 30, 2007 at 8:31 am
Trying to follow the logic in my head just made it hurt, so I did it in Management Studio. I then compared the actual execution plans agains ten rows of test data in each table. The first method with the left join results in a nested loop (for the left outer join) and an intermediate result set of 100 rows (table 1 rows times table 2 rows). The second method with the derived table looks like a more efficient solution because it never deals with more than 20 rows (table 1 rows plus table 2 rows), though the cost of the second query (with a test of 10 rows per table) is actually higher relative to the batch (of the two queries). The sorting for the "Distinct" appears to be the time killer on query two.
I wonder as the the total number of rows grows is there a break even point where the lack of the cartisian product of query 1 makes query 2 the better choice? Anyone have any thoughts?
James.
August 30, 2007 at 8:32 am
LOL... are you sure that JOIN is female? Maybe it would be a father 🙂
August 30, 2007 at 9:17 am
“…I wonder as the total number of rows grows is there a break even point where the lack of the Cartesian product of query 1 makes query 2 the better choice?...”
You could just try method 1. If it runs for hours until tempdb fills the disk, then you might want to switch to method 2. It really depends on the data. I just wanted to offer an alternative solution.
If would also be easier to use method 2 to implement if you import the data into multiple tables. Just include all the tables in the union.
August 30, 2007 at 9:34 am
I wasn't criticizing, it was a neat solution. I have now run all three methods against two tables with 1,000,000 records of random data in each table in a single batch with the following results:
Query 1: (Sub-Select) 22% of batch.
Query 2: (Left Join) 63% of batch.
Query 3: (Derived Table) 15% of batch
Query 3 looks like it's the most efficient, though time wise they were all within milliseconds of each other. It should be noted that the random data generated dups in Table 1 which results in multiple records with the same value in the compared column being retrieved with Query 1 and 2, however since Query 3 uses a group by it gets only 1 record per distinct value, which could make a difference depending on how the result is used.
I like Query 3 though it took me sometime to figure out why it worked, so on top of everything I learned a new technique.
James.
Here is the code I used for the test in case someone is interested:
if object_id('tab1_') is not null drop table tab1_
if object_id('tab2_') is not null drop table tab2_
go
create table tab1_ (id_ int identity,col1_ int)
create table tab2_ (id_ int identity,col1_ int)
go
create index i_tab1_ on tab1_ (col1_)
create index i_tab2_ on tab2_ (col1_)
go
insert into tab1_ (col1_)
SELECT TOP 1000000
SomeInt = CAST(RAND(CAST(NEWID() AS VARBINARY))*100000+1 AS INT)
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
go
insert into tab2_ (col1_)
SELECT TOP 1000000
SomeInt = CAST(RAND(CAST(NEWID() AS VARBINARY))*100000+1 AS INT)
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
go
/*
select top 1000 * from tab1_
select top 1000 * from tab2_
*/
--now test the different methods to find all rows in table 1 that do not exist in table 2
select *
from tab1_ t1
where not exists (select 1 from tab2_ t2 where t2.col1_ = t1.col1_)
go
Select t1.*
from tab1_ t1 left join tab2_ t2 on (t1.col1_ = t2.col1_)
where t2.col1_ is null
go
select
a.col1_
from
(
select col1_, T1 = 1, T2 = 0 from tab1_ group by col1_
union all
select col1_, T1 = 0, T2 = 1 from tab2_ group by col1_
) a
group by
a.col1_
having
max(T1) = 1 and max(T2) = 0
order by
a.col1_
go
August 30, 2007 at 12:47 pm
Hi guys,
Thanks yr prompt feedback and solution. All the solutions seems pretty feasible. The left outer join i guess might just work fine. I was reading about the data partitioning enhancements in SQL 2005. Do you think partioning the the t2 table would be least time consuming solution. Has anybody worked with table partitioning for large data before.
Thanks
August 31, 2007 at 2:04 am
The problem posed stated that the table1.column to be compared against was indeed unique.
It also said that the size was about 800 million.
Table2 size around a billion (20%) larger, and with 'some' dupes in table2.column.
Now, since t2.col may contain dupes, each dupe would produce 'extra rows' as t1.col * t2.col.
Since t1.col was unique, this is the same as 1 * # of t2 dupe values. (not a cartesian product)
When testing, it's important to create testdata with the same charachteristics, else the tests may show false results. The major problem here is how many is 'some' dupes..?
Perhaps it'd be more efficient to first pull out a distinct t2.column into it's own temp table, then do the left join or exists query on that. Now both are ensured to be unique, and any excessive intermediate results, cartesian funky-joins and such shouldn't be a problem. This is however extra overhead, so the same thing applies, you have to test it on the full load in order to see if the extra 'dupe cleansing perparation and indexing temptable' will be benificial overall or not.
Also, be aware that when testing the different variants, that using too small datavolumes for test compared to the actual intended volume, may not yíeld a truthful result. The volume itself is a very important factor when it comes to plans and performance in the end.
/Kenneth
Viewing 15 posts - 1 through 15 (of 15 total)
You must be logged in to reply to this topic. Login to reply