The following are my sql server 2005 table structures:
Products (productID INT PK, ...)
ProductBids (productID INT, userID INT, Created DATETIME)
Users(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.
The catch is, our business rules require that we only count up to 10 bids for any given day.
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.
eg day#1 bids 5 times in total day#2 bids 15 times in total day#3 bids 10 times
(assuming on 3 days in total) The bidCount for the user will be 5 + 10 + 10 = 25 (not 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:
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:
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:
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.
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.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.