繁体   English   中英

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

[英]Help with hard sql query to update based on daily totals to summary table

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

产品(productID INT PK,...)

产品出价(productID INT,userID INT,创建的DATETIME)

用户(UserID INT PK,TOTALBIDS INT)

每天,用户可以对所有产品进行任意多次的出价。 有一个sql作业会定期运行,该作业会计算用户执行的总出价,并将总出价放入TOTALBIDS字段中。

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

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

例如,第1天第1天总共出价5次#2第2天总共出价15次#3第10次出价

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

在单个查询中有可能吗?

您无需说出要如何处理结果,但可以确定每天选择用户最早的10个出价:

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;

对评论的回应:

您说要将其添加到用户的出价计数中,但是出价计数不是任何表中的列名称。 也许您的意思是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不允许CTE进行UPDATE,而且我也没有确认T-SQL的古怪UPDATE .. FROM是否可以与CTE结合使用。

无论如何,鉴于这似乎是一种更新,您只会很少运行,而绝不会同时运行,因此将我的第一个建议的结果(以您的目的为准)插入临时表并将更新基于此是最明智的选择那。

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语句的时间与下一次任何DML操作再次发生在ProductBids表之间的时间内,Users表中的数据是准确的。

我认为您可以使用汇总+案例声明来执行此操作。 就像是:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM