Помощь с сложным SQL-запросом для обновления на основе ежедневных итогов в сводную таблицу - PullRequest
0 голосов
/ 26 октября 2009

Ниже приведены структуры таблиц моего сервера sql 2005:

Продукты (productID INT PK, ...)

ProductBids (productID INT, userID INT, Created DATETIME)

Пользователи (UserID INT PK, TOTALBIDS INT)

Каждый день пользователь может делать ставки столько раз, сколько он хочет, на все товары. Периодически выполняется задание sql, в котором подсчитываются итоговые ставки, которые сделал пользователь, и оно помещается в поле TOTALBIDS.

Суть в том, что наши бизнес-правила требуют, чтобы мы учитывали только 10 заявок в любой день.

Таким образом, запрос на обновление должен группироваться по дням, и если общее количество заявок для пользователей на продукты превышает 10, мы просто используем 10 для этого дня.

например. 1 день - 5 раз. Второй день - 15 раз. день # 3 ставки 10 раз

(при условии 3 дня) Ставка для пользователя будет равна 5 + 10 + 10 = 25 (не 30).

Возможно ли это в одном запросе?

Ответы [ 3 ]

1 голос
/ 26 октября 2009

Вы не говорите, что хотите сделать с результатами, но вы, безусловно, можете ВЫБРАТЬ самые ранние десять заявок пользователя в день:

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 не допускает UPDATE с CTE, и я не подтвердил, что изворотливое UPDATE T-SQL ... FROM можно использовать в сочетании с CTE.

В любом случае, учитывая, что это похоже на тот тип обновления, который вы запускаете только нечасто и никогда одновременно, может быть целесообразно вставить результаты моего первого предложения (в зависимости от того, что вам нужно) во временную таблицу и базу Ваше обновление на этом.

1 голос
/ 26 октября 2009
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.

0 голосов
/ 26 октября 2009

Я думаю, что вы можете использовать агрегат + оператор 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...