[英]Help with hard sql query to update based on daily totals to summary table
The following are my sql server 2005 table structures: 以下是我的sql server 2005表结构:
Products (productID INT PK, ...) 产品(productID INT PK,...)
ProductBids (productID INT, userID INT, Created DATETIME) 产品出价(productID INT,userID INT,创建的DATETIME)
Users(UserID INT PK, TOTALBIDS INT) 用户(UserID INT PK,TOTALBIDS INT)
Each day a user can bid as many times as they want on all the products. 每天,用户可以对所有产品进行任意多次的出价。 There is a sql job that is run periodically, that counts the total bids a user has performed and puts the total in the TOTALBIDS field.
有一个sql作业会定期运行,该作业会计算用户执行的总出价,并将总出价放入TOTALBIDS字段中。
The catch is, our business rules require that we only count up to 10 bids for any given day. 问题是,我们的业务规则要求我们在任何一天最多只能计算10个出价。
So the update query has to group by day, and if the total bids for a user on the products goes over 10, we just use 10 for that day. 因此,更新查询必须按天分组,如果用户在产品上的总出价超过10,则当天只使用10。
eg day#1 bids 5 times in total day#2 bids 15 times in total day#3 bids 10 times 例如,第1天第1天总共出价5次#2第2天总共出价15次#3第10次出价
(assuming on 3 days in total) The bidCount for the user will be 5 + 10 + 10 = 25 (not 30). (假设总共3天),该用户的bidCount将为5 + 10 + 10 = 25(而不是30)。
Is this possible in a single query? 在单个查询中有可能吗?
You don't say what you want to do with the results, but you can certainly SELECT the user's earliest ten bids of each day: 您无需说出要如何处理结果,但可以确定每天选择用户最早的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
Of course, if you only need the total, and want to replace the total with 10 when it exceeds 10, that's easier: 当然,如果您只需要总数,并且想在总数超过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;
Response to comment: 对评论的回应:
You say you want to add it to the user's bidcount, but bidcount isn't a column name in any of your tables. 您说要将其添加到用户的出价计数中,但是出价计数不是任何表中的列名称。 Perhaps you meant TOTALBIDS, so for example, if the second query is the one that works for you, you could do something like this:
也许您的意思是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
FYI, there's some SQL Server specific stuff here - ANSI doesn't allow UPDATE with a CTE, and I didn't confirm that T-SQL's quirky UPDATE .. FROM can be used in combination with a CTE. 仅供参考,这里有一些SQL Server特定的东西-ANSI不允许CTE进行UPDATE,而且我也没有确认T-SQL的古怪UPDATE .. FROM是否可以与CTE结合使用。
In any case, given that this seems like the kind of update you would run only infrequently, and never concurrently, it could be wisest to insert the results of my first suggestion (whichever serves your purpose) into a temporary table and base your update on that. 无论如何,鉴于这似乎是一种更新,您只会很少运行,而绝不会同时运行,因此将我的第一个建议的结果(以您的目的为准)插入临时表并将更新基于此是最明智的选择那。
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;
However in general I frown upon storing this total, when you can derive the information from existing data. 但是,总的来说,当您可以从现有数据中获取信息时,我并不喜欢存储总数。 The problem is that the data in the Users table is only guaranteed to be accurate between the time you run the UPDATE statement and the next time any DML operation happens againt the ProductBids table.
问题在于,只能保证在您运行UPDATE语句的时间与下一次任何DML操作再次发生在ProductBids表之间的时间内,Users表中的数据是准确的。
I think you can use an aggregate + a case statement to do this. 我认为您可以使用汇总+案例声明来执行此操作。 Something like:
就像是:
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
The case statement is ensuring that you never add more than 10 if the value is greater than 10 case语句确保如果值大于10,则您永远不会添加超过10
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.