在不改变参考表中的项目序列的情况下拉取数据

时间:2015-09-04 03:04:25

标签: sql sql-server sql-server-2008-r2

我想查找临时表中列出的值:

所以让我们说:

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')

我需要将结果作为Top 1日期小于参考日期 并且应该以与临时表中相同的顺序返回。

我尝试了什么:

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

这有效,但速度很慢。此外,如果行数增加,则不会始终保持eserial的序列。我需要查询来保存我把它放在临时表中的顺序。

我的预期输出很简单:

enter image description here

其中eserial与临时表的序列相同,CREATEDDATETIME的最大日期小于参考日期。如果您了解Excel,则更像是条件Vlookup

3 个答案:

答案 0 :(得分:6)

你的意思并不十分清楚

  

维护临时表中项目的顺序

,但如果您希望按eserial订购结果,则必须在查询中添加ORDER BY eserial。如果没有ORDER BY,则可以按任何顺序返回结果行。这适用于您选择的任何方法。

因此,以您的上一个查询为基础,它将如下所示:

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;

要使其快速有效地在TableSource上向(ESERIAL, CREATEDDATETIME)添加索引。索引中的列顺序很重要。

了解在OUTER APPLY查询中是否还有其他列以及如何使用它们也很重要。您在问题的第一个变体中提到了列AREAID,但在最后一个变体中没有。如果你有更多的列,那么清楚地显示你打算如何使用它们,因为正确的索引将取决于它。 (ESERIAL, CREATEDDATETIME)上的索引对于我上面写的查询已经足够了,但是如果你有更多列,则可能需要不同的索引。

如果您使用PRIMARY KEY

定义临时表,它也会有助于优化器
Create Table #mylist
(
    eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS PRIMARY KEY,
    refdate datetime
)

主键会创建一个唯一的聚簇索引。

另一个重要的注意事项。主ESERIAL表中列CREATEDDATETIMETableSource的类型和排序规则是什么?确保临时表中列的类型和排序规则与主TableSource表匹配。如果类型不同(varcharnvarchardatetimedate)或排序规则不同,则可能无法使用索引=&gt;它会很慢。

修改

您使用短语&#34;与临时表相同的序列&#34;在问题中多次,但你的意思并不是很清楚。您的示例数据无助于解决歧义。列名eserial也增加了混淆。我可以看到两种可能的含义:

  1. eserial列中的值排序的临时表中返回行。
  2. 以与插入时相同的顺序从临时表中返回行。
  3. 我的原始答案暗示(1):它返回临时表中按{​​{1}}列中的值排序的行。

    如果要在插入表格时保留行的顺序,则需要以某种方式明确记住此顺序。最简单的方法是在临时表中添加eserial列,然后按此列排序。像这样:

    IDENTITY

    在最终查询中使用Create Table #mylist ( ID int IDENTITY PRIMARY KEY, eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS, refdate datetime )

答案 1 :(得分:3)

使用身份很容易。没有Order的查询不保证在SQL服务器中有订单。

Create Table #mylist
(
    seqId int identity(1,1),
    eserial nvarchar(35) Collate SQL_Latin1_General_CP850_CI_AS,
    refdate datetime
)

自由使用该表并将Order By seqId放在查询的末尾

修改

如果您在MAX()上没有群集索引,而在TableSource上有TOP 1

,请使用ESERIAL代替CREATEDDATETIME

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 

答案 2 :(得分:1)

也许性能问题是由索引引起的。尝试添加以下索引,如果密钥不唯一,请删除UNIQUE

CREATE UNIQUE NONCLUSTERED INDEX idx ON #mylist (eserial, refdate);
CREATE UNIQUE NONCLUSTERED INDEX idx ON TableSource (eserial, CREATEDDATETIME);