Выбирайте интервалы дат, делайте это быстро и всегда возвращайте последнюю запись с результатом - PullRequest
0 голосов
/ 24 апреля 2009

У меня есть база данных с таблицей, в которой хранятся изменения в балансе по нескольким счетам, с тремя столбцами;

float   balance, #The account balance after the change
Date    date,    #Date that balance change occurred
int     aid      #Account that the balance change occurred on

Он содержит пару записей для каждого дня года, и я хочу получать баланс каждые пять дней. Я также хочу, чтобы это разделить между учетными записями (то есть, если два изменения произошло в тот же день, но на отдельных счетах возвращаются оба).

Проблема в следующем : иногда могут быть несколько дней (или недель), когда нет доступных данных. Когда это происходит, я хочу убедиться, что вернула самую последнюю запись перед"дырой" в наборе данных. Это упрощенная версия проблемы, фактическая база данных большая (несколько гигабайт), размер - причина, по которой я хочу вернуть подмножество данных. Он не может использовать методы, специфичные для платформы, потому что он должен работать как на Oracle, так и на MySQL.

Мой вопрос : есть ли способ сделать это fast ? Я мог бы написать запрос, который выполнил бы работу, но я надеюсь, что есть какой-то дьявольский магический способ сделать это, который не требует большого количества вложенных запросов и агрегатных функций ..

Ответы [ 3 ]

2 голосов
/ 24 апреля 2009

Я бы использовал идею таблицы периодов Андомара, но я бы попробовал немного другой окончательный запрос. Это предполагает, что в вашей таблице Account_Balances есть PK для помощи и даты. Если бы у вас было два баланса для одной и той же учетной записи на одну и ту же дату и время, вы получите несколько повторяющихся строк.

SELECT
     P.start_date,
     P.end_date,
     AB1.account_id,
     AB1.balance
FROM
     Periods P
LEFT OUTER JOIN Account_Balances AB1 ON
     AB1.date <= P.end_date
LEFT OUTER JOIN Account_Balances AB2 ON
     AB2.aid = AB1.aid AND
     AB2.date > AB1.date AND
     AB2.date <= P.end_date
WHERE
     AB2.aid IS NULL

Если в учетной записи нет строк до или в течение заданного периода, вы не получите строку для нее.

1 голос
/ 24 апреля 2009

Вы можете сделать это относительно простым способом, создав таблицу периодов, которую вы можете объединить с таблицей счетов, чтобы создать по одной строке на учетную запись за период.

Вот пример. Давайте настроим несколько временных таблиц:

create table #balance (
    id int identity,
    balance float,
    date datetime,
    aid int
)

create table #period (
    id int identity,
    startdt datetime,
    enddt datetime
)

Введите некоторые тестовые данные:

insert into #yourtable (balance, date, aid) values (4,'2009-01-01',1)
insert into #yourtable (balance, date, aid) values (5,'2009-01-10',1)
insert into #yourtable (balance, date, aid) values (6,'2009-01-10',1)
insert into #yourtable (balance, date, aid) values (7,'2009-01-16',1)
insert into #yourtable (balance, date, aid) values (2,'2009-01-01',2)
insert into #yourtable (balance, date, aid) values (3,'2009-01-10',2)
insert into #yourtable (balance, date, aid) values (4,'2009-01-10',2)
insert into #yourtable (balance, date, aid) values (5,'2009-01-16',2)

insert into #period (startdt, enddt) values ('2009-01-01','2009-01-06')
insert into #period (startdt, enddt) values ('2009-01-06','2009-01-11')
insert into #period (startdt, enddt) values ('2009-01-11','2009-01-16')
insert into #period (startdt, enddt) values ('2009-01-16','2009-01-21')

Теперь давайте запросим все периоды:

from #period p

Добавить по одной строке для каждого баланса до конца периода:

left join #balance b1 on 
    b1.date <= p.enddt

Поиск сальдо между сальдо первого соединения и концом периода:

left join #balance b2 on 
    b2.aid = b1.aid
    and b1.id < b2.id
    and b2.date <= p.enddt

Затем отфильтруйте строки, которые не являются последними сальдо за их период.

where
    b2.aid is null

Соединение b2 в основном ищет промежуточное значение, и, говоря, что его идентификатор равен нулю, вы говорите, что промежуточной строки не существует. Окончательный запрос выглядит так:

select 
    b1.aid
,   p.startdt
,   b1.balance
from #period p
left join #balance b1 on 
    b1.date <= p.enddt
left join #balance b2 on 
    b2.aid = b1.aid
    and b1.id < b2.id
    and b2.date <= p.enddt
where
    b2.aid is null
order by b1.aid, p.startdt

Примечание: запросы предполагают, что баланс с более поздней датой всегда имеет больший идентификатор. Если вам никогда не нужно балансировать с точно такой же датой окончания, вы можете заменить «b1.id

0 голосов
/ 24 апреля 2009

Если вы ждете postgresql 8.4, вы можете использовать функции окна

http://www.postgresql.org/docs/8.4/static/tutorial-window.html

http://www.postgresql.org/docs/8.4/static/functions-window.html

...