帮助硬SQL查询根据每日总计更新为汇总表

时间:2009-10-26 03:32:30

标签: sql sql-server sql-server-2005 tsql

以下是我的sql server 2005表结构:

产品(productID INT PK,...)

ProductBids(productID INT,userID INT,Created DATETIME)

用户(UserID INT PK,TOTALBIDS INT)

用户每天都可以根据需要在所有产品上出价。 有一个定期运行的sql作业,它计算用户执行的总出价,并将总数放在TOTALBIDS字段中。

问题是,我们的业务规则要求我们在任何一天内最多只能计算10个出价。

因此,更新查询必须按天分组,如果产品上的用户总出价超过10,那么我们当天只使用10。

e.g。 第1天出价共计5次 第2天共出价15次 第3天出价10次

(假设总共3天) 用户的bidCount为5 + 10 + 10 = 25(不是30)。

这可以在一个查询中实现吗?

3 个答案:

答案 0 :(得分:1)

您没有说出您想对结果做什么,但您当然可以选择用户每天最早的十次出价:

with ProductBidsRanked(productID, userID, Created, rk) as (
  select
    productID, userID, Created,
    row_number() over (
      partition by userID, dateadd(datediff(day,0,Created),0)
      order by Created
    )
)
  select productID, userID, Created
  from ProductBidsRanked
  where rk <= 10

当然,如果你只需要总数,并希望在超过10时将总数替换为10,那就更容易了:

with PartialAgg(userID,countOr10) as (
  select
    userID,
    case when count(*) > 10 then 10 else count(*) end
  from ProductsBids
  group by userID, dateadd(datediff(day,0,Created),0)
)
  select
    userID, sum(countOr10) as BidsAdjusted
  from PartialAgg
  group by userID;

对评论的回应:

您说要将其添加到用户的bidcount中,但bidcount不是任何表中的列名。也许你的意思是TOTALBIDS,例如,如果第二个查询是适合你的查询,你可以这样做:

with PartialAgg(userID,countOr10) as (
  select
    userID,
    case when count(*) > 10 then 10 else count(*) end
  from ProductsBids
  group by userID, dateadd(datediff(day,0,Created),0)
), FullAgg(userID,BidsAdjusted) as (
  select
    userID, sum(countOr10) as BidsAdjusted
  from PartialAgg
  group by userID
)
  update users set
    TOTALBIDS = TOTALBIDS + BidsAdjusted
  from users join FullAgg
  on FullAgg.userID = users.userID

仅供参考,这里有一些SQL Server特定的东西 - ANSI不允许UPDATE使用CTE,我没有确认T-SQL的古怪UPDATE .. FROM可以与CTE结合使用。

在任何情况下,鉴于这似乎是那种不经常运行而且从不同时运行的更新,将我的第一个建议的结果(以您的目的为准)插入到临时表和基础中可能是最明智的你的更新。

答案 1 :(得分:1)

CREATE TABLE dbo.ProductBids(ProductID INT, UserID INT, Created DATETIME);

CREATE TABLE dbo.Users(UserID INT, TotalBids INT);

INSERT dbo.Users(UserID) SELECT 1 UNION ALL SELECT 2;

INSERT dbo.ProductBids 
           SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()-1
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 1, GETDATE()
UNION ALL  SELECT 1, 2, GETDATE()
UNION ALL  SELECT 1, 2, GETDATE()
UNION ALL  SELECT 1, 2, GETDATE();

UPDATE u 
SET TotalBids = x.TotalBids
FROM
(
    SELECT
        UserID, 
        TotalBids = SUM(CASE WHEN c > 10 THEN 10 ELSE c END)
    FROM
    (
        SELECT
            UserID,
            c = COUNT(*)
        FROM
            dbo.ProductBids
        GROUP BY
            UserID,
            DATEADD(DAY, 0, DATEDIFF(DAY, 0, Created))  
    ) AS y
    GROUP BY UserID
) AS x
INNER JOIN dbo.Users AS u
ON x.UserID = u.UserID;

GO

SELECT UserID, TotalBids FROM dbo.Users;

GO

DROP TABLE dbo.Users, dbo.ProductBids;

但是,一般情况下,当您可以从现有数据中获取信息时,我会对存储此总数感到不满。问题是,只有在运行UPDATE语句和下次在ProductBids表中再次发生任何DML操作时,才能保证Users表中的数据准确无误。

答案 2 :(得分:0)

我认为你可以使用聚合+一个case语句来做到这一点。类似的东西:

declare @t table (a int, b int)

insert into @t values(1, 5)
insert into @t values(1, 15)
insert into @t values(1, 10)


select a, sum( case when b>10 then 10 else b end) 
from @t
group by a

case语句确保如果值大于10

,则永远不会添加超过10