查询最新记录的快捷方式?

时间:2011-05-23 10:25:56

标签: sql performance oracle

我有一张表格:

USER |  PLAN |  START_DATE  |   END_DATE
1    |  A    |  20110101    |   NULL
1    |  B    |  20100101    |   20101231
2    |  A    |  20100101    |   20100505

如果END_DATEnull,则意味着此用户的该计划目前处于有效状态。

我想要查询的是: (a)他目前的计划,或(b)他所参与的最新计划。我只需要为每个给定用户返回一行。

现在,我设法在使用联合和子查询时这样做,但是发生这种情况很严重,而且效率不高。 你们中的任何人都可以更快地查询一下吗?

谢谢,

[编辑] 这里的大多数答案都返回单个值。那是我的坏事。我的意思是每个用户返回一个值,但一次返回所有用户。我已经调整了我的答案(并纠正了问题),但只是明确表示将来参考。

8 个答案:

答案 0 :(得分:3)

如果没有关于数据和表格的进一步信息,这个问题有点难以回答。当您在评论中说您拥有所需的所有索引时,这些索引是什么?

此外,时间段是否相邻且不重叠?你能获得最新的START_DATE期间吗?

查看END_DATE的问题是普通的B-Tree索引不会对NULL进行索引。因此,where end_date is nulll形式的谓词不太可能使用索引。您可以对列使用位图索引,因为这些索引类型会索引空值,但由于位图索引的其他一些缺点,这可能不太理想。

由于上面给出的原因,我可能会使用类似下面的查询:

select user, plan, start_date, end_date
from (
  select 
    user, 
    plan, 
    start_date, 
    end_date, 
    row_number() over (partition by user order start_date desc) as row_num_1,
    row_number() over (partition by user order end_date desc nulls first) as row_num_2
  from user_table
  where user = :userid
)
where row_num_1 = 1

您可以在此处使用row_num_1row_num_2列,具体取决于具体要求。

OR

select user, plan, start_date, end_date
from (
  select 
    user, 
    plan, 
    start_date, 
    end_date, 
  from user_table
  where user = :userid
  order by start_date desc
)
where rownum = 1

无论您是尝试获取所有用户还是仅尝试一个用户,第一个查询都应该有效。第二个查询仅适用于一个用户。

如果您可以使用模式的更多细节(索引,开始/结束日期的含义)来扩充问题,那么您可能会获得更好的答案。

答案 1 :(得分:2)

CREATE TABLE XY
( USERID      INTEGER                 NOT NULL
, PLAN        VARCHAR2(8)             NOT NULL
, START_DATE  DATE                    NOT NULL
, END_DATE    DATE                    )
  TABLESPACE USERS;


INSERT INTO XY ( USERID, PLAN, START_DATE, END_DATE )
       VALUES ( 1, 'A', To_Date('22-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS'), To_Date('22-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS') );
INSERT INTO XY ( USERID, PLAN, START_DATE, END_DATE )
       VALUES ( 1, 'B', To_Date('01-04-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS'), NULL );
INSERT INTO XY ( USERID, PLAN, START_DATE, END_DATE )
       VALUES ( 2, 'A', To_Date('03-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS'), To_Date('04-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS') );
INSERT INTO XY ( USERID, PLAN, START_DATE, END_DATE )
       VALUES ( 2, 'B', To_Date('15-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS'), To_Date('20-05-2011 00:00:00', 'DD-MM-YYYY HH24:MI:SS') );
COMMIT WORK;

SELECT USERID, PLAN, END_DATE, START_DATE
  FROM (SELECT USERID,
               PLAN,
               END_DATE,
               START_DATE,
               ROW_NUMBER() OVER(PARTITION BY USERID ORDER BY END_DATE DESC) SEQUEN
          FROM XY)
 WHERE SEQUEN < 2

答案 2 :(得分:1)

这可能有所帮助:

SELECT user,plan,end_date,start_date 
FROM ( SELECT users,plans,end_date,start_date, DENSE_RANK() OVER ( PARTITION BY user 
                                                                   ORDER BY end_date DESC) sequen 
        FROM table_name 
     ) 
WHERE sequen <= 2

答案 3 :(得分:0)

您是否尝试使用rownum限制结果集?

select  plan
from    (
        select  plan
        from    YourTable
        where   User = 1
        order by
                case when end_date is null then '99991231' else end_date end desc
        )
where   rownum < 2

答案 4 :(得分:0)

AFAIK使用CASE和子查询会导致查询变得非常慢。所以最好小心使用它们。怎么样:

SELECT User, Plan, start_Date, MAX(End_Date) FROM Plans WHERE User NOT IN 
(SELECT User FROM Plans WHERE End_Date IS NULL)
GROUP BY Start_Date, Plan, User  
UNION  
SELECT User,Plan,Start_Date FROM Plans WHERE End_Date IS NULL

我不是SQL大师。认为这只是一个建议 希望这会有所帮助。

答案 5 :(得分:0)

这有用吗?

SELECT U.user 
,(SELECT Plan FROM t WHERE t.user=u.user AND end_date IS NULL LIMIT 1) AS Current_Plan
,(SELECT Plan FROM t WHERE t.user=u.user AND end_date IS NOT NULL ORDER BY end_date DESC LIMIT 1) AS Last_Plan
FROM 
( SELECT DISTINCT USER FROM t ) AS U

如果速度很慢,请将查询的EXPLAIN输出发送给我们。

答案 6 :(得分:0)

这个怎么样?

select PLAN
from USER_TABLE
where END_DATE is null or END_DATE = (
        select max(END_DATE)
        from USER_TABLE
        where USER = 1 and END_DATE is not null)
    and USER = 1

答案 7 :(得分:0)

我建议如下:

with t as 
(select 1 as col_id, 1 as USER_id, 'A' as PLAN , 20110101 as START_DATE, NULL as  END_DATE from dual union all
 select 2,1,'B', 20100101,20101231 from dual union all
 select 3,2,'A', 20100102,20100505 from dual union all
 select 4,2,'C', 20100101,20100102 from dual)
--
SELECT user_id, plan
  FROM (SELECT user_id,
               plan,
               MAX(nvl(END_DATE, 99999999)) over(PARTITION BY user_id) max_date,
               nvl(END_DATE, 99999999) END_DATE
          FROM t)
 WHERE max_date = end_date