5

MySQL販売データベースに2つのテーブルがあります。

注文表:

CREATE TABLE salestest.`orders` (  
`ID` int(11) unsigned NOT NULL auto_increment,  
`OrderDate` datetime NOT NULL,  
`CustomerID` int(11) unsigned NOT NULL,  
PRIMARY KEY (`ID`),  
UNIQUE KEY `ID` (`ID`),  
KEY `OrderDate` (`OrderDate`),  
KEY `CustomerID` (`CustomerID`)  
) ENGINE=InnoDB;  

INSERT INTO salestest.orders VALUES  
( 1, '2012-04-15', 1 ),  
( 2, '2012-05-20', 1 ),  
( 3, '2012-06-30', 1 );  

OrderDetailsテーブル:

CREATE TABLE salestest.`OrderDetails` (  
`ID` int(11) unsigned NOT NULL auto_increment,  
`OrderID` int(11) unsigned NOT NULL,  
`ProductID` int(11) unsigned NOT NULL,  
`Price` double NOT NULL default '0',  
PRIMARY KEY  (`ID`),  
UNIQUE KEY `ID` (`ID`),  
KEY `OrderID` (`OrderID`),  
KEY `ProductID` (`ProductID`),  
CONSTRAINT `OrderID_fk` FOREIGN KEY (`OrderID`) REFERENCES `orders` (`ID`)  
) ENGINE=InnoDB;  

INSERT INTO salestest.OrderDetails VALUES  
( 1, 1, 1, 2 ),  
( 2, 1, 2, 15 ),  
( 3, 1, 3, 22 ),  
( 4, 2, 1, 3 ),  
( 5, 2, 2, 17 ),  
( 6, 2, 3, 23 ),  
( 7, 2, 4, 40 ),  
( 8, 3, 1, 4 ),  
( 9, 3, 2, 20 );  

次に、顧客ごとに、各製品を購入する最後の価格を選択する必要があります。

これを行う簡単な方法は、サブクエリを使用することです。

SELECT od2.CustomerID,od2.ProductID, od2.Price AS LastPrice, od2.OrderDate AS LastDate  
FROM (SELECT o1.ID, o1.CustomerID, o1.OrderDate, od1.ProductID, od1.Price  
  FROM orders AS o1  
  LEFT JOIN OrderDetails as od1 ON o1.ID=od1.OrderID  
  ORDER BY OrderDate DESC) AS od2  
GROUP BY CustomerID, ProductID  
ORDER BY CustomerID, ProductID;  

結果:

CustomerID ProductID LastPrice LastDate
1 1 4 2012-06-30 00:00:00
1 2 20 2012-06-30 00:00:00
1 3 23 2012-05-20 00:00:00
1 4 40 2012-05- 20 00:00:00

さて、質問です。サブクエリ、一時テーブル、またはビューを避けたい場合、どうすれば同じ結果を得ることができますか。結合のみを使用したいのです。このクエリは、はるかに大きなクエリのごく一部であり、サブクエリを使用することは非常に非効率的です。

私はこのクエリを試しました:

SELECT o1.CustomerID、od1.ProductID、od1.Price AS LastPrice、o1.OrderDate AS LastDate
FROM Orders AS o1 LEFT JOIN OrderDetails as od1 ON o1.ID = od1.OrderID
GROUP BY CustomerID、ProductID
ORDER BY CustomerID、ProductID;

しかし、それは異なる結果をもたらします:

CustomerID ProductID LastPrice LastDate
1 1 2 2012-04-15 00:00:00
1 2 15 2012-04-15 00:00:00
1 3 22 2012-04-15 00:00:00
1 4 40 2012-05- 20 00:00:00

ご覧のとおり、LastPriceとLastDateは正しくありません。アレンの提案も試しましたが、結果は次のとおりです。

CustomerID ProductID LastPrice LastDate
1 1 4 2012-06-30 00:00:00
1 2 20 2012-06-30 00:00:00

スペンサーの回答結果からの最初のクエリは、重複した製品です。

CustomerID ProductID LastPrice LastDate
1 3 22 2012-04-15 00:00:00
1 3 23 2012-05-20 00:00:00
1 4 40 2012-05-20 00:00:00
1 1 4 2012-06- 30 00:00:00
1 2 20 2012-06-30 00:00:00

他の答えはすべて、私が避けようとしているサブクエリを使用しています。
助言がありますか?

4

4 に答える 4

1

「グループあたり最大のn」を探す

これは私がSQLで学んだ中で最も素晴らしいことです。あなたもそれを気に入ってくれることを願っています。

OK、これが私の刺し傷です:

SELECT o.CustomerID, od.ProductID, od.Price AS LastPrice, o.OrderDate AS LastDate  
FROM OrderDetails od
LEFT JOIN orders as o ON od.OrderID = o.ID
LEFT JOIN orders as o2 ON o.CustomerID = o2.CustomerID AND o.id < o2.id
WHERE o2.id IS NULL
ORDER BY o.CustomerID, od.ProductID;

顧客+製品ごとに、その顧客が各製品を最後に購入したのはいつで、何を支払ったのかを知りたいとします。

そこで、製品から始めて注文に参加し(最初の参加)、次に注文に再度参加して、クエリを顧客+製品ごとに1つの注文に制限できるようにしました(o2は、最新の注文までのすべての注文に一致しますが、最新の注文は含まれません) 。次に、o2が最新の順序と一致しないという事実を使用して、その1つの行のみを選択します。

これは、価格が異なる1つの注文で同じアイテムが2回発生することはなく、新しい注文のIDは常に高くなることを前提としています。

うまくいけば、これで実際のデータ/クエリを必要に応じて変更できるようになります-頑張ってください!

于 2012-07-03T18:51:50.417 に答える
0

アップデート:

結合のみを使用して(インラインビューまたは相関サブクエリを使用せずに)結果セットを再現できませんでした。

インラインビューまたは相関サブクエリを使用せずに、指定された結果セットを確実に返すことはできないと思います。


これにより、指定された結果が返されますが、インラインビューやサブクエリは返されません。(しかし、これが結果セットを返す「最速の」クエリになるというわけではありません。

動作していません...待機してください

SELECT o1.CustomerID, d1.ProductID, d1.Price, o1.Orderdate
  FROM orders o1
  JOIN OrderDetails d1 ON d1.OrderID = o1.ID
  LEFT      
  JOIN orders o2 
    ON o1.CustomerID = o2.CustomerID
       AND o1.ID <> o2.ID
       AND (o1.OrderDate < o2.OrderDate
           OR (o1.OrderDate = o2.OrderDate AND o1.ID < o2.ID)
           )
  LEFT
  JOIN OrderDetails d2
    ON d2.OrderID = o2.ID
       AND d2.ProductID = d1.ProductId
       AND (o1.OrderDate < o2.OrderDate
           OR (o1.OrderDate = o2.OrderDate AND o1.ID < o2.ID)
           OR (o1.OrderDate = o2.OrderDate AND o1.ID = o2.ID AND d1.ID < d2.ID )
           )
 WHERE d2.ID IS NULL 

このクエリは、テーブルをそれら自体に結合し、各グループの「最上位」の行を除外します。

-

概念的には、そのクエリは次のクエリと同じです。次のクエリは、「インラインビュー」(別名ab)を使用します。インラインビューの目的は、実際には、各OrderDetail行に関連付けられたCustomerIDとOrderDateを取得することだけです。

 SELECT a.CustomerID, a.ProductID, a.Price, a.Orderdate
   FROM (SELECT o1.CustomerID, d1.ProductID, d1.Price, o1.OrderDate, d1.OrderID, d1.ID
           FROM orders o1
           JOIN OrderDetails d1 ON d1.OrderID = o1.ID
        ) a
   LEFT      
   JOIN (SELECT o2.CustomerID, d2.ProductID, d2.Price, o2.OrderDate, d2.OrderID, d2.ID
           FROM orders o2
           JOIN OrderDetails d2 ON d2.OrderID = o2.ID
        ) b
     ON a.CustomerID = b.CustomerID
        AND a.ProductID = b.ProductId
        AND a.OrderID <> b.OrderID
        AND a.ID <> b.ID
        AND (a.OrderDate < b.OrderDate 
             OR (a.OrderDate = b.OrderDate AND a.OrderID < b.OrderID)
             OR (a.OrderDate = b.OrderDate AND a.OrderID = b.OrderID AND a.ID < b.ID))
  WHERE b.ID IS NULL 

MySQLがインラインビューをサポートしている場合は、インラインビューの代わりに共通テーブル式(CTE)を使用します。


最後に、これはまったく異なるアプローチです。これは、MySQLの「ユーザー変数」を使用して、MySQLにない分析関数をシミュレートします。

SELECT q.CustomerID
     , q.ProductID
     , q.Price
     , q.OrderDate
  FROM (SELECT IF(p.CustomerID = @last_customerid,IF(p.ProductID = @last_productid,0,1),1) AS break
             , @last_customerid := p.CustomerID AS CustomerID
             , @last_productid := p.ProductID AS ProductID
             , p.Price
             , p.OrderDate
         FROM (SELECT @last_customerid := NULL, @last_productid := NULL) i
         JOIN ( SELECT o.CustomerID, d.ProductID, o.OrderDate, d.Price 
                  FROM orders o
                  JOIN OrderDetails d ON d.OrderID = o.ID
                 ORDER BY o.CustomerID, d.ProductID, o.OrderDate DESC
              ) p
       ) q
  WHERE q.break = 1

于 2012-07-03T19:04:27.630 に答える
0
select B.*,A.Price from
(select CustomerID,ProductID,OrderDate,Price from Orders o join OrderDetails od on o.ID=od.OrderID) A 
join 
(select CustomerID,ProductID,max(OrderDate) as LastOrderDate 
from Orders o 
join OrderDetails od on o.ID=od.OrderID
group by CustomerID,ProductID) B 
on A.CustomerID=B.CustomerID and A.ProductID=B.ProductID and A.OrderDate=B.LastOrderDate
于 2012-07-03T18:49:11.023 に答える
0

これがより効率的であることがわかります。

select opl.CustomerID,
    opl.ProductID,
    opl.LastDate,
    od.Price
from (
    select o.CustomerID,
        od.ProductID,
        max(o.OrderDate) as LastDate
    from Orders o
    inner join OrderDetails od on o.ID = od.OrderID
    group by o.CustomerID, od.ProductID
) opl
inner join Orders o on opl.CustomerID = o.CustomerID
    and opl.LastDate = o.OrderDate
inner join OrderDetails od on o.ID = od.OrderID
于 2012-07-03T18:52:24.497 に答える