SQL中join时如何限制行数上限?

Posted

技术标签:

【中文标题】SQL中join时如何限制行数上限?【英文标题】:How to restrict the upper limit of rows while doing join in SQL? 【发布时间】:2017-06-29 18:07:59 【问题描述】:

我有两张表:余额和日历。

余额:

账户日期余额 1111 2014 年 1 月 1 日 100 1111 2014 年 2 月 1 日 156 1111 03/01/2014 300 1111 04/01/2014 300 1111 07/01/2014 468 1112 2014 年 2 月 1 日 300 1112 03/01/2014 300 1112 06/01/2014 300 1112 2014 年 7 月 1 日 350 1112 08/01/2014 400 1112 09/01/2014 450 1113 2014 年 1 月 1 日 30 1113 2014 年 2 月 1 日 40 1113 2014 年 3 月 1 日 45 1113 06/01/2014 45 1113 2014 年 7 月 1 日 60 1113 2014 年 8 月 1 日 50 1113 2014 年 9 月 1 日 20 1113 10/01/2014 10

日历

日期 business_day_ind 2014 年 1 月 1 日 2014 年 2 月 1 日 2014 年 3 月 1 日 2014 年 4 月 1 日 2014 年 5 月 1 日 06/01/2014 是 2014 年 7 月 1 日 2014 年 8 月 1 日 2014 年 9 月 1 日 2014 年 10 月 1 日

我需要做以下事情:

我需要填写所有帐户的缺失天数,直到它具有价值的最大天数。假设账户 1111,它的价值只到 2014 年 7 月 1 日,所以只需要填写日期。但是当我加入日历表(普通左加入)时,我无法将最大天数限制为帐户可用的日期。 1111 01/01/2014 100 N 1111 2014 年 2 月 1 日 156 是 1111 03/01/2014 300 是 1111 04/01/2014 300 是 1111 2014 年 5 月 1 日 1111 06/01/2014 1111 07/01/2014 468 是 1111 08/01/2014 是 1111 09/01/2014 是 1111 2014 年 10 月 1 日 1112 2014 年 1 月 1 日 1112 2014 年 2 月 1 日 300 是 1112 03/01/2014 300 是 1112 2014 年 4 月 1 日 1112 2014 年 5 月 1 日 1112 06/01/2014 300 是 1112 07/01/2014 350 是 1112 08/01/2014 400 是 1112 09/01/2014 450 是 1112 2014 年 10 月 1 日

我需要一种有效的方法(最好不涉及多个步骤)将日期限制为帐户的最大可用余额日期(07/01/2014 在 1111 的情况下,09/01/2014 的情况下在 1112 的情况下)

期望的输出:

1111 01/01/2014 100 N 1111 2014 年 2 月 1 日 156 是 1111 03/01/2014 300 是 1111 04/01/2014 300 是 1111 2014 年 5 月 1 日 1111 06/01/2014 1111 07/01/2014 468 是 1112 2014 年 1 月 1 日 1112 2014 年 2 月 1 日 300 是 1112 03/01/2014 300 是 1112 2014 年 4 月 1 日 1112 2014 年 5 月 1 日 1112 06/01/2014 300 是 1112 07/01/2014 350 是 1112 08/01/2014 400 是 1112 09/01/2014 450 是

在填补缺失天数后,我打算将前一个工作日的余额归入缺失天数。我计划获取每个日期的前一个工作日,并通过以 acct 和前一个工作日为键加入原始余额表来更新丢失的行。

谢谢。

我是 Greenplum 数据库。

【问题讨论】:

你能添加想要的输出吗?有点混乱! 我已经添加了。 【参考方案1】:

一种可能的方法是在子查询中放置第二个选择。例如:

select ... from   calendar  a  left outer join balance b on a.date  = b.date 
where a.date <= (select max(date) from balance c where b.Account = c.Account )      

【讨论】:

【参考方案2】:

我想你有第三张桌子,accounts

select
  accounts.account,
  calendar.date,
  balance.balance,
  calendar.business_day_ind
from
  accounts cross join lateral (
    select * 
    from calendar
    where calendar.date <= (
      select max(date)
      from balance
      where balance.account = accounts.account)) as calendar left join
  balance on (balance.account = accounts.account and balance.date = calendar.date)
order by
  accounts.account, calendar.date;

About lateral joins

【讨论】:

【参考方案3】:

这是一个有趣的挑战!

CREATE TABLE balance 
(account int, balance_date timestamp, balance int)
DISTRIBUTED BY (account, balance_date);

INSERT INTO balance 
values (1111,'01/01/2014', 100),
(1111, '02/01/2014', 156),
(1111, '03/01/2014', 300), 
(1111, '04/01/2014', 300),
(1111, '07/01/2014', 468),
(1112, '02/01/2014', 300),
(1112, '03/01/2014', 300),
(1112, '06/01/2014', 300),
(1112, '07/01/2014', 350),
(1112, '08/01/2014', 400),
(1112, '09/01/2014', 450),
(1113, '01/01/2014', 30),
(1113, '02/01/2014', 40),
(1113, '03/01/2014', 45),
(1113, '06/01/2014', 45),
(1113, '07/01/2014', 60),
(1113, '08/01/2014', 50),
(1113, '09/01/2014', 20),
(1113, '10/01/2014', 10);

CREATE TABLE calendar
(calendar_date timestamp, business_day_ind boolean)
DISTRIBUTED BY (calendar_date);

INSERT INTO calendar
values ('01/01/2014', false),
('02/01/2014', true),
('03/01/2014', true),
('04/01/2014', false),
('05/01/2014', false),
('06/01/2014', true),
('07/01/2014', true),
('08/01/2014', true),
('09/01/2014', true),
('10/01/2014', true);

analyze balance;
analyze calendar;

现在是查询。

select d.account, d.my_date, b.balance, c.business_day_ind
from    (
    select account, start_date + interval '1 month' * (generate_series(0, duration)) AS my_date
    from    (
        select account, start_date, (date_part('year', duration) * 12 + date_part('month', duration))::int as duration
        from    (
            select start_date, age(end_date, start_date) as duration, account
            from    (
                select account, min(balance_date) as start_date, max(balance_date) as end_date
                from balance
                group by account
                ) as sub1
            ) as sub2
        ) sub3
    ) as d
left outer join balance b on d.account = b.account and d.my_date = b.balance_date
join calendar c on c.calendar_date = d.my_date
order by d.account, d.my_date;

结果:

 account |       my_date       | balance | business_day_ind 
---------+---------------------+---------+------------------
    1111 | 2014-01-01 00:00:00 |     100 | f
    1111 | 2014-02-01 00:00:00 |     156 | t
    1111 | 2014-03-01 00:00:00 |     300 | t
    1111 | 2014-04-01 00:00:00 |     300 | f
    1111 | 2014-05-01 00:00:00 |         | f
    1111 | 2014-06-01 00:00:00 |         | t
    1111 | 2014-07-01 00:00:00 |     468 | t
    1112 | 2014-02-01 00:00:00 |     300 | t
    1112 | 2014-03-01 00:00:00 |     300 | t
    1112 | 2014-04-01 00:00:00 |         | f
    1112 | 2014-05-01 00:00:00 |         | f
    1112 | 2014-06-01 00:00:00 |     300 | t
    1112 | 2014-07-01 00:00:00 |     350 | t
    1112 | 2014-08-01 00:00:00 |     400 | t
    1112 | 2014-09-01 00:00:00 |     450 | t
    1113 | 2014-01-01 00:00:00 |      30 | f
    1113 | 2014-02-01 00:00:00 |      40 | t
    1113 | 2014-03-01 00:00:00 |      45 | t
    1113 | 2014-04-01 00:00:00 |         | f
    1113 | 2014-05-01 00:00:00 |         | f
    1113 | 2014-06-01 00:00:00 |      45 | t
    1113 | 2014-07-01 00:00:00 |      60 | t
    1113 | 2014-08-01 00:00:00 |      50 | t
    1113 | 2014-09-01 00:00:00 |      20 | t
    1113 | 2014-10-01 00:00:00 |      10 | t
(25 rows)

我必须获取每个帐户的最小和最大日期,然后使用 generate_series 生成两个日期之间的月份。如果您想要每天的记录,这将是一个更清晰的查询,但我必须使用另一个子查询来获取每月级别的结果。

【讨论】:

以上是关于SQL中join时如何限制行数上限?的主要内容,如果未能解决你的问题,请参考以下文章

记一次SQL调优

如何修改XP系统的IIS的连接数限制?

MySQL的表上限

限制 SQL JOIN

如何将 LEFT JOIN 限制为 SQL Server 中的第一个结果?

使用 SQL JOIN,如何将一张表的结果限制为最近的记录