มีวิธีเข้าถึงค่า“ แถวก่อนหน้า” ในคำสั่ง SELECT หรือไม่?


96

ฉันต้องการคำนวณความแตกต่างของคอลัมน์ระหว่างสองบรรทัดของตาราง มีวิธีใดบ้างที่ฉันสามารถทำได้โดยตรงใน SQL ฉันใช้ Microsoft SQL Server 2008

ฉันกำลังมองหาสิ่งนี้:

SELECT value - (previous.value) FROM table

จินตนาการว่าตัวแปร "ก่อนหน้า" อ้างอิงแถวล่าสุดที่เลือก แน่นอนว่าการเลือกแบบนั้นฉันจะลงเอยด้วย n-1 แถวที่เลือกในตารางที่มี n แถวนั่นอาจไม่ใช่สิ่งที่ฉันต้องการจริงๆ

เป็นไปได้ในทางใดทางหนึ่ง?


6
เพียงแค่เพิ่มความคิดเห็นที่เป็นประโยชน์สำหรับผู้ชมใหม่ ๆ SQL 2012 มี LAG และ LEAD แล้ว :) อ้างอิงลิงค์นี้blog.sqlauthority.com/2013/09/22/…
KD

คำตอบ:


65

SQL ไม่มีแนวคิดในการสั่งซื้อดังนั้นคุณต้องเรียงลำดับตามคอลัมน์เพื่อให้สิ่งนี้มีความหมาย สิ่งนี้:

select t1.value - t2.value from table t1, table t2 
where t1.primaryKey = t2.primaryKey - 1

หากคุณรู้วิธีสั่งซื้อสิ่งต่าง ๆ แต่ไม่ทราบวิธีรับค่าก่อนหน้าที่กำหนดให้เป็นค่าปัจจุบัน (เช่นคุณต้องการเรียงลำดับตามตัวอักษร) ฉันไม่รู้วิธีทำใน SQL มาตรฐาน แต่การใช้งาน SQL ส่วนใหญ่จะมี ส่วนขยายที่ต้องทำ

นี่คือวิธีสำหรับเซิร์ฟเวอร์ SQL ที่ทำงานหากคุณสามารถจัดเรียงแถวเพื่อให้แต่ละแถวแตกต่างกัน:

select  rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t

select t1.value - t2.value from temp1 t1, temp1 t2 
where t1.Rank = t2.Rank - 1

drop table temp1

หากคุณต้องการทำลายความสัมพันธ์คุณสามารถเพิ่มคอลัมน์ได้มากเท่าที่จำเป็นใน ORDER BY


ไม่เป็นไรคำสั่งซื้อไม่ใช่ปัญหาฉันแค่ลบมันออกจากตัวอย่างเพื่อให้ง่ายขึ้นฉันจะลองดู
Edwin Jarvis

7
ซึ่งถือว่าคีย์หลักถูกสร้างขึ้นตามลำดับและแถวจะไม่ถูกลบและการเลือกไม่มีส่วนคำสั่งอื่น ๆ และและและ ...
MartinStettner

มาร์ตินถูกต้อง แม้ว่าอาจได้ผลในบางกรณีคุณจำเป็นต้องกำหนดสิ่งที่คุณหมายถึงโดย "ก่อนหน้า" ในแง่ธุรกิจโดยเฉพาะอย่างยิ่งโดยไม่ต้องอาศัย ID ที่สร้างขึ้น
Tom H

คุณพูดถูกฉันได้เพิ่มการปรับปรุงโดยใช้ส่วนขยายเซิร์ฟเวอร์ SQL
RossFabricant

2
ในการตอบกลับว่า "ไม่เป็นไรคำสั่งซื้อไม่ใช่ปัญหา" ... แล้วทำไมคุณไม่ลบค่าอนุญาโตตุลาการในแบบสอบถามของคุณเพราะนั่นคือสิ่งที่คุณกำลังทำอยู่หากคุณไม่พิจารณาคำสั่ง
JohnFx

85

ใช้ฟังก์ชันlag :

SELECT value - lag(value) OVER (ORDER BY Id) FROM table

ลำดับที่ใช้สำหรับ Ids สามารถข้ามค่าได้ดังนั้น Id-1 จึงไม่ได้ผลเสมอไป


1
นี่คือโซลูชัน PostgreSQL คำถามเกี่ยวกับ MSSQL MSSQL มีฟังก์ชันดังกล่าวในเวอร์ชัน 2012+ ( msdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspx )
Kromster

10
@KromStern ไม่เพียง แต่เป็นโซลูชัน PostgreSQL ฟังก์ชัน SQL Windowถูกนำมาใช้ในมาตรฐานSQL: 2003
Hans Ginzel

ฟังก์ชัน LAG สามารถรับพารามิเตอร์ได้สามพารามิเตอร์: LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue). จำนวนแถวเริ่มต้นของความล่าช้าคือ 1 แต่คุณสามารถระบุและค่าเริ่มต้นเพื่อเลือกเมื่อไม่สามารถหน่วงได้เนื่องจากคุณอยู่ที่จุดเริ่มต้นของชุด
vaindil

30

Oracle, PostgreSQL, SQL Server และเอ็นจิ้น RDBMS อื่น ๆ อีกมากมายมีฟังก์ชันการวิเคราะห์ที่เรียกว่าLAGและLEADทำสิ่งนี้ได้ดี

ใน SQL Server ก่อนปี 2012 คุณต้องทำสิ่งต่อไปนี้:

SELECT  value - (
        SELECT  TOP 1 value
        FROM    mytable m2
        WHERE   m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk)
        ORDER BY 
                col1, pk
        )
FROM mytable m1
ORDER BY
      col1, pk

ซึ่งCOL1เป็นคอลัมน์ที่คุณมีการสั่งซื้อจาก

การมีดัชนี(COL1, PK)จะช่วยปรับปรุงการสืบค้นนี้ได้มาก


14
ขณะนี้ SQL Server 2012 มี LAG และ LEAD เช่นกัน
ErikE

สคริปต์ Hana SQL ยังรองรับ LAG และ LEAD
MIK

เพียงเพื่อเพิ่มความคิดเห็นอื่นให้กับผู้ชมที่มาถึงที่นี่เพื่อค้นหาสิ่งนั้นใน Hive นอกจากนี้ยังมีฟังก์ชัน LAG และ LEAD เอกสารที่นี่: cwiki.apache.org/confluence/display/Hive/…
Jaime Caffarel

27
WITH CTE AS (
  SELECT
    rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by),
    value
  FROM table
)
SELECT
  curr.value - prev.value
FROM CTE cur
INNER JOIN CTE prev on prev.rownum = cur.rownum - 1

จะทำงานได้อย่างถูกต้องหากไม่มีการจัดกลุ่มในแบบสอบถาม แต่ถ้าเราต้องการลบค่าจากค่าก่อนหน้าเฉพาะภายในกลุ่มสมมติว่า EmployeeID เดียวกันแล้วเราจะทำเช่นนั้นได้อย่างไร? Coz ที่เรียกใช้สิ่งนี้ใช้ได้เฉพาะกับ 2 แถวบนสุดของแต่ละกลุ่มและไม่ใช้กับแถวที่เหลือในกลุ่มนั้น สำหรับสิ่งนี้ฉันใช้การเรียกใช้รหัสนี้ใน while loop แต่ดูเหมือนว่าจะช้ามาก แนวทางอื่นใดที่เราสามารถทำได้ในสถานการณ์นี้? และเฉพาะใน SQL Server 2008 ด้วย?
Hemant Sisodia

10

LEFT JOIN ตารางเข้ากับตัวเองโดยที่เงื่อนไขการรวมทำงานออกมาดังนั้นแถวที่ตรงกับเวอร์ชันที่เข้าร่วมของตารางจึงเป็นแถวก่อนหน้าหนึ่งแถวสำหรับคำจำกัดความเฉพาะของคุณคือ "ก่อนหน้า"

อัปเดต: ตอนแรกฉันคิดว่าคุณต้องการเก็บแถวทั้งหมดโดยมีค่า NULL สำหรับเงื่อนไขที่ไม่มีแถวก่อนหน้า อ่านอีกครั้งคุณแค่ต้องการให้แถวนั้นถูกคัดออกดังนั้นคุณควรเข้าร่วมภายในมากกว่าการรวมด้านซ้าย


อัปเดต:

Sql Server เวอร์ชันใหม่กว่ายังมีฟังก์ชัน LAG และ LEAD Windowing ที่สามารถใช้สำหรับสิ่งนี้ได้เช่นกัน



2

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

วิธีนี้จะใช้ได้ผลถ้าคุณมีช่องว่าง

declare @temp (value int, primaryKey int, tempid int identity)
insert value, primarykey from mytable order by  primarykey

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