I want to lookup values listed in a temp table:
So let us say:
Create Table #mylist
(
eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS,
refdate datetime
)
Insert Into #mylist (eserial, refdate) Values ('A', '2015-09-15')
Insert Into #mylist (eserial, refdate) Values ('B', '2015-09-14')
Insert Into #mylist (eserial, refdate) Values ('C', '2015-09-13')
Insert Into #mylist (eserial, refdate) Values ('D', '2015-09-12')
I need the result to be the Top 1
date less than the reference date.
And should be returned in the same sequence as is in the temporary table.
What I tried:
Select
lst.eserial,
lst.refdate,
app.CREATEDDATETIME,
From #mylist lst
Outer Apply
(Select Top 1 rec.CREATEDDATETIME, rec.ESERIAL, rec.ITEMID
From TableSource rec
Where lst.eserial=rec.ESERIAL And rec.CREATEDDATETIME<lst.refdate
Order By rec.CREATEDDATETIME Desc
) As app
This works but it is slow. Also, if the number of rows are increased, it does not consistently preserve the sequence of eserial
. I need the query to preserve the order I put it in the temporary table.
Again my expected output is simply:
Where eserial
is the same sequence as the temp table and CREATEDDATETIME
is the maximum date less than the reference date. More like a conditional Vlookup
if you know Excel.
It is not quite clear what you mean by
maintain the sequence of the items in the temporary table
, but if you want to get result ordered by eserial
, then you have to add ORDER BY eserial
to your query. Without ORDER BY
the resulting rows can be returned in any order. This applies to any method that you choose.
So, taking your last query as a basis, it will look like this:
Select
lst.eserial
,lst.refdate
,app.CREATEDDATETIME
From
#mylist lst
Outer Apply
(
Select Top 1 rec.CREATEDDATETIME
From TableSource rec
Where lst.eserial=rec.ESERIAL And rec.CREATEDDATETIME<lst.refdate
Order By rec.CREATEDDATETIME Desc
) As app
ORDER BY lst.eserial;
To make it work fast and efficiently add an index to TableSource
on (ESERIAL, CREATEDDATETIME)
. Order of columns in the index is important.
It is also important to know if there are any other columns that you use in OUTER APPLY
query and how you use them. You mentioned column AREAID
in the first variant in the question, but not in the last variant. If you do have more columns, then clearly show how you intend to use them, because the correct index would depend on it. The index on (ESERIAL, CREATEDDATETIME)
is enough for the query I wrote above, but if you have more columns a different index may be required.
It would also help optimizer if you defined your temp table with a PRIMARY KEY
:
Create Table #mylist
(
eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS PRIMARY KEY,
refdate datetime
)
Primary key would create a unique clustered index.
One more important note. What is the type and collation of columns ESERIAL
and CREATEDDATETIME
in the main TableSource
table? Make sure that types and collation of columns in your temp table matches the main TableSource
table. If the type is different (varchar
vs. nvarchar
or datetime
vs. date
) or collation is different index may not be used => it will be slow.
Edit
You use the phrase "same sequence as the temp table" several times in the question, but it is not really clear what you mean by it. Your sample data doesn't help to resolve the ambiguity. The column name eserial
also adds to the confusion. I can see two possible meanings:
eserial
column.My original answer implies (1): it returns rows from temp table ordered by values in eserial
column.
If you want to preserve the order of rows as they were inserted into the table, you need to explicitly remember this order somehow. The easiest method is to add an IDENTITY
column to the temp table and later order by this column. Like this:
Create Table #mylist
(
ID int IDENTITY PRIMARY KEY,
eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS,
refdate datetime
)
And in the final query use ORDER BY lst.ID
.
That's easy using identity. Query without Order
is not guarantee to have order in SQL server.
Create Table #mylist
(
seqId int identity(1,1),
eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS,
refdate datetime
)
Use the table freely and put Order By seqId
at the end of your query
Edit
Use MAX()
instead of TOP 1
with order if you have no cluster index on ESERIAL
, CREATEDDATETIME
on the TableSource
https://stackoverflow.com/a/21420643/1287352
Select
lst.eserial,
lst.refdate,
app.CREATEDDATETIME,
From #mylist lst
Outer Apply
(
Select MAX(rec.CREATEDDATETIME), rec.ESERIAL, rec.ITEMID
From TableSource rec
Where lst.eserial = rec.ESERIAL And rec.CREATEDDATETIME < lst.refdate
GROUP BY rec.ESERIAL, rec.ITEMID
) As app
ORDER BY lst.seqId
Perhaps the performance issue is due to indexing. Try adding the indexes below, removing UNIQUE
if the keys are not unique.
CREATE UNIQUE NONCLUSTERED INDEX idx ON #mylist (eserial, refdate);
CREATE UNIQUE NONCLUSTERED INDEX idx ON TableSource (eserial, CREATEDDATETIME);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With