ประโยค SQL OVER () - เมื่อใดและทำไมจึงมีประโยชน์?


169
    USE AdventureWorks2008R2;
GO
SELECT SalesOrderID, ProductID, OrderQty
    ,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Total'
    ,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Avg'
    ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Count'
    ,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Min'
    ,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Max'
FROM Sales.SalesOrderDetail 
WHERE SalesOrderID IN(43659,43664);

ฉันอ่านเกี่ยวกับข้อนั้นและฉันไม่เข้าใจว่าทำไมฉันต้องการมัน ฟังก์ชั่นOverทำอะไร? อะไรPartitioning Byทำอย่างไร ทำไมฉันไม่สามารถทำแบบสอบถามที่มีการเขียนGroup By SalesOrderID?


30
ไม่ว่าคุณจะใช้ RDBMS ใดการสอน Postgresอาจมีประโยชน์ มีตัวอย่าง ช่วยฉัน
Andrew Lazarus

คำตอบ:


144

คุณสามารถGROUP BY SalesOrderIDใช้ ความแตกต่างคือด้วย GROUP BY คุณสามารถมีค่ารวมสำหรับคอลัมน์ที่ไม่รวมอยู่ใน GROUP BY เท่านั้น

ในทางตรงกันข้ามการใช้ฟังก์ชั่นการรวมแบบรวมแทนที่จะเป็น GROUP BY คุณสามารถดึงค่าที่รวมและไม่รวมเข้าด้วยกันได้ นั่นคือถึงแม้ว่าคุณจะไม่ได้ทำแบบนั้นในแบบสอบถามตัวอย่างเช่นคุณสามารถเรียกบุคคลทั้งสองOrderQtyค่านิยมและผลรวมของพวกเขานับค่าเฉลี่ย ฯลฯ มากกว่ากลุ่มเดียวกันSalesOrderIDs

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

SELECT
  orig.[Partition],
  orig.Value,
  orig.Value * 100.0 / agg.TotalValue AS ValuePercent
FROM OriginalRowset orig
  INNER JOIN (
    SELECT
      [Partition],
      SUM(Value) AS TotalValue
    FROM OriginalRowset
    GROUP BY [Partition]
  ) agg ON orig.[Partition] = agg.[Partition]

ตอนนี้ดูวิธีที่คุณสามารถทำเช่นเดียวกันกับการรวมหน้าต่าง:

SELECT
  [Partition],
  Value,
  Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent
FROM OriginalRowset orig

ง่ายกว่าและสะอาดกว่าใช่ไหม


68

ส่วนOVERคำสั่งที่มีประสิทธิภาพซึ่งคุณสามารถรวบรวมได้ในช่วงที่แตกต่างกัน ("หน้าต่าง") ไม่ว่าคุณจะใช้GROUP BYหรือไม่ก็ตาม

ตัวอย่าง: รับจำนวนต่อSalesOrderIDและนับทั้งหมด

SELECT
    SalesOrderID, ProductID, OrderQty
    ,COUNT(OrderQty) AS 'Count'
    ,COUNT(*) OVER () AS 'CountAll'
FROM Sales.SalesOrderDetail 
WHERE
     SalesOrderID IN(43659,43664)
GROUP BY
     SalesOrderID, ProductID, OrderQty

ได้รับแตกต่างกันCOUNTs ไม่มีGROUP BY

SELECT
    SalesOrderID, ProductID, OrderQty
    ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'CountQtyPerOrder'
    ,COUNT(OrderQty) OVER(PARTITION BY ProductID) AS 'CountQtyPerProduct',
    ,COUNT(*) OVER () AS 'CountAllAgain'
FROM Sales.SalesOrderDetail 
WHERE
     SalesOrderID IN(43659,43664)

47

หากคุณต้องการจัดกลุ่มตาม SalesOrderID คุณจะไม่สามารถรวมคอลัมน์ ProductID และ OrderQty ในส่วนคำสั่ง SELECT

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

SELECT
    O.order_id,
    O.order_date,
    ROW_NUMBER() OVER(PARTITION BY O.order_id) AS line_item_no,
    OL.product_id
FROM
    Orders O
INNER JOIN Order_Lines OL ON OL.order_id = O.order_id

(ไวยากรณ์ของฉันอาจปิดเล็กน้อย)

จากนั้นคุณจะได้รับสิ่งที่ชอบกลับไป:

order_id    order_date    line_item_no    product_id
--------    ----------    ------------    ----------
    1       2011-05-02         1              5
    1       2011-05-02         2              4
    1       2011-05-02         3              7
    2       2011-05-12         1              8
    2       2011-05-12         2              1

42

ให้ฉันอธิบายด้วยตัวอย่างและคุณจะสามารถดูว่ามันทำงานอย่างไร

สมมติว่าคุณมีตารางต่อไปนี้ DIM_EQUIPMENT:

VIN         MAKE    MODEL   YEAR    COLOR
-----------------------------------------
1234ASDF    Ford    Taurus  2008    White
1234JKLM    Chevy   Truck   2005    Green
5678ASDF    Ford    Mustang 2008    Yellow

ทำงานด้านล่าง SQL

SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR ,
  COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2
FROM DIM_EQUIPMENT

ผลลัพธ์จะเป็นดังนี้

VIN         MAKE    MODEL   YEAR    COLOR     COUNT2
 ----------------------------------------------  
1234JKLM    Chevy   Truck   2005    Green     1
5678ASDF    Ford    Mustang 2008    Yellow    2
1234ASDF    Ford    Taurus  2008    White     2

ดูว่าเกิดอะไรขึ้น

คุณสามารถนับได้โดยไม่ต้องจัดกลุ่มตามในปีและเข้าคู่กับ ROW

วิธีที่น่าสนใจอีกประการหนึ่งเพื่อให้ได้ผลลัพธ์เดียวกันหากใช้ด้านล่างด้วย WITH Clause, with ทำงานเป็น in-line VIEW และสามารถทำให้การสืบค้นง่ายขึ้นโดยเฉพาะอย่างยิ่งคำถามที่ซับซ้อนซึ่งไม่ใช่กรณีที่นี่เนื่องจากฉันพยายามแสดงการใช้งาน

 WITH EQ AS
  ( SELECT YEAR AS YEAR2, COUNT(*) AS COUNT2 FROM DIM_EQUIPMENT GROUP BY YEAR
  )
SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR,
  COUNT2
FROM DIM_EQUIPMENT,
  EQ
WHERE EQ.YEAR2=DIM_EQUIPMENT.YEAR;

17

ส่วนคำสั่งย่อย OVER เมื่อรวมกับ PARTITION BY ระบุว่าการเรียกใช้ฟังก์ชันก่อนหน้าต้องทำการวิเคราะห์โดยการประเมินแถวที่ส่งคืนของแบบสอบถาม คิดว่ามันเป็นคำสั่ง GROUP BY แบบอินไลน์

OVER (PARTITION BY SalesOrderID) กำลังระบุว่าสำหรับฟังก์ชัน SUM, AVG และอื่น ๆ ... ให้ส่งคืนค่ามากกว่าชุดย่อยของระเบียนที่ส่งคืนจากแบบสอบถามและพาร์ติชันที่ตั้งค่าย่อยตามคีย์ต่างประเทศ SalesOrderID

ดังนั้นเราจะรวมทุกระเบียน OrderQty สำหรับแต่ละ SalesOrderID ที่ไม่ซ้ำกันและชื่อคอลัมน์นั้นจะถูกเรียกว่า 'ยอดรวม'

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

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200

2
  • Query Petitionประโยคที่เรียกอีกอย่างว่า
  • คล้ายกับGroup Byข้อ

    • แบ่งข้อมูลออกเป็นส่วน ๆ (หรือพาร์ติชัน)
    • คั่นด้วยขอบเขตพาร์ติชัน
    • ฟังก์ชั่นดำเนินการภายในพาร์ทิชัน
    • เริ่มต้นใหม่เมื่อข้ามขอบเขตการพรากจากกัน

ไวยากรณ์:
ฟังก์ชั่น (... ) ขึ้นไป (PARTITION BY col1 col3, ... )

  • ฟังก์ชั่น

    • ฟังก์ชั่นที่คุ้นเคยเช่นCOUNT(), SUM(), MIN(), MAX()ฯลฯ
    • ฟังก์ชั่นใหม่เช่นกัน (เช่นROW_NUMBER(), RATION_TO_REOIRT()ฯลฯ )


ข้อมูลเพิ่มเติมพร้อมตัวอย่าง: http://msdn.microsoft.com/en-us/library/ms189461.aspx


-3
prkey   whatsthat               cash   
890    "abb                "   32  32
43     "abbz               "   2   34
4      "bttu               "   1   35
45     "gasstuff           "   2   37
545    "gasz               "   5   42
80009  "hoo                "   9   51
2321   "ibm                "   1   52
998    "krk                "   2   54
42     "kx-5010            "   2   56
32     "lto                "   4   60
543    "mp                 "   5   65
465    "multipower         "   2   67
455    "O.N.               "   1   68
7887   "prem               "   7   75
434    "puma               "   3   78
23     "retractble         "   3   81
242    "Trujillo's stuff   "   4   85

นั่นเป็นผลของการสืบค้น ตารางที่ใช้เป็นแหล่งข้อมูลเดียวกันนั้นไม่มีคอลัมน์สุดท้าย คอลัมน์นี้เป็นผลรวมที่เคลื่อนที่ได้ของหนึ่งในสาม

ค้นหา:

SELECT prkey,whatsthat,cash,SUM(cash) over (order by whatsthat)
    FROM public.iuk order by whatsthat,prkey
    ;

(ตารางแสดงเป็นสาธารณะ. iuk)

sql version:  2012

มันน้อยกว่าระดับ dbase (1986) ฉันไม่รู้ว่าทำไม 25 ปีที่ผ่านมาจึงต้องทำให้เสร็จ

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