Join

加入產品和訂單,以便列出每個產品以及特定買家訂購的數量

  • June 22, 2017

表 A 是列表,"products"表 B 是"orders"參考產品列表(使用product_id),以及買家資訊(buyer_nameecc )和購買quantity的每個產品的列表。

一個訂單有許多產品和一個買家,因此當買家發出訂單時,具有相同的行buyer_name並被order_no插入到表 B中,用於每個訂購的產品以及所需的quantity.

這是加入最快的兩個查詢"products""orders"因此對於每一"products"行,我還quantity擁有指定買家訂購的查詢:

  • 在單個訂單中(order_no已指定)
  • quantity在他的所有訂單中(買方發出的每個訂單的總和)

為了提高效率,所有"orders"不屬於指定買家的行(並且沒有order_no為第一個查詢指定的行)應該在連接之前被排除,但是應該返回每一行"products",即使產品還沒有從買家那裡訂購(quantity應該0在這種情況下)。

更新(來自評論):

目前查詢:

SELECT p.*, o.buyer_name, coalesce(o.quantity,0)
 FROM products p
        LEFT JOIN (SELECT *
                     FROM orders
                    WHERE buyer_name='Joe'
                     AND order_no=123
                  ) AS o USING(product_id)

我現在沒有性能問題,也不想嚴格調整這個特定的查詢,而是確保我總體上有效地編寫查詢。同樣,目前的 DBMS 是 PostgreSQL 9.4.4 - 但正在尋找適用於大多數 SQL 變體的通用建議。

通常,最好將數據如何連接在一起的細節留給數據庫引擎——通常,有一個優化器會查看表/索引統計資訊,並找出最有效的方法來消除考慮中的行。如果您在特定查詢上遇到性能問題,您有時可以強制引擎在給定查詢上使用特定方法/選項 - 但這可能會導致特定情況下的良好性能和其他所有情況下的糟糕性能。

也就是說,您的查詢邏輯是,如果您的“正確”表中的行不屬於目標買家/訂單,則應將它們排除在考慮之外,因此它們確實需要作為LEFT JOIN子句的一部分包含在內。我會修改如下:

 FROM products p
        LEFT JOIN orders o ON (    p.product_id = o.product_id
                               AND o.buyer_name = 'Joe'
                               AND o.order_no = 123
                              )

如果你只是簡單地連接這兩個表,數據庫引擎將從查詢的其餘部分中找出它需要從這些表中的哪些列。我想不出一個很好的理由來使用只從連接中的表中選擇特定列的子查詢,而不是直接使用表本身。我不會說沒有這樣的原因,只是我從未遇到過。

某些版本的 SQL 可能會實現子查詢 - 基本上,在評估主查詢之前執行子查詢,然後根據這些結果評估主查詢。如果確實選擇使用子查詢,我建議您不要使用SELECT *,而是僅在主查詢 ( SELECT product_id, order_qty from orders…) 中指定您需要的實際列,以避免拉入您實際上不會使用的數據。

同樣的建議也適用於主查詢;而不是使用SELECT *,指定您需要的實際列。如前所述,數據庫引擎將通過查看主查詢的 SELECT 列表、WHERE子句、JOIN條件等來確定每個表中需要哪些列。如果您在列表中指定的列SELECT比您真正需要的多,那麼引擎會將它們提供給您。如果表中的每一行數據有兩個varchar欄位,每個欄位平均 250 個字元,但您真正需要的是一個varchar(25)欄位和三個整數,那麼您將返回一個大約 10 倍大的結果集(其中到達發出請求的機器大約需要十倍的時間)。

將您的列限制為您實際需要的列也將允許數據庫引擎的優化器應用某些可以提高性能的快捷方式。例如,如果您的表的行(平均)為 1000 字節寬,但您有一個僅包含所需列的索引,並且這些列平均每行僅 100 字節,則優化器可以使用該索引,就好像它是表,因此可能能夠將獲取數據所需讀取的數據庫頁數減少 10 倍。這個術語是覆蓋索引

將列名分配給計算列也是一個好主意 - 它可以更容易地在其他地方引用它們。

因此,我們最終會得到以下查詢(請注意,我假設您只會使用product_name結果中命名的列;如果應用中的其他列products,請將它們添加到SELECT列表中(以及 ORDER BY 子句):

對於特定訂單:

SELECT p.product_name
     ,o.buyer_name
     ,COALESCE(o.quantity,0) as Qty_Ordered
 FROM products p
        LEFT JOIN orders o ON (    p.product_id = o.product_id
                               AND o.buyer_name = 'Joe'
                               AND o.order_no = 123
                              )

對於買方的總活動:

SELECT p.product_name
     ,o.buyer_name
     ,COALESCE(SUM(o.quantity),0) as Qty_Ordered
 FROM products p
        LEFT JOIN orders o ON (    p.product_id = o.product_id
                               AND o.buyer_name = 'Joe'
                              )
GROUP BY p.product_name, o.buyer_name

最後的幾點說明:

在某些 SQL 變體中,不在列表中聚合函式中的每一列都SELECT必須出現在GROUP BY子句中。其他版本允許您省略列 - 將返回“隨機”行的值。如果您從 中的表中獲得了唯一值GROUP BY,則如果使用這些其他版本之一,則可以安全地將該表中的其他列排除在外,因為正在聚合的行中的所有可能值都必須相同。

此外,您的查詢做了一個假設:任何訂單對於給定的產品只有一個訂單項。現在,從表面上看,這似乎是一個合理的假設。但是,我見過一些系統,有人可能會在本月 1 日訂購 10 個小元件,並在 15 日再訂購 10 個。如果某個產品有可能在同一個訂單中出現兩次,您也可以使用第二個查詢來提取特定於訂單的資訊;你只需要放AND o.order_no = 123回去,就像在第一個查詢中一樣。SUM這可能比沒有and的查詢花費稍長的時間ORDER BY- 但我希望在大多數情況下,差異對於人類來說太小了。

引用自:https://dba.stackexchange.com/questions/176920