3

私のテーブルの表現:

CREATE TABLE Sales 
    (
     id int identity primary key, 
     SaleAmount numeric(10,2)
    );

DECLARE @i INT;
SELECT @i = 1;
SET NOCOUNT ON
WHILE @i <= 100
BEGIN
    INSERT INTO Sales VALUES (ABS(CHECKSUM(NEWID()))/10000000.0 );
    SELECT @i = @i + 1;
END;
SET NOCOUNT OFF

テーブルを並べ替えて、現在の合計が X 以下のすべてのレコードを選択する必要がSalesありSaleAmountますSaleAmount

これを行うために、現在、一時テーブルを使用して最初にレコードを並べ替え、次に現在の合計が X 以下 (この例では 10) のレコードを選択しています。

CREATE TABLE #TEMP_TABLE 
    (
      ID integer IDENTITY PRIMARY KEY, 
      SaleAmount numeric(10,2)
    );

INSERT INTO #TEMP_TABLE 
(SaleAmount)
SELECT SaleAmount FROM Sales
ORDER BY SaleAmount

SELECT * FROM
  (SELECT
      Id,
      SaleAmount,
      (SaleAmount+COALESCE((SELECT SUM(SaleAmount)
          FROM #TEMP_TABLE b
          WHERE b.Id < a.Id),0))
          AS RunningTotal
    FROM #TEMP_TABLE a) InnerTable
WHERE RunningTotal <= 10

Sales一時テーブルを使用せずにテーブルを最初に並べ替える方法はありますか?

4

5 に答える 5

3

SQL Server 2012 を使用している場合は、累積合計にウィンドウ関数を使用できます。

select s.*,
       sum(SaleAmount) over (order by id) as RunningTotal
from Sales s

これは、次の相関サブクエリと同等です。

select s.*,
       (select sum(SalesAmount) from sales s2 where s2.id <= s.id) as RunningTotal
from Sales s
于 2013-03-04T14:34:49.803 に答える
1

カーソル方式を使用するという Aaron Bertrand の提案に従います。

DECLARE @st TABLE
(
    Id       Int PRIMARY KEY,
    SaleAmount  Numeric(10,2),
    RunningTotal Numeric(10,2)
);

DECLARE
    @Id         INT,
    @SaleAmount  Numeric(10,2),
    @RunningTotal Numeric(10,2) = 0;

DECLARE c CURSOR
    LOCAL STATIC FORWARD_ONLY READ_ONLY
    FOR
    SELECT id, SaleAmount
      FROM Sales
      ORDER BY SaleAmount;

OPEN c;

FETCH NEXT FROM c INTO @Id, @SaleAmount;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @RunningTotal = @RunningTotal + @SaleAmount;

    INSERT @st(Id, SaleAmount,  RunningTotal)
        SELECT @Id, @SaleAmount, @RunningTotal;

    FETCH NEXT FROM c INTO @Id, @SaleAmount;
END

CLOSE c;
DEALLOCATE c;

SELECT Id, SaleAmount, RunningTotal
    FROM @st
    WHERE RunningTotal<=10
    ORDER BY SaleAmount;

これはコードの増加であり、依然としてテーブル変数が必要です。ただし、パフォーマンスの向上は顕著です。

Aaron Bertrand 氏は、ランニング トータルに関する優れた記事を執筆しています。

于 2013-03-04T16:17:00.127 に答える
1

CTE、 ROW_NUMBER()ランキング関数、およびAPPLY()演算子を使用したもう 1 つのオプション

  ;WITH cte AS
   (
    SELECT ROW_NUMBER() OVER(ORDER BY SaleAmount) AS rn, SaleAmount
    FROM Sales s  
    )
    SELECT *
    FROM cte c CROSS APPLY (
                            SELECT SUM(s2.SaleAmount) AS RunningTotal
                            FROM Sales s2
                            WHERE c.SaleAmount >= s2.SaleAmount
                            ) o
    WHERE o.RunningTotal <= 10     

参考までに、並べ替え操作を回避するために、次のインデックスを使用できます。

CREATE INDEX ix_SaleAmount_Sales ON Sales(SaleAmount)

ここに画像の説明を入力

于 2013-03-05T22:06:31.077 に答える
0

いくつかの調査の結果、SS2012 または Oracle を使用しない限り、あなたの狙いは不可能であると私は信じています。

あなたのソリューションは機能しているように見えるので、スキーマテーブルの代わりにテーブル変数を使用することをお勧めします:

DECLARE @TEMP_TABLE TABLE (
    ID integer IDENTITY PRIMARY KEY,
    SaleAmount numeric(10,2) 
);

INSERT INTO @TEMP_TABLE 
(SaleAmount)
SELECT SaleAmount FROM Sales
ORDER BY SaleAmount

SELECT * FROM
  (SELECT
      Id,
      SaleAmount,
      (SaleAmount+COALESCE((SELECT SUM(SaleAmount)
          FROM @TEMP_TABLE b
          WHERE b.Id < a.Id),0))
          AS RunningTotal
    FROM @TEMP_TABLE a) InnerTable
WHERE RunningTotal <= 10

並べてテストすると、いくつかのパフォーマンスの向上が見られました。

于 2013-03-04T14:45:56.247 に答える
0

まず、サブセレクトを実行し、次にサブセレクトから select * を実行します。これは不要です。

SELECT
  Id,
  SaleAmount,
  (SaleAmount+COALESCE((SELECT SUM(SaleAmount)
      FROM #TEMP_TABLE b
      WHERE b.Id < a.Id),0))
      AS RunningTotal
FROM #TEMP_TABLE
WHERE RunningTotal <= 10

現在、一時テーブルは、Sales テーブルに対する単なるクエリです。一時テーブルを順序付けする目的はありません。SQL の規則により、一時テーブル内の順序を尊重する必要はなく、外側のクエリの order by 句のみを尊重する必要があるためです。

SELECT
  Id,
  SaleAmount,
  (SaleAmount+COALESCE((SELECT SUM(SaleAmount)
      FROM Sales b
      WHERE b.Id < a.Id),0))
      AS RunningTotal
FROM Sales
WHERE RunningTotal <= 10
于 2013-03-04T14:31:18.473 に答える