2012-03-14 2 views
1

Я хотел бы написать SQL-запрос (SQL Server), который будет возвращать строки (в данном порядке), но только до заданной суммы. Мой клиент заплатил мне определенную сумму, и я хочу вернуть только те строки, которые равны < = этой сумме.SQL-хранимая процедура для добавления значений и остановки после достижения максимума

Например, если клиент заплатил мне $ 370, и данные в таблице

id amount 
1 100 
2 122 
3 134 
4 23 
5 200 

, то я хотел бы вернуться только строки 1, 2 и 3

Это должна быть эффективной , так как будет тысячи строк, поэтому цикл for не будет идеальным, я думаю. Или SQL Server достаточно эффективен для оптимизации хранимого proc с циклами?

Заранее спасибо. Джим.

+0

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

+0

Какая версия сервера SQL Server? Если не в 2012 году, у вас есть возможность перейти к нему, так как он скоро будет выпущен? –

+0

- это идентификатор, который будет использоваться для заказа? вы можете использовать функцию LAG. – Randy

ответ

3

Пара вариантов.

1) Треугольное Регистрация

SELECT * 
FROM YourTable Y1 
WHERE (SELECT SUM(amount) 
     FROM YourTable Y2 
     WHERE Y1.id >= Y2.id) <= 370 

2) Рекурсивный CTE

WITH RecursiveCTE 
AS  (
     SELECT TOP 1 id, amount, CAST(amount AS BIGINT) AS Total 
     FROM YourTable 
     ORDER BY id 
     UNION ALL 
     SELECT R.id, R.amount, R.Total 
     FROM (
       SELECT T.*, 
         T.amount + Total AS Total, 
         rn = ROW_NUMBER() OVER (ORDER BY T.id) 
       FROM YourTable T 
       JOIN RecursiveCTE R 
         ON R.id < T.id 
       ) R 
     WHERE R.rn = 1 AND Total <= 370 
     ) 
SELECT id, amount, Total 
FROM RecursiveCTE 
OPTION (MAXRECURSION 0); 

2-я один, скорее всего, работать лучше.

В SQL Server 2012 вы сможете так что-то вроде

;WITH CTE AS 
(
SELECT id, 
     amount, 
     SUM(amount) OVER(ORDER BY id 
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
      AS RunningTotal 
    FROM YourTable 
) 
    SELECT * 
    FROM CTE 
    WHERE RunningTotal <=370 

Хотя, вероятно, будет более эффективным способом (остановить сканирование, как только будет достигнуто общее)

+0

вариант 1 работает хорошо, и на этом этапе достаточно хорошо работает. спасибо кучи. –

+0

Вот мой условный SQL. Обратите внимание, что дополнительные элементы «где» в предложении sub sub select улучшают производительность, а «топ-100» гарантирует, что он не запускается слишком долго. Выбор TOP 100 PerfSearchID, идентификатор пользователя, totalPrice ОТ PerformedSearches AS Р1 WHERE (идентификатор пользователя = 210) И (totalPrice <> 0) и ((SELECT SUM (totalPrice) AS Expr1 ОТ PerformedSearches AS Р2 ГДЕ (идентификатор пользователя = 210) И (totalPrice <> 0) И (P1.PerfSearchID> = PerfSearchID)) <= 370) –

+0

Это был мой первый опыт работы с stackoverflow.com, и это замечательно! –

0

Straight -forward:

SELECT a.id, a.amount 
FROM table1 a 
INNER JOIN table1 b ON (b.id <=a.id) 
GROUP BY a.id, a.amount 
HAVING SUM(b.amount) <= 370 

К сожалению, у него есть проблема с производительностью N^2.

0

что-то вроде этого:

select id from 
(
select t1.id, t1.amount, sum(t2.amount) s 
from tst t1, tst t2 
where t2.id <= t1.id 
group by t1.id, t1.amount 
) 
where s < 370 
Смежные вопросы