快速查询最新记录的方法?

发布于 2024-11-09 07:39:58 字数 522 浏览 0 评论 0原文

我有一个这样的表:

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

如果 END_DATEnull,则意味着该用户当前有该计划处于活动状态。

我想查询的是: (a) 他当前正在执行的计划,或 (b) 他最近执行的计划。我只需要为每个给定用户返回一行。

现在,我设法通过使用联合和子查询来做到这一点,但碰巧表很大并且这些效率不够高。 你们中有人有更快的方法来查询吗?

谢谢,

[编辑] 这里的大多数答案都会返回一个值。那是我的错。我的意思是为每个用户返回一个值,但同时返回所有用户。我已经调整了我能给出的答案(并纠正了问题),但只是为了将来的参考而清楚地说明了这一点。

I have a table of the sort:

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

In a way that if END_DATE is null, means that this user has that plan currently active.

What I want to query is:
(a) the current plan he has active, or (b) the lastest plan he was into. I need only one row returned for each given user.

Now, I managed to do that in using unions and sub queries, but it happens that table is massive and these are not efficient enough.
Would any of you guys have a quicker way to query that?

Thanks,

[EDIT]
Most answers here return a single value. That was my bad. What I meant was to return a single value per user but all users at once. I've adapted the answers I could (and corrected the question) but just making it clear for future reference.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

陌生 2024-11-16 07:39:58

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

另外,时间段是否相邻且不重叠?您能否获取最新 START_DATE 的时间段?

查看 END_DATE 的问题是普通的 B 树索引不会索引 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 列。

或者,

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

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

如果您可以使用架构的更多详细信息(索引、开始/结束日期的含义)来补充问题,您可能会得到更好的答案。

This question is a little hard to answer without further information about the data and the table. When you say in your comment that you have all the indexes that you need, what are these indexes?

Also, are the time periods abutting and non-overlapping? Can you just get the period with the latest START_DATE?

The problem with looking at END_DATE is that a normal B-Tree index doesn't index NULLs. So, a predicate of the form where end_date is nulll is unlikely to use the index. You could use a bitmap index with the column as those type of indexes do index nulls but that might not be ideal because of some of the other drawbacks of bitmap indexes.

For the reasons given above, I would probably use a query similar to the one below:

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

You could probably use either the row_num_1 or the row_num_2 column here depending on the exact requirements.

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

The first query should work whether you are trying get all the users back or just one. The second query will only work with one user.

If you can augment the question with more details of the schema (indexes, meaning of the start/end date) you are likely to get better answers.

錯遇了你 2024-11-16 07:39:58
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
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
锦爱 2024-11-16 07:39:58

这可能会有所帮助:

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

This may help:

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
忆梦 2024-11-16 07:39:58

您是否尝试过使用 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

Have you tried to limit the resultset with 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
眼藏柔 2024-11-16 07:39:58

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 专家。将此视为一个建议。
希望这有帮助。

AFAIK Using CASE and sub queries will cause your query to become very slow. So better to use them with care. How About:

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

I'm not a SQL guru. consider this just as a suggestion.
Hope this helps.

不甘平庸 2024-11-16 07:39:58

这有效吗?

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 输出发送给我们。

Does this work?

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

If it is slow, please send us the EXPLAIN output for the query.

断念 2024-11-16 07:39:58

这个怎么样?

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

How about this?

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
一花一树开 2024-11-16 07:39:58

我建议如下:

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

I suggest the following :

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文