คำนวณผลรวมสะสมใน SQL Server


170

ลองนึกภาพตารางต่อไปนี้ (เรียกว่าTestTable):

id     somedate    somevalue
--     --------    ---------
45     01/Jan/09   3
23     08/Jan/09   5
12     02/Feb/09   0
77     14/Feb/09   7
39     20/Feb/09   34
33     02/Mar/09   6

ฉันต้องการข้อความค้นหาที่ส่งคืนผลรวมการเรียงลำดับตามวันที่ที่ต้องการเช่น:

id     somedate    somevalue  runningtotal
--     --------    ---------  ------------
45     01/Jan/09   3          3
23     08/Jan/09   5          8
12     02/Feb/09   0          8
77     14/Feb/09   7          15  
39     20/Feb/09   34         49
33     02/Mar/09   6          55

ฉันรู้ว่ามีหลายวิธีในการทำเช่นนี้ใน SQL Server 2000/2005/2008

ฉันสนใจวิธีการเรียงลำดับนี้ที่ใช้เคล็ดลับการรวมชุดคำสั่ง:

INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal) 
   SELECT id, somedate, somevalue, null
   FROM TestTable
   ORDER BY somedate

DECLARE @RunningTotal int
SET @RunningTotal = 0

UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl

... นี้มีประสิทธิภาพมาก แต่ฉันได้ยินมาว่ามีปัญหาอยู่รอบตัวเพราะคุณไม่สามารถรับประกันได้ว่าUPDATEคำสั่งจะประมวลผลแถวตามลำดับที่ถูกต้อง บางทีเราอาจได้รับคำตอบที่ชัดเจนเกี่ยวกับปัญหานั้น

แต่อาจมีวิธีอื่นที่ผู้คนสามารถแนะนำได้?

แก้ไข: ขณะนี้มีSqlFiddleพร้อมการตั้งค่าและตัวอย่าง 'update trick' ด้านบน


blogs.msdn.com/sqltips/archive/2005/07/20/441053.aspx เพิ่มคำสั่งซื้อในชุดอัปเดตของคุณ ... และคุณจะได้รับการรับประกัน
Simon D

แต่คำสั่งซื้อโดยไม่สามารถใช้กับคำสั่ง UPDATE ... ทำได้หรือไม่
codeulike

โปรดดูsqlperformance.com/2012/07/t-sql-queries/running-totalsโดยเฉพาะอย่างยิ่งหากคุณใช้ SQL Server 2012
Aaron Bertrand

คำตอบ:


133

อัปเดตหากคุณใช้ SQL Server 2012 โปรดดู: https://stackoverflow.com/a/10309947

ปัญหาคือว่าการดำเนินการของ SQL Server ของประโยคกว่าจะค่อนข้าง จำกัด

Oracle (และ ANSI-SQL) ช่วยให้คุณทำสิ่งต่าง ๆ เช่น:

 SELECT somedate, somevalue,
  SUM(somevalue) OVER(ORDER BY somedate 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
          AS RunningTotal
  FROM Table

SQL Server ไม่ได้ช่วยแก้ปัญหาให้คุณ ลำไส้ของฉันบอกฉันว่านี่เป็นหนึ่งในกรณีที่หายากที่เคอร์เซอร์เร็วที่สุดแม้ว่าฉันจะต้องทำการเปรียบเทียบกับผลลัพธ์ที่ยิ่งใหญ่

เคล็ดลับการอัปเดตมีประโยชน์ แต่ฉันรู้สึกว่ามันค่อนข้างบอบบาง ดูเหมือนว่าหากคุณกำลังอัปเดตตารางเต็มจะดำเนินการต่อไปตามลำดับของคีย์หลัก ดังนั้นหากคุณตั้งวันที่เป็นคีย์หลักจากน้อยไปมากคุณจะprobablyปลอดภัย แต่คุณต้องพึ่งพารายละเอียดการใช้งาน SQL Server ที่ไม่มีเอกสาร (เช่นถ้าแบบสอบถามสิ้นสุดลงด้วยการดำเนินการโดยสอง procs ฉันสงสัยว่าจะเกิดอะไรขึ้นดู: MAXDOP):

ตัวอย่างการทำงานเต็ม:

drop table #t 
create table #t ( ord int primary key, total int, running_total int)

insert #t(ord,total)  values (2,20)
-- notice the malicious re-ordering 
insert #t(ord,total) values (1,10)
insert #t(ord,total)  values (3,10)
insert #t(ord,total)  values (4,1)

declare @total int 
set @total = 0
update #t set running_total = @total, @total = @total + total 

select * from #t
order by ord 

ord         total       running_total
----------- ----------- -------------
1           10          10
2           20          30
3           10          40
4           1           41

คุณขอมาตรฐานนี่คือการลดลง

วิธีที่ปลอดภัยที่สุดในการทำสิ่งนี้คือเคอร์เซอร์มันเป็นลำดับความสำคัญเร็วกว่าแบบสอบถามย่อยที่สัมพันธ์กันของการเข้าร่วมข้าม

วิธีที่เร็วที่สุดที่แน่นอนคือเคล็ดลับการอัพเดท ข้อกังวลเดียวของฉันคือว่าฉันไม่แน่ใจว่าภายใต้สถานการณ์ทั้งหมดการปรับปรุงจะดำเนินการในลักษณะเชิงเส้น ไม่มีสิ่งใดในแบบสอบถามที่บอกอย่างชัดเจน

บรรทัดล่างสำหรับรหัสการผลิตฉันจะใช้เคอร์เซอร์

ข้อมูลการทดสอบ:

create table #t ( ord int primary key, total int, running_total int)

set nocount on 
declare @i int
set @i = 0 
begin tran
while @i < 10000
begin
   insert #t (ord, total) values (@i,  rand() * 100) 
    set @i = @i +1
end
commit

ทดสอบ 1:

SELECT ord,total, 
    (SELECT SUM(total) 
        FROM #t b 
        WHERE b.ord <= a.ord) AS b 
FROM #t a

-- CPU 11731, Reads 154934, Duration 11135 

ทดสอบ 2:

SELECT a.ord, a.total, SUM(b.total) AS RunningTotal 
FROM #t a CROSS JOIN #t b 
WHERE (b.ord <= a.ord) 
GROUP BY a.ord,a.total 
ORDER BY a.ord

-- CPU 16053, Reads 154935, Duration 4647

ทดสอบ 3:

DECLARE @TotalTable table(ord int primary key, total int, running_total int)

DECLARE forward_cursor CURSOR FAST_FORWARD 
FOR 
SELECT ord, total
FROM #t 
ORDER BY ord


OPEN forward_cursor 

DECLARE @running_total int, 
    @ord int, 
    @total int
SET @running_total = 0

FETCH NEXT FROM forward_cursor INTO @ord, @total 
WHILE (@@FETCH_STATUS = 0)
BEGIN
     SET @running_total = @running_total + @total
     INSERT @TotalTable VALUES(@ord, @total, @running_total)
     FETCH NEXT FROM forward_cursor INTO @ord, @total 
END

CLOSE forward_cursor
DEALLOCATE forward_cursor

SELECT * FROM @TotalTable

-- CPU 359, Reads 30392, Duration 496

ทดสอบ 4:

declare @total int 
set @total = 0
update #t set running_total = @total, @total = @total + total 

select * from #t

-- CPU 0, Reads 58, Duration 139

1
ขอบคุณ ดังนั้นตัวอย่างรหัสของคุณคือการแสดงให้เห็นว่ามันจะรวมกันตามลำดับของคีย์หลักฉันเข้าใจ เป็นที่น่าสนใจที่จะทราบว่าเคอร์เซอร์ยังคงมีประสิทธิภาพมากกว่าการรวมสำหรับชุดข้อมูลขนาดใหญ่
codeulike

1
ฉันเพิ่งทดสอบ CTE @Martin ไม่มีอะไรใกล้เคียงกับเคล็ดลับการอัพเดท - เคอร์เซอร์ดูเหมือนจะต่ำกว่าเมื่ออ่าน นี่คือ profiler trace i.stack.imgur.com/BbZq3.png
Sam Saffron

3
@Martin Denali กำลังจะมีทางออกที่ดีงามสำหรับmsdn.microsoft.com/en-us/library/ms189461(v=SQL.110).aspx
Sam Saffron

1
+1 สำหรับงานทั้งหมดที่ใส่ไว้ในคำตอบนี้ - ฉันชอบตัวเลือกอัปเดต สามารถสร้างพาร์ติชันไว้ในสคริปต์ UPDATE นี้ได้หรือไม่ เช่นถ้ามีฟิลด์เพิ่มเติม "สีรถ" สคริปต์นี้สามารถส่งคืนผลรวมการทำงานภายในแต่ละพาร์ติชัน "สีรถ" ได้หรือไม่
ทำไม

2
คำตอบเริ่มต้น (Oracle (และ ANSI-SQL)) ตอนนี้สามารถใช้งานได้ใน SQL Server 2017 ขอบคุณมากหรูหรามาก!
DaniDev


40

ในขณะที่แซมซัฟฟรอนทำงานได้อย่างยอดเยี่ยมเขาก็ยังไม่ได้ให้รหัสนิพจน์ตารางแบบเรียกซ้ำสำหรับปัญหานี้ และสำหรับพวกเราที่ทำงานกับ SQL Server 2008 R2 และไม่ใช่ Denali มันยังคงเป็นวิธีที่เร็วที่สุดในการทำงานทั้งหมดมันเร็วกว่าเคอร์เซอร์บนคอมพิวเตอร์ที่ทำงานของฉันประมาณ 10 เท่าของแถว 100000 แถวและยังเป็นคิวรีแบบอินไลน์
ดังนั้นที่นี่เป็น (ฉันสมมติว่ามีordคอลัมน์ในตารางและเป็นหมายเลขต่อเนื่องโดยไม่มีช่องว่างสำหรับการประมวลผลที่รวดเร็วก็ควรมีข้อ จำกัด ที่ไม่ซ้ำกันในหมายเลขนี้):

;with 
CTE_RunningTotal
as
(
    select T.ord, T.total, T.total as running_total
    from #t as T
    where T.ord = 0
    union all
    select T.ord, T.total, T.total + C.running_total as running_total
    from CTE_RunningTotal as C
        inner join #t as T on T.ord = C.ord + 1
)
select C.ord, C.total, C.running_total
from CTE_RunningTotal as C
option (maxrecursion 0)

-- CPU 140, Reads 110014, Duration 132

sql fiddle demo

อัปเดต ฉันยังอยากรู้อยากเห็นเกี่ยวกับเรื่องนี้ปรับปรุงกับตัวแปรหรือปรับปรุงที่เล่นโวหาร ดังนั้นโดยปกติแล้วจะใช้ได้ดี แต่เราจะมั่นใจได้อย่างไรว่ามันใช้ได้ทุกครั้ง นี่เป็นเคล็ดลับเล็กน้อย (พบได้ที่นี่ - http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ) - คุณเพียงตรวจสอบปัจจุบันและก่อนหน้าordและใช้1/0งานในกรณีที่พวกเขาแตกต่างจากสิ่งที่ คุณคาดหวัง:

declare @total int, @ord int

select @total = 0, @ord = -1

update #t set
    @total = @total + total,
    @ord = case when ord <> @ord + 1 then 1/0 else ord end,
    ------------------------
    running_total = @total

select * from #t

-- CPU 0, Reads 58, Duration 139

จากสิ่งที่ฉันเห็นถ้าคุณมีดัชนีที่เหมาะสมกลุ่ม / คีย์หลักในตารางของคุณ (ในกรณีของเรามันจะเป็นดัชนีโดยord_id) การปรับปรุงจะดำเนินการในลักษณะเชิงเส้นตลอดเวลา (ไม่เคยพบหารด้วยศูนย์) ที่กล่าวว่ามันขึ้นอยู่กับคุณที่จะตัดสินใจว่าคุณต้องการที่จะใช้มันในรหัสการผลิต :)

ปรับปรุง 2ฉันเชื่อมโยงคำตอบนี้ทำให้มันมีข้อมูลที่เป็นประโยชน์บางอย่างเกี่ยวกับความเชื่อถือของการปรับปรุงที่เล่นโวหาร - nvarchar concatenation / index / nvarchar (สูงสุด) พฤติกรรมลึกลับ


6
คำตอบนี้สมควรได้รับการยอมรับมากขึ้น (หรืออาจมีข้อบกพร่องที่ฉันไม่เห็น?)
user1068352

ควรมีเลขลำดับเพื่อให้คุณสามารถเข้าร่วมใน ord = ord + 1 และบางครั้งก็ต้องการงานเพิ่มอีกเล็กน้อย แต่อย่างไรก็ตามใน SQL 2008 R2 ฉันใช้โซลูชันนี้
Roman Pekar

+1 บน SQLServer2008R2 ฉันชอบแนวทางแบบเรียกซ้ำ CTE FYI เพื่อหาค่าสำหรับตารางซึ่งอนุญาตให้มีช่องว่างฉันใช้คิวรีย่อยที่สัมพันธ์กัน มันเพิ่มการดำเนินการค้นหาเพิ่มเติมสองรายการลงในแบบสอบถามsqlfiddle.com/#!3/d41d8/18967
Aleksandr Fedorenko

2
สำหรับกรณีที่คุณมีลำดับสำหรับข้อมูลของคุณอยู่แล้วและคุณกำลังมองหาโซลูชันที่อิงตามชุดที่กระชับ (ไม่ใช่เคอร์เซอร์) บน SQL 2008 R2 สิ่งนี้ดูเหมือนจะสมบูรณ์แบบ
Nick.McDermaid

1
ไม่ใช่ทุกแบบสอบถามที่ทำงานอยู่ทั้งหมดจะมีเขตลำดับที่ต่อเนื่องกัน บางครั้งเขตข้อมูลวันที่และเวลาคือสิ่งที่คุณมีหรือบันทึกถูกลบออกจากการเรียงลำดับ นั่นอาจเป็นสาเหตุว่าทำไมมันไม่ได้ถูกใช้งานบ่อยขึ้น
รูเบน

28

ตัวดำเนินการที่ใช้ใน SQL 2005 และสูงกว่าสามารถใช้ได้กับสิ่งนี้:

select
    t.id ,
    t.somedate ,
    t.somevalue ,
    rt.runningTotal
from TestTable t
 cross apply (select sum(somevalue) as runningTotal
                from TestTable
                where somedate <= t.somedate
            ) as rt
order by t.somedate

5
ทำงานได้ดีมากสำหรับชุดข้อมูลขนาดเล็ก ข้อเสียคือคุณจะต้องมีที่ตรงกับข้อในแบบสอบถามภายในและภายนอก
ฝ่าบาท

เนื่องจากวันที่ของฉันบางวันเหมือนกันหมด (ลงไปที่เสี้ยววินาที) ฉันต้องเพิ่ม: row_number () เหนือ (เรียงลำดับตาม txndate) ไปที่ตารางด้านในและด้านนอกและดัชนีผสมไม่กี่ตัวเพื่อให้มันทำงานได้ วิธีการแก้ปัญหาลื่น / ง่าย BTW การทดสอบ cross ใช้กับเคียวรี่ย่อย ... มันเร็วกว่าเล็กน้อย
pghcpa

นี่สะอาดมากและทำงานได้ดีกับชุดข้อมูลขนาดเล็ก เร็วกว่า CTE แบบเรียกซ้ำ
jtate

นี่เป็นทางออกที่ดีเช่นกัน (สำหรับชุดข้อมูลขนาดเล็ก) แต่คุณต้องระวังด้วยว่าคอลัมน์ Somedate นั้นมีความเป็นเอกลักษณ์
Roman Pekar

11
SELECT TOP 25   amount, 
    (SELECT SUM(amount) 
    FROM time_detail b 
    WHERE b.time_detail_id <= a.time_detail_id) AS Total FROM time_detail a

คุณยังสามารถใช้ฟังก์ชัน ROW_NUMBER () และตาราง temp เพื่อสร้างคอลัมน์โดยพลการเพื่อใช้ในการเปรียบเทียบกับคำสั่ง SELECT ภายใน


1
นี้จะไม่มีประสิทธิภาพจริงๆ ... แต่แล้วอีกครั้งไม่มีวิธีทำความสะอาดที่แท้จริงของการทำเช่นนี้ในเซิร์ฟเวอร์ SQL
แซม Saffron

แน่นอนมันไม่มีประสิทธิภาพ - แต่ทำงานและไม่มีคำถามว่ามีบางสิ่งสำหรับดำเนินการในลำดับที่ถูกหรือผิด
Sam Axe

ขอบคุณมันมีประโยชน์ที่จะมีคำตอบทางเลือกและยังมีประโยชน์ที่จะมี efficienty วิจารณ์
codeulike

7

ใช้แบบสอบถามย่อยที่สัมพันธ์กัน ง่ายมากคุณไปที่นี่:

SELECT 
somedate, 
(SELECT SUM(somevalue) FROM TestTable t2 WHERE t2.somedate<=t1.somedate) AS running_total
FROM TestTable t1
GROUP BY somedate
ORDER BY somedate

รหัสอาจไม่ถูกต้อง แต่ฉันแน่ใจว่าเป็นความคิด

GROUP BY เป็นกรณีที่วันที่ปรากฏมากกว่าหนึ่งครั้งคุณต้องการเห็นเพียงครั้งเดียวในชุดผลลัพธ์

หากคุณไม่ต้องการเห็นวันที่ทำซ้ำหรือคุณต้องการเห็นคุณค่าและรหัสดั้งเดิมแสดงว่าคุณต้องการสิ่งต่อไปนี้

SELECT 
id,
somedate, 
somevalue,
(SELECT SUM(somevalue) FROM TestTable t2 WHERE t2.somedate<=t1.somedate) AS running_total
FROM TestTable t1
ORDER BY somedate

ขอบคุณ ... ง่ายมาก มีดัชนีที่จะเพิ่มประสิทธิภาพ แต่นั่นก็ง่ายพอ (รับหนึ่งในคำแนะนำจาก Database Engine Tuning Advisor;) แล้วมันก็วิ่งเหมือนช็อต
Doug_Ivison

5

นอกจากนี้คุณยังสามารถ denormalize - เก็บผลรวมสะสมในตารางเดียวกัน:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx

เลือกทำงานได้เร็วกว่าโซลูชันอื่น ๆ แต่การแก้ไขอาจช้าลง


4

สมมติว่า windowing ทำงานบน SQL Server 2008 เหมือนที่อื่น ๆ (ที่ฉันได้ลอง) ไปเลย:

select testtable.*, sum(somevalue) over(order by somedate)
from testtable
order by somedate;

MSDNบอกว่ามันมีอยู่ใน SQL Server 2008 (และอาจจะ 2005 ด้วย) แต่ฉันไม่มีอินสแตนซ์ให้ลอง

แก้ไข: เห็นได้ชัดว่า SQL Server ไม่อนุญาตสเปคของหน้าต่าง ("OVER (... )") โดยไม่ได้ระบุ "PARTITION BY" (แบ่งผลลัพธ์ออกเป็นกลุ่ม แต่ไม่รวมกันในลักษณะที่ GROUP BY ทำ) น่ารำคาญ - การอ้างอิงไวยากรณ์ของ MSDN แสดงให้เห็นว่าเป็นทางเลือก แต่ฉันมีเพียงอินสแตนซ์ของ SqlServer 2000 เท่านั้นในขณะนี้

แบบสอบถามที่ฉันให้ทำงานทั้งใน Oracle 10.2.0.3.0 และ PostgreSQL 8.4-beta ดังนั้นบอก MS ให้ทัน;)


2
การใช้ OVER กับ SUM จะไม่ทำงานในกรณีนี้เพื่อให้ผลรวมสะสม ข้อ OVER ไม่ยอมรับ ORDER BY เมื่อใช้กับ SUM คุณต้องใช้ PARTITION BY ซึ่งจะไม่สามารถทำงานได้ทั้งหมด
Sam Axe

ขอบคุณจริงๆมันมีประโยชน์ที่จะได้ยินว่าทำไมมันถึงไม่ทำงาน araqnid คุณอาจแก้ไขคำตอบของคุณเพื่ออธิบายว่าทำไมมันถึงไม่ใช่ตัวเลือก
codeulike


ใช้งานได้จริงสำหรับฉันเพราะฉันต้องแบ่งพาร์ติชัน - แม้ว่านี่ไม่ใช่คำตอบที่ได้รับความนิยมมากที่สุด แต่ก็เป็นทางออกที่ง่ายที่สุดสำหรับปัญหา RT ใน SQL ของฉัน
William MB

ฉันไม่มี MSSQL 2008 กับฉัน แต่ฉันคิดว่าคุณอาจจะแบ่งพาร์ติชันด้วย (เลือก null) และแฮ็คปัญหาการแบ่งพาร์ติชัน หรือทำการเลือกย่อยด้วย1 partitionmeและแบ่งพาร์ติชันตามนั้น นอกจากนี้อาจจำเป็นต้องมีการแบ่งพาร์ติชันในสถานการณ์จริงเมื่อทำรายงาน
nurettin

4

หากคุณใช้ SQL Server 2008 R2 ข้างต้น จากนั้นมันจะเป็นวิธีที่สั้นที่สุดที่จะทำ;

Select id
    ,somedate
    ,somevalue,
LAG(runningtotal) OVER (ORDER BY somedate) + somevalue AS runningtotal
From TestTable 

LAGใช้เพื่อรับค่าแถวก่อนหน้า คุณสามารถทำ google สำหรับข้อมูลเพิ่มเติม

[1]:


1
ฉันเชื่อว่าLAGมีอยู่เฉพาะใน SQL Server 2012 ขึ้นไป (ไม่ใช่ 2008)
AaA

1
การใช้ LAG () ไม่ได้ปรับปรุงSUM(somevalue) OVER(...) ซึ่งดูเหมือนดีขึ้นสำหรับฉัน
Used_By_Already

2

ฉันเชื่อว่าสามารถทำได้โดยใช้การดำเนินการ INNER JOIN ด้านล่าง

SELECT
     ROW_NUMBER() OVER (ORDER BY SomeDate) AS OrderID
    ,rt.*
INTO
    #tmp
FROM
    (
        SELECT 45 AS ID, CAST('01-01-2009' AS DATETIME) AS SomeDate, 3 AS SomeValue
        UNION ALL
        SELECT 23, CAST('01-08-2009' AS DATETIME), 5
        UNION ALL
        SELECT 12, CAST('02-02-2009' AS DATETIME), 0
        UNION ALL
        SELECT 77, CAST('02-14-2009' AS DATETIME), 7
        UNION ALL
        SELECT 39, CAST('02-20-2009' AS DATETIME), 34
        UNION ALL
        SELECT 33, CAST('03-02-2009' AS DATETIME), 6
    ) rt

SELECT
     t1.ID
    ,t1.SomeDate
    ,t1.SomeValue
    ,SUM(t2.SomeValue) AS RunningTotal
FROM
    #tmp t1
    JOIN #tmp t2
        ON t2.OrderID <= t1.OrderID
GROUP BY
     t1.OrderID
    ,t1.ID
    ,t1.SomeDate
    ,t1.SomeValue
ORDER BY
    t1.OrderID

DROP TABLE #tmp

ใช่ฉันคิดว่านี่เทียบเท่ากับ 'ทดสอบ 3' ในคำตอบของ Sam Saffron
codeulike

2

ต่อไปนี้จะให้ผลลัพธ์ที่ต้องการ

SELECT a.SomeDate,
       a.SomeValue,
       SUM(b.SomeValue) AS RunningTotal
FROM TestTable a
CROSS JOIN TestTable b
WHERE (b.SomeDate <= a.SomeDate) 
GROUP BY a.SomeDate,a.SomeValue
ORDER BY a.SomeDate,a.SomeValue

การมีดัชนีคลัสเตอร์ใน SomeDate จะช่วยปรับปรุงประสิทธิภาพได้อย่างมาก


@Dave ผมคิดว่าคำถามนี้พยายามที่จะหาวิธีที่มีประสิทธิภาพของการทำเช่นนี้ข้ามมาร่วมงานเป็นไปได้ช้าจริงๆสำหรับชุดใหญ่
แซม Saffron

ขอบคุณมันมีประโยชน์ที่จะมีคำตอบทางเลือกและยังมีประโยชน์ที่จะมี efficienty วิจารณ์
codeulike

2

การใช้การเข้าร่วมรูปแบบอื่นคือการใช้การเข้าร่วม ตอนนี้แบบสอบถามอาจมีลักษณะดังนี้:

    SELECT a.id, a.value, SUM(b.Value)FROM   RunTotalTestData a,
    RunTotalTestData b
    WHERE b.id <= a.id
    GROUP BY a.id, a.value 
    ORDER BY a.id;

สำหรับข้อมูลเพิ่มเติมคุณสามารถเยี่ยมชมลิงค์นี้ http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12


2

แม้ว่าวิธีที่ดีที่สุดคือการได้รับมันทำจะใช้ฟังก์ชั่นหน้าต่างก็ยังสามารถทำได้โดยใช้ที่เรียบง่ายมีความสัมพันธ์แบบสอบถามย่อย

Select id, someday, somevalue, (select sum(somevalue) 
                                from testtable as t2
                                where t2.id = t1.id
                                and t2.someday <= t1.someday) as runningtotal
from testtable as t1
order by id,someday;

0
BEGIN TRAN
CREATE TABLE #Table (_Id INT IDENTITY(1,1) ,id INT ,    somedate VARCHAR(100) , somevalue INT)


INSERT INTO #Table ( id  ,    somedate  , somevalue  )
SELECT 45 , '01/Jan/09', 3 UNION ALL
SELECT 23 , '08/Jan/09', 5 UNION ALL
SELECT 12 , '02/Feb/09', 0 UNION ALL
SELECT 77 , '14/Feb/09', 7 UNION ALL
SELECT 39 , '20/Feb/09', 34 UNION ALL
SELECT 33 , '02/Mar/09', 6 

;WITH CTE ( _Id, id  ,  _somedate  , _somevalue ,_totvalue ) AS
(

 SELECT _Id , id  ,    somedate  , somevalue ,somevalue
 FROM #Table WHERE _id = 1
 UNION ALL
 SELECT #Table._Id , #Table.id  , somedate  , somevalue , somevalue + _totvalue
 FROM #Table,CTE 
 WHERE #Table._id > 1 AND CTE._Id = ( #Table._id-1 )
)

SELECT * FROM CTE

ROLLBACK TRAN

คุณอาจให้ข้อมูลเกี่ยวกับสิ่งที่คุณทำที่นี่และจดบันทึกข้อดี / ข้อเสียของวิธีการนี้โดยเฉพาะ
TT

0

ต่อไปนี้เป็นวิธีง่ายๆ 2 วิธีในการคำนวณผลรวมการทำงาน:

วิธีที่ 1 : สามารถเขียนได้ด้วยวิธีนี้ถ้า DBMS ของคุณรองรับฟังก์ชั่นการวิเคราะห์

SELECT     id
           ,somedate
           ,somevalue
           ,runningtotal = SUM(somevalue) OVER (ORDER BY somedate ASC)
FROM       TestTable

วิธีที่ 2 : คุณสามารถใช้ประโยชน์จาก OUTER APPLY หากเวอร์ชันฐานข้อมูล / DBMS ของคุณไม่สนับสนุนฟังก์ชันวิเคราะห์

SELECT     T.id
           ,T.somedate
           ,T.somevalue
           ,runningtotal = OA.runningtotal
FROM       TestTable T
           OUTER APPLY (
                           SELECT   runningtotal = SUM(TI.somevalue)
                           FROM     TestTable TI
                           WHERE    TI.somedate <= S.somedate
                       ) OA;

หมายเหตุ: - ถ้าคุณต้องคำนวณผลรวมสะสมสำหรับพาร์ติชั่นที่ต่างกันก็สามารถทำได้ตามที่โพสต์ไว้ที่นี่: การคำนวณผลรวมสะสมของแถวและการจัดกลุ่มตาม ID

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.