查找子查询中的最新记录(SQL Server)

时间:2017-02-16 22:25:15

标签: sql sql-server tsql subquery sql-order-by

我正在将一些代码从Oracle转换为SQL Server(2012)并且遇到了这个子查询使用PARTITION / ORDER BY来检索最新记录的问题。子查询本身运行正常,但由于它是子查询,我收到错误:

  

SQL Server数据库错误:ORDER BY子句在视图中无效,   内联函数,派生表,子查询和公用表   表达式,除非还指定了TOP,OFFSET或FOR XML。

这是SQL的一部分:

FROM (
  SELECT distinct enr.MemberNum,
    (ISNULL(enr.MemberFirstName, '') + ' ' + ISNULL(enr.MemberLastName, '')) AS MEMBER_NAME,
    enr.MemberBirthDate as DOB,
    enr.MemberGender as Gender,
    LAST_VALUE(enr.MemberCurrentAge) OVER (PARTITION BY MemberNum ORDER BY StaticDate ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS AGE,
    LAST_VALUE(enr.EligStateAidCategory)OVER (PARTITION BY MemberNum ORDER BY StaticDate ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS EligStateAidCategory,
    LAST_VALUE(enr.EligStateAidCategory)OVER (PARTITION BY MemberNum ORDER BY StaticDate ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS AID_CAT_ROLL_UP,
    LAST_VALUE(enr.EligFinanceAidCategoryRollup)OVER (PARTITION BY MemberNum ORDER BY StaticDate ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS EligFinanceAidCategoryRollup,
    SUM(enr.MemberMonth) OVER (PARTITION BY MemberNum) AS TOTAL_MEMBER_MONTHS
  FROM dv_Enrollment enr
  WHERE enr.StaticDate BETWEEN '01-JUN-2016' AND '30-JUN-2016'
)A

所以,我环顾四周,发现你可以使用TOP(2147483647)hack,所以我尝试将第一行更改为:

SELECT distinct TOP (2147483647) enr.MemberNum,

但我仍然遇到同样的错误。我想到的所有其他方法也需要ORDER BY(使用DENSE RANK等)。

2 个答案:

答案 0 :(得分:2)

在这两个数据库中,我会这样写:

FROM (SELECT enr.MemberNum,
             (ISNULL(enr.MemberFirstName, '') + ' ' + ISNULL(enr.MemberLastName, '')) AS MEMBER_NAME,
             enr.MemberBirthDate as DOB,
             enr.MemberGender as Gender,
             MAX(CASE WHEN seqnum = 1 THEN enr.MemberCurrentAge END) AS AGE,
             MAX(CASE WHEN seqnum = 1 THEN enr.EligStateAidCategory END) AS EligStateAidCategory,
             MAX(CASE WHEN seqnum = 1 THEN enr.EligStateAidCategory END) AS AID_CAT_ROLL_UP,
             MAX(CASE WHEN seqnum = 1 THEN enr.EligFinanceAidCategoryRollup END) AS EligFinanceAidCategoryRollup,
            SUM(enr.MemberMonth) as TOTAL_MEMBER_MONTHS
    FROM (SELECT enr.*,
                 ROW_NUMBER() OVER (PARTITION BY MemberNum ORDER BY StaticDate DESC) as seqnum
          FROM dv_Enrollment enr
         ) enr
    WHERE enr.StaticDate >= DATE '2016-06-01' AND  -- DATE not needed in SQL Server
          enr.StaticDate < DATE '2016-07-01'       -- DATE not needed in SQL Server
    GROUP BY enr.MemberNum, enr.MemberFirstName, enr.MemberLastName,
             enr.MemberBirthDate, enr.MemberGender
   ) A

为什么要改变?

  • 日期更改只是要注意日期中的时间组件。带有日期/时间的BETWEEN是一个坏习惯,因为有时它会导致代码错误并且难以调试错误。
  • 我根本不喜欢使用SELECT DISTINCT来表示GROUP BY。将它与窗口函数一起使用是很聪明的(对于LAST_VALUE())是必要的;但我认为代码最终会产生误导。
  • 我发现子查询与seqnum一起使用,以明确四个&#34;最后一个值&#34;变量都是从最后一行提取数据。
  • 此外,排序不稳定(即密钥不唯一),seqnum保证值全部来自同一行。 last_value()没有。

答案 1 :(得分:0)

将其转换为聚合子查询和cross apply(),看看会发生什么。

select 
    e.MemberNum
  , e.MemberName
  , e.DOB
  , e.Gender
  , x.MemberCurrentAge
  , x.EligStateAidCategory
  , x.EligFinanceAidCategoryRollup
  , x.MemberMonth
  , e.Total_Member_Months
from (
    select
        enr.MemberNum
      , MemberName = isnull(enr.MemberFirstName+' ', '') + isnull(enr.MemberLastName, '')
      , DOB    = enr.MemberBirthDate
      , Gender = enr.MemberGender
      /* This sounds like a weird thing to sum */
      , Total_Member_Months = sum(enr.MemberMonth) 
    from dv_Enrollment enr
    group by 
        enr.MemberNum
      , isnull(enr.MemberFirstName+' ', '') + isnull(enr.MemberLastName, '')
      , enr.MemberBirthDate
      , enr.MemberGender
    ) as e
  /* cross apply() is like an inner join
   , use outer apply() for something like a left join */
  cross apply (
    select top 1
        i.MemberCurrentAge
      , i.EligStateAidCategory
      , i.EligFinanceAidCategoryRollup
      , i.MemberMonth
      from dv_Enrollment as i
      where i.MemberNum = e.MemberNum
        and i.StaticDate >= '20160601'
        and i.StatisDate <= '20160630'
      order by i.StaticDate desc -- descending for most recent
    ) as x