我有一张表格:
USER | PLAN | START_DATE | END_DATE
1 | A | 20110101 | NULL
1 | B | 20100101 | 20101231
2 | A | 20100101 | 20100505
如果END_DATE
为null
,则意味着此用户的该计划目前处于有效状态。
我想要查询的是: (a)他目前的计划,或(b)他所参与的最新计划。我只需要为每个给定用户返回一行。
现在,我设法在使用联合和子查询时这样做,但是发生这种情况很严重,而且效率不高。 你们中的任何人都可以更快地查询一下吗?
谢谢,
[编辑] 这里的大多数答案都返回单个值。那是我的坏事。我的意思是每个用户返回一个值,但一次返回所有用户。我已经调整了我的答案(并纠正了问题),但只是明确表示将来参考。
答案 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_1
或row_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