具体的な、しかし仮説的な例を使用します。
各 注文 には通常1つの 明細しかありません :
オーダー:
OrderGUID OrderNumber
========= ============
{FFB2...} STL-7442-1
{3EC6...} MPT-9931-8A
LineItems:
LineItemGUID Order ID Quantity Description
============ ======== ======== =================================
{098FBE3...} 1 7 prefabulated amulite
{1609B09...} 2 32 spurving bearing
しかし、時折、2つの広告申込情報がある注文があります。
LineItemID Order ID Quantity Description
========== ======== ======== =================================
{A58A1...} 6,784,329 5 pentametric fan
{0E9BC...} 6,784,329 5 differential girdlespring
通常、ユーザーに注文を表示するときは、
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
INNER JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
注文に単一の商品を表示したいのですが。しかし、2つ(またはそれ以上)の項目を含むこの時折の注文では、注文は 出現 be 複製 :
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 spurving bearing
KSG-0619-81 5 panametric fan
KSG-0619-81 5 differential girdlespring
私が本当に欲しいのはSQL Serverを持っていることです ちょうど1つを選ぶ 、 十分に良い :
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 differential girdlespring
KSG-0619-81 5 panametric fan
私が冒険的になった場合は、ユーザーに複数の要素があることを示すために省略記号を表示することがあります。
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 differential girdlespring
KSG-0619-81 5 panametric fan, ...
問題はどちらか
私の最初の素朴な試みは、 " TOP 1 "の広告申込情報にのみ参加することでした。
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
INNER JOIN (
SELECT TOP 1 LineItems.Quantity, LineItems.Description
FROM LineItems
WHERE LineItems.OrderID = Orders.OrderID) LineItems2
ON 1=1
しかし、それはエラーを与えます:
列または接頭辞 'Orders'はそうではありません。
テーブル名または別名と一致
クエリで使用されています。
おそらく内側のselectが外側のテーブルを見ていないからです。
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
JOIN LineItems
ON LineItems.LineItemGUID =
(
SELECT TOP 1 LineItemGUID
FROM LineItems
WHERE OrderID = Orders.OrderID
)
SQL Server 2005
以上では、INNER JOIN
をCROSS APPLY
に置き換えることができます。
SELECT Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM Orders
CROSS APPLY
(
SELECT TOP 1 LineItems.Quantity, LineItems.Description
FROM LineItems
WHERE LineItems.OrderID = Orders.OrderID
) LineItems2
TOP 1
のないORDER BY
は確定的ではないことに注意してください。このクエリでは、注文ごとに1つの広告申込情報が表示されますが、どちらになるかは定義されていません。
基になるものが変更されていなくても、クエリを複数回呼び出すと、同じ注文に対して異なる広告申込情報が表示される可能性があります。
決定論的な順序が必要な場合は、最も内側のクエリにORDER BY
句を追加する必要があります。
私はこの質問にしばらく前に回答したことを知っていますが、大規模なデータセットを扱う場合、入れ子になったクエリはコストがかかる可能性があります。これは、返される各行に対してではなく、ネストされたクエリが1回だけ実行されるという別の解決策です。
SELECT
Orders.OrderNumber,
LineItems.Quantity,
LineItems.Description
FROM
Orders
INNER JOIN (
SELECT
Orders.OrderNumber,
Max(LineItem.LineItemID) AS LineItemID
FROM
Orders INNER JOIN LineItems
ON Orders.OrderNumber = LineItems.OrderNumber
GROUP BY Orders.OrderNumber
) AS Items ON Orders.OrderNumber = Items.OrderNumber
INNER JOIN LineItems
ON Items.LineItemID = LineItems.LineItemID
あなたがすることができます:
SELECT
Orders.OrderNumber,
LineItems.Quantity,
LineItems.Description
FROM
Orders INNER JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
WHERE
LineItems.LineItemID = (
SELECT MIN(LineItemID)
FROM LineItems
WHERE OrderID = Orders.OrderID
)
これにはLineItems.LineItemID
のインデックス(または主キー)とLineItems.OrderID
のインデックスが必要ですが、遅くなります。
@Quassnoiの答えは良い場合があります。(特に外側のテーブルが大きい場合は)、ウィンドウ関数を使った方が効率的なクエリが得られるでしょう。
SELECT Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM Orders
LEFT JOIN
(
SELECT LineItems.Quantity, LineItems.Description, OrderId, ROW_NUMBER()
OVER (PARTITION BY OrderId ORDER BY (SELECT NULL)) AS RowNum
FROM LineItems
) LineItems2 ON LineItems2.OrderId = Orders.OrderID And RowNum = 1
時々 テストする必要がある どのクエリがより良いパフォーマンスを与えるか。
共通の表式を使用したもう1つの方法:
with firstOnly as (
select Orders.OrderNumber, LineItems.Quantity, LineItems.Description, ROW_NUMBER() over (partiton by Orders.OrderID order by Orders.OrderID) lp
FROM Orders
join LineItems on Orders.OrderID = LineItems.OrderID
) select *
from firstOnly
where lp = 1
または、結局、結合されたすべての行を表示しますか?
ここでコンマ区切りのバージョン:
select *
from Orders o
cross apply (
select CAST((select l.Description + ','
from LineItems l
where l.OrderID = s.OrderID
for xml path('')) as nvarchar(max)) l
) lines
相関サブクエリは、外側のクエリに依存するサブクエリです。これはSQLのforループのようなものです。サブクエリは外側のクエリの各行に対して一回実行されます。
select * from users join widgets on widgets.id = (
select id from widgets
where widgets.user_id = users.id
order by created_at desc
limit 1
)
SQL Server 2012以降では、これでうまくいくと思います。
SELECT DISTINCT
o.OrderNumber ,
FIRST_VALUE(li.Quantity) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Quantity ,
FIRST_VALUE(li.Description) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Description
FROM Orders AS o
INNER JOIN LineItems AS li ON o.OrderID = li.OrderID
編集:決して気にしないで、Quassnoiはより良い答えを持っています。
SQL2Kの場合は、次のようになります。
SELECT
Orders.OrderNumber
, LineItems.Quantity
, LineItems.Description
FROM (
SELECT
Orders.OrderID
, Orders.OrderNumber
, FirstLineItemID = (
SELECT TOP 1 LineItemID
FROM LineItems
WHERE LineItems.OrderID = Orders.OrderID
ORDER BY LineItemID -- or whatever else
)
FROM Orders
) Orders
JOIN LineItems
ON LineItems.OrderID = Orders.OrderID
AND LineItems.LineItemID = Orders.FirstLineItemID
このクエリを実行する私のお気に入りの方法は、存在しない句を使用することです。私はこれがこの種のクエリを実行するための最も効率的な方法だと思います。
select o.OrderNumber,
li.Quantity,
li.Description
from Orders as o
inner join LineItems as li
on li.OrderID = o.OrderID
where not exists (
select 1
from LineItems as li_later
where li_later.OrderID = o.OrderID
and li_later.LineItemGUID > li.LineItemGUID
)
しかし、ここで提案した他の方法に対してこの方法をテストしたことはありません。
十字架を試してみました、うまく動作しますが、少し時間がかかります。速度を維持し、追加レコードを削除する最大および追加グループを持つように行の列を調整しました。
これが調整されたクエリです。
SELECT Orders.OrderNumber, max(LineItems.Quantity), max(LineItems.Description)
FROM Orders
INNER JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
Group by Orders.OrderNumber
私はLEFT JOINとGROUP BY Orders.OrderNumberを使って同様の問題を解決します。このようにしない理由はありますか?
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
LEFT JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
GROUP BY Orders.OrderNumber
私はあなた自身の質問の答えと共にあなたの答えの質問に答えます。
Orders LineItems
+-------------+ +---------+----------+---------------+
| OrderNumber | | OrderID | Quantity | Description |
+-------------+ +---------+----------+---------------+
| 22586 | | 22586 | 17 | Trunion |
+-------------+ | 22586 | 3 | Girdle Spring |
+---------+----------+---------------+
2つをOrderNumberで結合すると、次のようになります。
OrderNumber Quantity Description
----------- -------- -------------
22586 17 Trunion
22586 3 Girdle Spring
2 row(s) affected
1行だけを返したい場合は、次のようにします。
OrderNumber Quantity Description
----------- -------- -------------
22586 17 Trunion
1 row(s) affected
これが、OrderNumberごとに1行しか返さないGROUP BY Orders.OrderNumberを使用する理由です。