2016-11-18 26 views
0

我有一個幫助人們銷售東西的服務數據庫。如果他們沒有交付出售,他們會受到處罰。我試圖提取每個用戶在應用特定懲罰時的有效列表數。需要幫助包裝頭圍繞連接

我有相當於下面的表格(及相關領域):

  1. 用戶(ID)
  2. 上市(ID,USER_ID,狀態)
  3. 交易(listing_id,seller_id)
  4. listing_history(ID,listing_status,DATE_CREATED)
  5. 懲罰(ID,TRANSACTION_ID,USER_ID,DATE_CREATED)

每次列表被修改時,listing_history表會保存一個條目,保存列表的新狀態記錄。

我的目標是結果列表中包含字段:penalty_id,以及懲罰用戶應用懲罰時的活動列表數量。

到目前爲止我有以下:

SELECT s1.penalty_id, 
    COUNT(s1.record_id) 'active_listings' 
FROM (
    SELECT penalty.id AS 'penalty_id', 
    listing_history.id AS 'record_id', 
    FROM user 
    JOIN penalty ON penalty.user_id = user.id 
    JOIN transaction ON transaction.id = penalty.transaction_id 
    JOIN listing_history ON listing_history.listing_id = listing.id 
    WHERE listing_history.date_created < penalty.date_created 
    AND listing_history.status = 0 
) s1 
GROUP BY s1.penalty_id 

狀態= 0意味着列表是激活的(或該列表是活躍在記錄的創建時間)。我得到的結果與我的預期相似,但我擔心我可能會錯過某些內容,或者可能會錯誤地執行JOIN。這會得到你的認可嗎? (除明顯不使用別名外,清晰度問題)。

+0

我希望你可能會錯過粘貼一個JOIN,它是'JOIN listing ON listing.id = transaction.listing_id'。交叉檢查一次,不管你在查詢中是否有這個連接。 – Viki888

+0

@ viki888 - 爲什麼你認爲這個查詢需要一個額外的表?它看起來並不需要來自'LISTING'表的任何數據,如果關鍵結構如他所描述的那樣 –

+0

@MarkAdelsberger在他的查詢中,他使用'listing'與'listing_history'和'listing'作爲'',''加入listing_history ON listing_history.listing_id = listing.id',但列表表格在連接中缺失。 – Viki888

回答

2

UPDATE - 正如對這個答案的評論表明,改變表結構不是一個選項,這裏是一些查詢,你可以使用現有的結構更多的細節。

請注意,我甚至在修改邏輯之前對查詢進行了一些更改。

  • 正如viki888指出,有一個問題參考listing.id;我已經取代了它。
  • 在原始查詢中沒有真正需要子查詢;我簡化了它。

所以原來的查詢改寫爲

SELECT penalty.id AS 'penalty_id' 
    , COUNT(listing_history.id) 'active_listings' 
    FROM  user 
     JOIN penalty 
     ON penalty.user_id = user.id 
     JOIN transaction 
     ON transaction.id = penalty.transaction_id 
     JOIN listing_history 
     ON listing_history.listing_id = transaction.listing_id 
WHERE listing_history.date_created < penalty.date_created 
    AND listing_history.status = 0 
GROUP BY penalty.id 

現在最自然的方式,在我看來,寫修正時間軸約束是一個NOT EXISTS條件過濾掉所有,但最近listing_history記錄給定id。這確實需要考慮一些邊緣情況:

  • 兩個上市歷史記錄可以具有相同的創建日期嗎?如果是這樣,你如何決定首先發生的事情?
  • 如果列表歷史記錄是在懲罰的同一天創建的,那麼會被視爲首先發生?

如果created_date確實是一個時間戳,那麼這可能並不重要(如果有的話);如果真的是一個約會,這可能是一個更大的問題。由於您的原始查詢需要在之前創建上市歷史記錄,我會繼續保持這種風格;但如何處理兩個具有匹配狀態的歷史記錄具有相同日期的情況仍然不明確。您可能需要調整日期比較以獲得所需的行爲。

SELECT penalty.id AS 'penalty_id' 
    , COUNT(DISTINCT listing_history.id) 'active_listings' 
    FROM  user 
     JOIN penalty 
     ON penalty.user_id = user.id 
     JOIN transaction 
     ON transaction.id = penalty.transaction_id 
     JOIN listing_history 
     ON listing_history.listing_id = transaction.listing_id 
WHERE listing_history.date_created < penalty.date_created 
    AND listing_history.status = 0 
    AND NOT EXISTS (SELECT 1 
        FROM listing_history h2 
        WHERE listing_history.date_created < h2.date_created 
         AND h2.date_created < penalty.date_created 
         AND h2.id = listing_history.id) 
GROUP BY penalty.id 

注意,我從COUNT(...)切換到COUNT(DISTINCT ...);這有助於一些邊緣案例,其中可能會計算同一列表的兩個活動記錄。

如果更改日期比較使用<=而不是< - 或者等價地,如果使用BETWEEN的日期比較相結合 - 那麼你要新增AND h2.status != 0(或AND h2.status <> 0,這取決於你的數據庫)到子查詢,以便兩個併發的ACTIVE記錄不會相互抵消。

有幾種等價的方法來編寫它,不幸的是它的查詢不總是與數據庫查詢優化器配合使用,所以爲了使它在大數據量下運行,可能需要一些試驗和錯誤。希望能夠給出足夠的洞察力,以便能夠根據需要制定出一些等價的邏輯。你可以考慮使用NOT IN而不是NOT EXISTS;或者你可以使用一個外部連接到第二個實例LISTING_HISTORY ...可能還有其他我沒有想到的。


我不知道我們是否有能力在一般性聲明中籤署該查詢是否「正確」。如果在特定情況下查詢是否包含/排除記錄(或者爲什麼它會/不會,或者如何修改它以至於不會/將會),這些問題可能會得到更完整的答案。

我可以說,有幾個可能的問題:

唯一明顯的邏輯問題有時間表的管理,這是一件好事,導致了很多與SQL麻煩的事情。問題在於,雖然您的查詢顯示該處列表在處罰創建日期前的某個時間點處於活動狀態,但並未表明該處列表在仍處於處於懲罰創建日期時處於活動狀態。考慮

PENALTY 
id    transaction date 
1    10    2016-02-01 

TRANSACTION 
id    listing_id 
10    100 

LISTING_HISTORY 
listing_id  status   date 
100   0    2016-01-01 
100   1    2016-01-15 

的加入將創造一個記錄,並罰款1計數將包括100上市,即使其狀態已經改變爲0以外的東西被創造點球點前。

這很難 - 但並非不可能 - 用您現有的表格結構進行修復。您可以添加一個NOT EXISTS條件查找與第一個LISTING_HISTORY日期和罰分日期之間的日期匹配的ID的另一個LISTING_HISTORY記錄。

將結束日期添加到LISTING_HISTORY日期會更有效,但根據數據的維護方式可能並不那麼容易。

第二個潛在問題COUNT(RECORD_ID)。這可能不符合你的意思 - COUNT(x)可能直觀地看起來應該如此,COUNT(DISTINCT RECORD_ID)實際上是做什麼的。正如所寫的,如果連接產生兩個具有相同LISTING_HISTORY.ID值的匹配項 - 即在懲罰之前的兩個不同時間激活列表 - 則列表將被計數兩次。

+0

哦,非常感謝。我現在看到我的錯誤。我不能編輯數據庫的結構,所以不幸的是,您的更有效的方法不是一種選擇。但感謝您的建議!無論如何,我是SQL新手,只是學習。我將如何執行NOT EXISTS?我需要在第一個子查詢的WHERE子句中使用第二個子查詢嗎? –

+0

我在答案的更新中包含了一個示例。順便說一句,雖然我簡化了原來的子查詢,但大多數數據庫都允許你嵌套子查詢,所以你可以在另一個子查詢的WHERE子句中使用NOTCHISIS子查詢 - 這並不是必須的這個案例。 –

+0

抱歉等了很長時間才能再次回覆。非常感謝。這件事情讓我感到很快樂。我認爲我將不得不微調它,但現在我不想惹它太多,只是想到這個解決方案的美麗。 :) –