2

データベース - Microsoft Adventureworks

テーブル - Sales.SalesOrderHeader

問題 - 2003 年 3 月または 2003 年 4 月にどちらの顧客 (顧客 ID) が注文したか (両方ではない)。

概念 -

青い部分、つまりA に固有の要素/行と B に 固有の要素/行を取得します。画像クレジット - CodingHorror.com の Jeff Atwood

私のクエリ -

select Soh.CustomerID, Soh.OrderDate 
from Sales.SalesOrderHeader as Soh
where Soh.OrderDate >= '2003-03-01' AND Soh.OrderDate < '2003-04-01' -- march only

UNION

select Soh.CustomerID, Soh.OrderDate 
from Sales.SalesOrderHeader as Soh
where Soh.OrderDate >= '2003-04-01' AND Soh.OrderDate < '2003-05-01' -- april only    
order by Soh.OrderDate asc;

私の質問 -

問題に正しく答えましたか? この問題を解決する他の方法、できればコードの量を減らす方法はありますか?

編集 -おっと。これは2か月以内に注文するだけで、質問には答えません. だから、私は間違っています。それを修正しようとしています。

ありがとう。

4

4 に答える 4

3
SELECT  Cust.CustomerID
FROM    Sales.Customer AS Cust
        INNER JOIN Sales.SalesOrderHeader AS Soh
            ON Cust.CustomerID = Soh.CustomerID
WHERE   Soh.OrderDate >= '2003-03-01' AND Soh.OrderDate < '2003-05-01'
GROUP   BY Cust.CustomerID
HAVING  COUNT(DISTINCT CASE WHEN MONTH(Soh.OrderDate) = 3 AND
                                YEAR(Soh.OrderDate) = 2003 THEN 1 ELSE 2 END) = 1

SELECTFROMWHEREおよびGROUP BYは一目瞭然です。ここで難しいのはHAVING節です。簡単に説明すると、表示されるCASEステートメントは、レコードをグループに分類した値を示します。andの代わりに1and2を使っMarchAprilわかりやすくします。

SELECT  CustomerID,
        CASE WHEN MONTH(OrderDate) = 3 AND YEAR(OrderDate) = 2003 
              THEN 'March' 
              ELSE 'April'
        END AS MonthBought
FROM    TableName
WHERE   OrderDate >= '2003-03-01' AND OrderDate < '2003-05-01'

デモでわかるように、注文日March, 2013がいずれかの日付の月に該当する場合、対応する の値はMonthBoughtですMarch。それ以外の場合は、句のためにAprilすべてのレコードがその間にあると確信しているためです。March and April 2013WHERE

この句は、顧客が特定の月にのみ購入したことを意味するto onlyHAVINGに一意の値の数を持つすべてのレコードをフィルター処理します。MonthBought1

于 2013-09-18T22:04:49.843 に答える
1
CREATE TABLE table_a ( id INTEGER NOT NULL PRIMARY KEY
   , OrderDate DATE NOT NULL DEFAULT '2003-03-15');
CREATE TABLE table_b ( id INTEGER NOT NULL PRIMARY KEY
   , OrderDate DATE NOT NULL DEFAULT '2003-04-15');

INSERT INTO table_a(id) VALUES (0),(2),(4),(6),(8),(10),(12),(14),(16),(18),(20);
INSERT INTO table_b(id) VALUES (0),(3),(6),(9),(12),(15),(18),(21);


SELECT COALESCE (a.id, b.id) AS id
FROM (
        SELECT DISTINCT id
        FROM table_a
        WHERE OrderDate >= '2003-03-01' AND OrderDate < '2003-04-01'
        ) a
FULL OUTER JOIN (
        SELECT DISTINCT id
        FROM table_b
        WHERE OrderDate >= '2003-04-01' AND OrderDate < '2003-05-01'
        )  b ON b.id = a.id
WHERE a.id IS NULL OR b.id IS NULL
        ;

注:OPが提供しなかったため、独自のデータを発明する必要があり、入力するのが面倒でした.

UPDATE: 元の UNION クエリ (ここでは table_a/table_b コンストラクトを使用し、元のデータ モデルには table_a = table_b = を使用しますSales.SalesOrderHeader)

SELECT a.id, a.OrderDate
FROM table_a as a
WHERE a.OrderDate >= '2003-03-01' AND a.OrderDate < '2003-04-01' -- march only
AND NOT EXISTS (
        SELECT * FROM table_b nx
        WHERE nx.id = a.id
        AND nx.OrderDate >= '2003-04-01' AND nx.OrderDate < '2003-05-01' -- april only
        )
UNION ALL
SELECT b.id, b.OrderDate
FROM table_b as b
WHERE b.OrderDate >= '2003-04-01' AND b.OrderDate < '2003-05-01' -- april only    
AND NOT EXISTS (
        SELECT * FROM table_a nx
        WHERE nx.id = b.id
        AND nx.OrderDate >= '2003-03-01' AND nx.OrderDate < '2003-04-01' -- march only
        )
ORDER BY OrderDate ASC;

ノート:

  • 重複は不可能であり、削除する必要がないため、the にするUNION必要がありますUNION ALL
  • NOT EXISTS ()句が必要です。4 月には存在しない記録が 3 月に必要であり、その逆も同様です。
  • の必要性UNIONは、多くの場合、最適化されていないデータ モデルを示しています (この場合はそうではありません)。
  • FULL OUTER JOIN関係分割の特別な形態と見なすことができます
于 2013-09-18T22:15:01.237 に答える
0

いいえ、あなたは問題に正しく答えていません。"Union" は、最初のクエリ (A) のすべての結果と、結果がまだ返されていない 2 番目のクエリのすべての結果を提供します。

でも素敵なグラフィック!

于 2013-09-18T21:56:23.540 に答える
0

よし、やっと手に入れた気がする。回答 - クエリで 746 行 -

-- Customers who had an order on Mar or Apr, but not both
select Ord.CustomerID
from Sales.SalesOrderHeader as Ord
where (Ord.OrderDate >= '2003-03-01' AND Ord.OrderDate < '2003-04-01') -- all March
or (Ord.OrderDate >= '2003-04-01' AND Ord.OrderDate < '2003-05-01') -- all April

except 

select MarchAndApril.CustomerID
from
(
select Ord.CustomerID
from Sales.SalesOrderHeader as Ord
where (Ord.OrderDate >= '2003-03-01' AND Ord.OrderDate < '2003-04-01') -- March

intersect

select Ord.CustomerID
from Sales.SalesOrderHeader as Ord
where (Ord.OrderDate >= '2003-04-01' AND Ord.OrderDate < '2003-05-01') -- April
) as MarchAndApril

order by Ord.CustomerID

簡単にするための別のサンプル データ セットを次に示します。

表 - 注文

列 - CustomerID(PK, int, Not null), OrderDate(date, not null) 注文は 1 月、2 月、7 月のみ。

1   2012-01-01
1   2012-01-02
1   2012-02-01
2   2012-01-01
2   2012-02-01
3   2012-01-01
4   2012-02-01
5   2012-07-01

新しい質問 - 両方ではなく、1 月または 2 月に注文した顧客を取得します。

戦略 - 1 月と 2 月の顧客を獲得します。次に、そのセットから、1 月と 2 月の両方に注文した顧客を削除します。

結果は 3,4 になると予想されます。確かにそうです。

-- Customers who had an order on Jan or Feb, but not both
select Ord.CustomerID
from Orders as Ord
where (Ord.OrderDate >= '2012-01-01' AND Ord.OrderDate < '2012-02-01') -- all January
or (Ord.OrderDate >= '2012-02-01' AND Ord.OrderDate < '2012-03-01') -- all February

--We can replace this where + or by a UNION ??? I got the same results, ie 3,4

except 

select JanuaryAndFebruary.CustomerID
from
(
select Ord.CustomerID
from Orders as Ord
where (Ord.OrderDate >= '2012-01-01' AND Ord.OrderDate < '2012-02-01') -- January

intersect

select Ord.CustomerID
from Orders as Ord
where (Ord.OrderDate >= '2012-02-01' AND Ord.OrderDate < '2012-03-01') -- February
) as JanuaryAndFebruary

order by Ord.CustomerID
于 2013-09-19T00:49:58.857 に答える