2012-03-14 5 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はforループでストアドプロシージャを最適化するのに十分効率的ですか?

ありがとうございます。ジム

+0

私は、これは行ごとに、カーソルのアプローチは、セットベースのアプローチよりも高速になりますいくつかのシナリオの一つであるという疑いを持っている... –

+0

はどのバージョンのSQL Serverのですか? 2012年ではない場合は、リリースされる予定なので、移動する能力はありますか? –

+0

は注文に使用されるIDですか? 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); 

は第二1の可能性が高いパフォーマンスが向上します参加します。

あなたがそう

;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 

のようなものは、おそらく、より効率的な方法があるでしょうするけれどもことができるようになりますSQL Server 2012ので

(できるだけ早く合計に達するとスキャンを停止する)

+0

オプション1はうまく動作し、この段階で十分に機能します。ありがとう。 –

+0

ここに私のevental SQLがあります。ここで注意すべき点は、テイクサブセレクト節の「where」要素がパフォーマンスを改善し、「トップ100」がそれが長時間実行されないことを保証していることです。P1 WHERE(ユーザID = 210)としてPerformedSearches FROM SELECT TOP 100 PerfSearchID、ユーザID、totalPrice (totalPrice <> 0)AND ((SELECT SUM(totalPrice)ます。Expr1 AS PerformedSearches FROM P2 WHERE(ユーザーID AS = 210)AND(totalPrice <> 0)AND(P1.PerfSearchID> = PerfSearchID))<= 370) –

+0

これは、stackoverflow.comの私の最初の経験であり、それは素晴らしいです! –

0

ストレート前方アプローチ:

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 
関連する問題