2017-11-10 128 views
2

在此查詢中,我需要顯示左表中的所有記錄,並且只顯示右表中所有結果爲最高日期的記錄。從第二個表中只加入一行,如果不存在行返回null

目前查詢:

SELECT a.*, c.* 
FROM users a 
INNER JOIN payments c 
    ON a.id = c.user_ID 
INNER JOIN 
(
    SELECT user_ID, MAX(date) maxDate 
    FROM payments 
    GROUP BY user_ID 
) b ON c.user_ID = b.user_ID AND 
     c.date = b.maxDate 
WHERE a.package = 1 

這將返回所有記錄中,其中加入是有效的,但我要顯示所有用戶,如果他們沒有付款尚未從支付表中的字段爲空。

我可以用一個工會來顯示其他行:

SELECT a.*, c.* 
FROM users a 
INNER JOIN payments c 
    ON a.id = c.user_ID 
INNER JOIN 
(
    SELECT user_ID, MAX(date) maxDate 
    FROM payments 
    GROUP BY user_ID 
) b ON c.user_ID = b.user_ID AND 
     c.date = b.maxDate 
WHERE a.package = 1 
union 
SELECT a.*, c.* 
FROM users a 
--here I would need to join with payments table to get the columns from the payments table, 
but where the user doesn't have a payment yet 
WHERE a.package = 1 

選擇使用工會似乎並不像一個很好的解決方案,但是這是我的嘗試。

回答

2

所以,換句話說,您需要一個用戶列表和最後一筆付款。

您可以使用OUTER APPLY而不是INNER JOIN來獲取每個用戶的最後付款。性能可能會更好,它將按照您希望的方式針對沒有付款的用戶開展工作。

SELECT a.*, b.* 
FROM users a 
OUTER APPLY (SELECT * FROM payments c 
       WHERE c.user_id = a.user_id 
       ORDER BY c.date DESC 
       FETCH FIRST ROW ONLY) b 
WHERE a.package = 1; 

這是一個通用版本的相同的概念,不需要你的表(對於其他讀者)。它給出了每個用戶的數據庫用戶列表和最近修改的對象。您可以正確地看到包含沒有對象的用戶。

SELECT a.*, b.* 
FROM all_users a 
OUTER APPLY (SELECT * FROM all_objects b 
       WHERE b.owner = a.username 
       ORDER BY b.last_ddl_time desc 
       FETCH FIRST ROW ONLY) b 
1

我喜歡@Matthew麥克皮克答案,但OUTER APPLY是12C或更高,是不是很地道的甲骨文,歷史反正。這裏有一個直LEFT OUTER JOIN版本:

SELECT * 
FROM users a 
LEFT OUTER JOIN 
(
    -- retrieve the list of payments for just those payments that are the maxdate per user 
    SELECT payments.* 
    FROM payments 
    JOIN (SELECT user_id, MAX(date) maxdate 
      FROM payments 
      GROUP BY user_id 
     ) maxpayment_byuser 
     ON maxpayment_byuser.maxdate = payments.date 
      AND maxpayment_byuser.user_id = payments.user_id 
) b ON a.ID = b.user_ID 

如果性能是一個問題,你會發現下面的更高性能,但爲了簡單起見,你會用一個額外的「的maxDate」最終列。

SELECT * 
FROM users a 
LEFT OUTER JOIN 
(
    -- retrieve the list of payments for just those payments that are the maxdate per user 
    SELECT * 
    FROM (
      SELECT payments.*, 
        MAX(date) OVER (PARTITION BY user_id) maxdate 
      FROM payments 
     ) max_payments 
    WHERE date = maxdate 
) b ON a.ID = b.user_ID 
+0

我有12c,所以他的答案有效。但我喜歡你的方法+1 – davejal

1

使用row_number()一個通用的方法是「最高日」或「最近」或類似條件下非常有用:

SELECT 
     * 
FROM users a 
LEFT OUTER JOIN (
     -- determine the row corresponding to "most recent" 
     SELECT 
      payments.* 
      , ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date DESC) is_recent 
     FROM payments 
) b ON a.ID = b.user_ID 
     AND b.is_recent = 1 

(扭轉了子句中的ORDER BY也使「最老」)

+0

你能否解釋一些關於「最古老」的內容。 +1 – davejal

+0

查看'over(partition by ... order by ...)'內的內容''partition''order by'控制哪一行給出值1。如果您顛倒了這個訂單,那麼這個行就會變成一個。 IE瀏覽器。你可以使用「最早的」,或者反過來找到「最早的」。我建議你稍微調整一下,直到mskes感覺到。 –

相關問題