If you are familiar with financial securities, you know they are uniquely identified by their Ticker (i.e. MSFT) and/or their CUSIP. You probably also know that there are dozens of vendors which provide financial data. Let’s say you needed to provide a security’s price from the most reliable source using the most reliable identifier. How would you do it?
Let’s assume we have a single security:
Ticker: JC
CUSIP : 012345678
Security ID: 1 (which is consistent across all sources)Our requirements state that prices should be retrieved with the following priority:
1. Source A by CUSIP
2. Source B by Ticker
3. Source A by Ticker
4. Source B by CUSIPWhat’s the data look like?
In Source A, the security’s price of $10 can be found by CUSIP.
In Source B, the security’s price of $15 can be found by Ticker.
In Source A, the security’s price of $20 can be found by Ticker.
In Source B, the security’s price of $25 can be found by CUSIP.
We took on the problem a couple of ways, but ultimately we found the following provided the best performance and most accurate results.
It doesn’t take long to realize a temporary table (or table variable) is required. In this case, we chose to use the temporary table so an index could be applied. Though the following example is truly “dumbed down,” in real life, there were approximately 20 combinations to handle.
Create Table #Results
(
PriorityID int identity(1,1),
SecurityID int,
Ticker varchar (5) NULL,
CUSIP varchar (9) NULL,
Price money NULL
)
We opted to dump all matches into our temporary table using a giant union.
Insert Into #Results (SecurityID, Ticker, CUSIP, Price)
Select SecurityID, Ticker, Price
From SourceA
Where CUSIP = ’012345678′Union All
Select SecurityID, Ticker, Price
From SourceB
Where Ticker = ‘JC’Union All
Select SecurityID, Ticker, Price
From SourceA
Where Ticker = ‘JC’Union All
Select SecurityID, Ticker, Price
From SourceB
Where CUSIP = ’012345678′
So, #Results would include the following rows:
PriorityID, SecurityID, Ticker, CUSIP, Price
1, 1, null, 012345678, $10
2, 1, JC, null, $15
3, 1, JC, null, $20
4, 1, null, 012345678, $25
We then we returned the row which the lowest PriorityID. This was the first row inserted into the table and was effectively our best price based on priority.
Select SecurityID, Ticker, Price, SortOrder
From #Results
Where PriorityID in ( Select Min( PriorityID ) From #Results group by [SecurityID])
Pretty slick solution, right? Well, we thought so as well, but as it works out there’s a huge flaw. You see, we were working under the assumption that SQL Server would insert the result of the first select statement of the union into the #Results table before moving onto other union sections. Well, we thought wrong. We weren’t able to determine why, but each execution of the routine might return the results in a different order. (Our best guess is that SQL Server operates on a first come – first serve basis. Whichever result comes back first get inserted into the temp table first.)
Today’s implementation is slightly different. We have added a SortOrder column to the #Results table.
Create Table #Results
(
PriorityID int identity(1,1),
SecurityID int,
Ticker varchar (5) NULL,
CUSIP varchar (9) NULL,
Price money NULL,
SortOrder int
)Insert Into #Results (SecurityID, Ticker, CUSIP, Price, SortOrder )
Select SecurityID, Ticker, Price, 1 as SortOrder
From SourceA
Where CUSIP = ’012345678′Union All
Insert Into #Results (SecurityID, Ticker, Price, SortOrder )
Select SecurityID, Ticker, Price, 2 as SortOrder
From SourceB
Where Ticker = ‘JC’Union All
Insert Into #Results (SecurityID, Ticker, CUSIP, Price, SortOrder )
Select SecurityID, Ticker, Price, 3 as SortOrder
From SourceA
Where Ticker = ‘JC’Union All
Insert Into #Results (SecurityID, Ticker, CUSIP, Price, SortOrder )
Select SecurityID, Ticker, Price, 4 as SortOrder
From SourceB
Where CUSIP = ’012345678′Order By SortOrder
Now we are inserting security information into the #Results table with consideration for SortOrder and the correct results are consistently returned.
Select SecurityID, Ticker, Price
From #Results
Where PriorityID in ( Select Min( PriorityID ) From #Results group by [SecurityID])
We haven’t measured performance, but slow and accurate is better than fast and inaccurate…
Didn’t notice it before…quite clever.
It’s unfortunate CUSIPs aren’t always present and tickers aren’t always consistent in the financial world. This trick will definitely come in handy for my job dealing with financial securities using SQL. Nice work!
Cool article ! Come at the right time (even if I’m 2 years late)
I had a big SQL procedure that return results from the same source but I wanted to prioritize results based on which filter returned that row.
I talk a lot with my collegue at Developer IT and the only way to do it was to do a single request per criteria and dump all results in a temp table.
I cam across this post and saw that you recommend usign union. Weird. We thought that usign a temp table would prevent us from usign unions. Can’t you dump separate queries into the same temp table. Will it prevent having duplicates ?
Hey just found out i’m 4 years late
hahaha
@DeveloperIT – There should be no problem using unions in conjunction with a temp table and, sure, you can insert the results of multiple queries into a temporary table too. Union will remove duplicates from your result set whereas Union All will return all rows in each result set without filtering. Slight, but important, difference. By the way, glad you found the post.
@BenGriswold – I finalized my stored proc. usign a temp table and unions and I came across another problem ! (BTW, this is why we loved programming ! solving a problem makes you find another one)
Sounds like UNION remove deplicates by looking at all the returned (or inserted in that case) columns. I used a temp table to contains all search pks and the FREETEXT rank… I was then obliged to use a second temp table to insert the SUM(rank) group by pk and then do a select query to get the paged data usign a select OVER.
Not bad… The I feel like it’s not optimzed. You can try it to see the time of response. (Search asp.net on Developer IT)
Oh ! By the way…
I added your blog on Developer IT. Interesting blog !
Thanks.
@DeveloperIT – I checked it out and your search responds time seems just fine to me. Good work!
I have read a few excellent stuff here. Certainly price bookmarking for revisiting. I surprise how a lot effort you set to make any such magnificent informative website.
You can definitely see your enthusiasm within the work you write. The world hopes for even more passionate writers such as you who are not afraid to say how they believe. Always go after your heart.
I had been searching the net interested in some cool stuff and stummbled accross your blog. I desired to inform that that I think your web blog has the right pages so i have formerly saved this site to visit again soon Thanks!
whoah this blog is wonderful i love reading your articles. Keep up the great work! You know, a lot of people are searching around for this information, you can help them greatly.
I recently wanted to provide feedback and say that I truly enjoyed reading your blog post here.
I like what you guys are up also. Such smart work and reporting! Carry on the superb works guys I¡¦ve incorporated you guys to my blogroll. I think it will improve the value of my site