选择语句以显示具有最低/最高金额的相应用户?

时间:2013-10-03 20:33:43

标签: sql sql-server sql-server-2008 tsql

我想编写一个select语句输出,除其他外,它还包含lowest_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

(一旦我弄清楚要把那些放在那里,那么柱子应该去哪里了!)

3 个答案:

答案 0 :(得分: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 :(得分: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

答案 2 :(得分: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

希望有所帮助!