2013-10-03 39 views
1

我想寫一個select語句輸出,其中包含一個least_bid和highest_bid列。我知道該怎麼做,但是我想要的是將用戶(user_firstname和user_lastname合併到自己的列中)顯示爲lowest_bidder和highest_bidder。我至今是:選擇語句以顯示最低/最高金額的相應用戶?

select item_name, item_reserve, count(bid_id) as number_of_bids, 
     min(bid_amount) as lowest_bid, ???, max(big_amount) as highest_bid, 
     ??? 
     from vb_items 
     join vb_bids on item_id=bid_item_id 
     join vb_users on item_seller_user_id=user_id 
     where bid_status = ‘ok’ and 
      item_sold = ‘no’ 
     sort by item_reserve 

(!該供應商的是其中列應該去,一旦我弄清楚要放什麼東西在那裏)

+0

而不是'item_seller_user_id'是否有出價競標'user_id'的列? – Laurence

+0

有一個item_buyer_user_id。 –

+0

如何判斷在出價與用戶之間沒有關聯的情況下誰出價最低? – Laurence

回答

1

這似乎很好地使用窗口函數。我假設了一列vb_bids.bid_user_id。如果有一個出價和用戶之間沒有聯繫,你不能回答這個問題

With x as (
    Select 
     b.bid_item_id, 
     count(*) over (partition by b.bid_item_id) as number_of_bids, 
     row_number() over (
      partition by b.bid_item_id 
      order by b.bid_amount desc 
     ) as high_row, 
     row_number() over (
      partition by b.bid_item_id 
      order by b.bid_amount 
     ) as low_row, 
     b.bid_amount, 
     u.user_firstname + ' ' + u.user_lastname username 
    From 
     vb_bids b 
      inner join 
     vb_users u 
      on b.bid_user_id = u.user_id 
    Where 
     b.bid_status = 'ok' 
) 
Select 
    i.item_name, 
    i.item_reserve, 
    min(x.number_of_bids) number_of_bids, 
    min(case when x.low_row = 1 then x.bid_amount end) lowest_bid, 
    min(case when x.low_row = 1 then x.username end) low_bidder, 
    min(case when x.high_row = 1 then x.bid_amount end) highest_bid, 
    min(case when x.high_row = 1 then x.username end) high_bidder 
From 
    vb_items i 
     inner join 
    x 
     on i.item_id = x.bid_item_id 
Where 
    i.item_sold = 'no' 
Group By 
    i.item_name, 
    i.item_reserve 
Order By 
    i.item_reserve 

Example Fiddle

1

爲了獲得用戶,我將這些聚合物分解成它們自己的表格,並通過item_id加入它們,並通過衍生值進行過濾,最小值或最大值爲bid_amount。我本來可以第三次加入vb_bids,並保留了聚合函數,但這會是多餘的。

如果您對同一商品有兩個完全相同金額的低出價,則此操作將失敗,因爲連接位於bid_amount。如果你使用這個,那麼你會想要在vb_bids上創建一個索引,覆蓋bid_amount

select item_name, item_reserve, count(bid_id) as number_of_bids, 
    low_bid.bid_amount as lowest_bid, low_user.first_name + ' ' + low_user.last_name, 
    high_bid.bid_amount as highest_bid, high_user.first_name + ' ' + high_user.last_name 
    from vb_items 
    join vb_bids AS low_bid on item_id = low_bid.bid_item_id 
     AND low_bid.bid_amount = (
     SELECT MIN(bid_amount) 
     FROM vb_bids 
     WHERE bid_item_id = low_bid.bid_item_id) 
    join vb_bids AS high_bid on item_id = high_bid.bid_item_id 
     AND high_bid.bid_amount = (
     SELECT MAX(bid_amount) 
     FROM vb_bids 
     WHERE bid_item_id = high_bid.bid_item_id) 
    join vb_users AS low_user on low_bid.user_id=user_id 
    join vb_users AS high_user on high_bid.user_id=user_id 
    where bid_status = ‘ok’ and 
     item_sold = ‘no’ 
    group by item_name, item_reserve, 
    low_bid.bid_amount, low_user.first_name, low_user.last_name, 
    high_bid.bid_amount, high_user.first_name, high_user.last_name 
    order by item_reserve 
1

我使用通用表表達式(CTE的)像的情況下的大風扇此,由於以下優點:

  • 分離的邏輯的不同部分,從而增加可讀性和
  • 降低複雜性(例如,需要GROUP BY大量的字段,或者重複相同的加入多次。)

所以,我建議的方法是這樣的:

-- semi-colon must precede CTE 
; 

-- collect bid info 
WITH item_bids AS (
    SELECT 
    i.item_id, i.item_name, i.item_reserve, b.bid_id, b.bid_amount, 
    (u.first_name + ' ' + u.last_name) AS bid_user_name 
    FROM vb_items i 
    JOIN vb_bids b ON i.item_id = b.bid_item_id 
    JOIN vb_users u ON b.user_id = u.user_id 
    WHERE b.bid_status = 'ok' 
    AND i.item_sold = 'no' 
), 

-- group bid info 
item_bid_info AS ( 
    SELECT item_id, item_name, item_reserve 
    COUNT(bid_id) AS number_of_bids, MIN(bid_amount) AS lowest_bid, MAX(bid_amount) AS highest_bid 
    FROM item_bids 
    GROUP BY item_id, item_name, item_reserve 
) 

-- assemble final result 
SELECT 
    bi.item_name, bi.item_reserve, bi.number_of_bids, 
    bi.low_bid, low_bid.bid_user_name AS low_bid_user, 
    bi.high_bid, high_bid.bid_user_name AS high_bid_user 
FROM item_bid_info bi 
    JOIN item_bids AS low_bid ON bi.lowest_bid = low_bid.bid_amount AND bi.item_id = low_bid.bid_item_id 
    JOIN item_bids AS high_bid ON bi.lowest_bid = high_bid.bid_amount AND bi.item_id = high_bid.bid_item_id 
ORDER BY bi.item_reserve; 

請注意,整個SQL語句(從WITH開始直到ORDER BY之後的最後一個分號)是單個語句,並由優化器評估。 (有些人認爲每個部分都是單獨評估的,就像臨時表一樣,然後所有行都在最後一步結合在一起,但這不是它的工作原理,CTE和子查詢一樣高效。)

另請注意,此方法對出價金額做了JOIN,所以如果單個商品的出價相同,則會失敗。 (無論如何,這似乎應該是一個無效狀態,但是,對吧?)你也可能因有效率的擔憂:

  • 你的表的大小
  • 查詢是否可以使用索引

您可以通過包括唯一約束,解決這兩個問題(其中有索引外鍵bid_item_id還有的附加優勢;總是一個很好的做法):

ALTER TABLE [dbo].[vb_bids] ADD CONSTRAINT [UK_vbBids_item_amount] 
UNIQUE NONCLUSTERED (bid_item_id, bid_amount) 
GO 

希望幫助!