Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Bisakah Solusi Rekursif ini ditulis menjadi T-SQL Query menggunakan CTE atau OVER?

Total berjalan. UPDATE tabel temp vs CTE

create table Test(
    OrderID int primary key,
    Qty int not null
);



declare @i int = 1;

while @i <= 5000 begin
    insert into Test(OrderID, Qty) values (@i * 2,rand() * 10); 
    set @i = @i + 1;
end;

Solusi rekursif membutuhkan waktu 9 detik:

with T AS
(
    select ROW_NUMBER() over(order by OrderID) as rn, * from test
)
,R(Rn, OrderId, Qty, RunningTotal) as
(
    select Rn, OrderID, Qty, Qty
    from t 
    where rn = 1

    union all

    select t.Rn, t.OrderId, t.Qty, p.RunningTotal + t.Qty
    from t t
    join r p on t.rn = p.rn + 1

)
select R.OrderId, R.Qty, R.RunningTotal from r
option(maxrecursion 0);

PERBARUI tabel membutuhkan waktu 0 detik:

create function TestRunningTotal()
returns @ReturnTable table(
    OrderId int, Qty int, RunningTotal int
)
as begin

    insert into @ReturnTable(OrderID, Qty, RunningTotal)
    select OrderID, Qty, 0 from Test
    order by OrderID;

    declare @RunningTotal int = 0;

    update @ReturnTable set 
           RunningTotal = @RunningTotal, 
           @RunningTotal = @RunningTotal + Qty;

    return;
end;

Kedua pendekatan tersebut setidaknya bisa memberi Anda kerangka kerja untuk membangun kueri Anda.

BTW di SQL Server, tidak seperti di MySQL, urutan penugasan variabel tidak masalah. Ini:

update @ReturnTable set 
    RunningTotal = @RunningTotal, 
    @RunningTotal = @RunningTotal + Qty;

Dan berikut ini:

update @ReturnTable set 
    @RunningTotal = @RunningTotal + Qty,
    RunningTotal = @RunningTotal; 

Keduanya mengeksekusi dengan cara yang sama, yaitu penugasan variabel terjadi terlebih dahulu, terlepas dari posisi penugasan variabel dalam pernyataan. Kedua kueri memiliki keluaran yang sama:

OrderId     Qty         RunningTotal
----------- ----------- ------------
2           4           4
4           8           12
6           4           16
8           5           21
10          3           24
12          8           32
14          2           34
16          9           43
18          1           44
20          2           46
22          0           46
24          2           48
26          6           54

Di tabel persis Anda, cukup deteksi Beli/Jual, Anda dapat mengalikannya dengan 1 dan -1 masing-masing, atau Anda cukup menandatangani kolom, mis. :

update @ReturnTable set 
       @RunningTotal = @RunningTotal + 
                       CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END,
       RunningTotal = @RunningTotal;            

Jika Anda mengupgrade ke SQL Server 2012, berikut adalah implementasi langsung dari running total:

select OrderID, Qty, sum(Qty) over(order by OrderID) as RunningTotal
from Test

Tentang masalah Anda yang sebenarnya:

select OrderID, Qty, 

   sum(CASE WHEN BuySell = 'Buy' THEN Qty ELSE -Qty END) 
   over(order by OrderID) as RunningTotal

from Test;

PERBARUI

Jika Anda merasa tidak nyaman dengan pembaruan unik , Anda dapat menempatkan klausa penjaga untuk memeriksa apakah urutan baris yang akan diperbarui cocok dengan urutan aslinya (dibantu oleh identitas(1,1)):

create function TestRunningTotalGuarded()
returns @ReturnTable table(
    OrderId int, Qty int, 
    RunningTotal int not null, 
    RN int identity(1,1) not null
)
as begin

    insert into @ReturnTable(OrderID, Qty, RunningTotal)
    select OrderID, Qty, 0 from Test
    order by OrderID;

    declare @RunningTotal int = 0;

    declare @RN_check INT = 0;

    update @ReturnTable set 
            @RN_check = @RN_check + 1,
            @RunningTotal = 
                (case when RN = @RN_check then @RunningTotal + Qty else 1/0 end),
            RunningTotal = @RunningTotal;

    return;

end;

Jika UPDATE benar-benar memperbarui baris dalam urutan yang tidak dapat diprediksi (atau kemungkinan akan terjadi), @RN_Check tidak akan lagi sama dengan RN(urutan identitas), kode akan memunculkan kesalahan bagi-dengan-nol kemudian. Menggunakan klausa penjaga, urutan pembaruan yang tidak terduga akan gagal cepat ; jika ini terjadi, inilah saatnya untuk mengajukan bug petisi ke Microsoft untuk membuat pembaruan unik tidak begitu unik :-)

Lindung nilai klausa penjaga pada operasi imperatif inheren (penugasan variabel) benar-benar berurutan.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jenis yang disebutkan di SQL Server 2008?

  2. Aliran Bersyarat SQL Server

  3. Menggunakan PIVOT di SQL Server 2008

  4. Indeks berkerumun dan Urutan berdasarkan Klausa

  5. Apa itu Pemblokiran SQL Server?