2014-01-13 3 views
0

В настоящее время я использую курсор в моей процедуре sql-сервера. Хотел узнать, есть ли способ заменить его лучшим подходом. Процесс равенМожет ли этот курсор быть заменен

  1. Клиент платит деньги, и я создаю для него запись в таблице платежей.
  2. Я начинаю курсор, выбирающие все платежи этого клиента, которые имеют доступный остаток, от ОПЛАТА ТАБЛИЦА
  3. Затем я начинаю внутренний курсор, который извлекает все счета этого клиента, которые все еще невыплаченные, от BILL ТАБЛИЦЫ
  4. я окупится каждый счет, пока текущий платеж будет исчерпан, а затем повторить процесс

Как я могу удалить курсоров для более эффективного способа шаги 2 и 3. Также делает использование курсоров означает, что ВЫПЛАТЫ и Таблицы BILL остаются заблокированными до тех пор, пока процедура не будет выполнена?

Tx

+1

Для этого вы можете определенно использовать наборный подход. Если вам нужна конкретная помощь, вам необходимо поделиться своей структурой таблиц и существующим кодом. – JNK

ответ

1

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

установка данных:

declare @bills table (billid int, balance decimal(38,4)) 
declare @payments table (paymentid int, balance decimal(38,4)) 

insert into @bills (billid, balance) values 
(1,0), (2,22.50), (3,12.75), (4,19.20) 
insert into @payments (paymentid,balance) values 
(1,20.19),(2,5.50),(3,20) 

declare @newpayments table (billid int, paymentid int, 
          paymentamount decimal(38,4)) 

я предполагал, что bills и payments таблицы есть столбец, называемый balance, который показывает любые суммы, не раскрытые до сих пор. В качестве альтернативы вам, возможно, придется рассчитать это из нескольких столбцов. Но нет выборки данных в вашем вопросе не означает, что я получаю, чтобы составить простую структуру :-)

запроса для заполнения @newpayments, с которой счета должны быть оплачены из которых (частичных) платежей :

; With unpaidbills as (
    select billid,balance, 
     ROW_NUMBER() OVER (ORDER BY billid) as rn, 
     SUM(balance) OVER (ORDER BY billid 
         ROWS BETWEEN UNBOUNDED PRECEDING 
          AND CURRENT ROW) as endbalance, 
     SUM(balance) OVER (ORDER BY billid 
         ROWS BETWEEN UNBOUNDED PRECEDING 
          AND CURRENT ROW) - balance as startbalance 
    from @bills 
    where balance > 0 
), unusedpayments as (
    select paymentid,balance, 
     ROW_NUMBER() OVER (ORDER BY paymentid) as rn, 
     SUM(balance) OVER (ORDER BY paymentid 
         ROWS BETWEEN UNBOUNDED PRECEDING 
          AND CURRENT ROW) as endbalance, 
     SUM(balance) OVER (ORDER BY paymentid 
         ROWS BETWEEN UNBOUNDED PRECEDING 
          AND CURRENT ROW) - balance as startbalance 
    from @payments 
    where balance > 0 
), overlaps as (
    select 
     billid,paymentid, 
     CASE WHEN ub.startbalance < up.startbalance 
       THEN up.startbalance ELSE ub.startbalance END as overlapstart, 
     CASE WHEN ub.endbalance > up.endbalance 
       THEN up.endbalance ELSE ub.endbalance END as overlapend 
    from 
     unpaidbills ub 
      inner join 
     unusedpayments up 
      on 
       ub.startbalance < up.endbalance and 
       up.startbalance < ub.endbalance 
) 
insert into @newpayments(billid,paymentid,paymentamount) 
select billid,paymentid,overlapend - overlapstart as paymentamount 
from overlaps 

на данный момент, @newpayments может быть использован для создания истории транзакций, и т.д.

И затем, наконец, мы обновляем исходные таблицы, чтобы отметить суммы, используемые:

;With totalpaid as (
    select billid,SUM(paymentamount) as payment from @newpayments 
    group by billid 
) 
update b 
set b.balance = b.balance - tp.payment 
from @bills b 
    inner join 
    totalpaid tp 
    on b.billid = tp.billid 

;With totalused as (
    select paymentid,SUM(paymentamount) as payment from @newpayments 
    group by paymentid 
) 
update p 
set p.balance = p.balance - tu.payment 
from @payments p 
    inner join 
    totalused tu 
    on p.paymentid = tu.paymentid 

Ключевой частью было использовать SUM() с window functions для расчета текущих сумм сумм, причитающихся (счетов) или доступных сумм (платежей), в обоих случаях с использованием столбца (billid или paymentid), чтобы определить, в каком порядке каждый из этих предметы должны быть рассмотрены. Например. unpaidbills CTE производит набор результатов, как это:

billid  balance rn     endbalance startbalance 
----------- --------- -------------------- ------------- ------------- 
2   22.5000 1     22.5000  0.0000 
3   12.7500 2     35.2500  22.5000 
4   19.2000 3     54.4500  35.2500 

и unusedpayments выглядит следующим образом:

paymentid balance rn     endbalance startbalance 
----------- ---------- -------------------- ------------ ------------- 
1   20.1900 1     20.1900  0.0000 
2   5.5000  2     25.6900  20.1900 
3   20.0000 3     45.6900  25.6900 

Затем мы создаем overlaps КТР, который находит перекрывается между счетами и платежами где (часть) платеж может быть использован для удовлетворения (части) счета. Область перекрытия - это фактическая сумма для оплаты этого счета.


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

Многие люди, пытающиеся найти совпадения, делают вещи абсурдно сложными и касаются многих особых случаев, чтобы найти все перекрытия. Обычно это можно сделать гораздо больше, просто в том, что я показываю в overlaps КТРЕ - два диапазона перекрывается, если первый диапазон начинается до второго диапазон заканчивается, и второго диапазона начинается до первого диапазона заканчивается.

Единственная сложная задача - решить, хотите ли вы иметь дело с двумя диапазонами, которые примыкают (первое значение первого в точности равно второму началу или наоборот), но это просто приводит к решению о том, использовать < или <= в сравнении.

В данном случае нам неважно, выплачивается ли платеж только с предыдущего, поэтому мы используем <, чтобы избежать таких ситуаций, как перекрытие.

+0

спасибо, отличный ответ! – user1763470

Смежные вопросы