рассчитать сумму для значений в SQL для отображения по названию месяца - PullRequest
5 голосов
/ 25 августа 2011

У меня есть таблица со следующим макетом.

Email Blast Table

EmailBlastId |  FrequencyId | UserId
---------------------------------
1            |   5          |   1
2            |   2          |   1
3            |   4          |   1


Frequency Table

Id | Frequency 
------------
 1 |  Daily
 2 |  Weekly
 3 |  Monthly
 4 |  Quarterly
 5 |  Bi-weekly

Мне нужно создать сетку на моей странице asp.net следующим образом.

Email blasts per month.

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative
-----------------------------------------------------
1        7      6     6     7          6     #xx

Единственноеспособ, которым я могу думать об этом, приведен ниже, для каждого месяца есть описание случая.

select SUM(
        CASE WHEN FrequencyId = 1 THEN 31 
        WHEN FrequencyId = 2 THEN 4
        WHEN FrequencyId = 3 THEN 1
        WHEN FrequencyId = 4 THEN 1
        WHEN FrequencyId = 5 THEN 2 END) AS Jan, 
      SUM(
        CASE WHEN FrequencyId = 1 THEN 28 (29 - leap year)
        WHEN FrequencyId = 2 THEN 4
        WHEN FrequencyId = 3 THEN 1
        WHEN FrequencyId = 4 THEN 0
        WHEN FrequencyId = 5 THEN 2 END) AS Feb, etc etc
FROM EmailBlast 
Group BY UserId

Есть ли другой лучший способ добиться того же?

Ответы [ 4 ]

3 голосов
/ 25 августа 2011

Зависит от того, что вы ищете.Предложение 1 будет отслеживать ваши фактические сообщения электронной почты (с датой :-).

Без фактических дат все, что вы придумали в течение одного месяца, будет одинаковым для каждого месяца.

В любом случае, если вы собираетесь обобщать, то я бы предложил использовать что-токроме целых чисел - как, возможно, с плавающей запятой или десятичных дробей.Поскольку ваши результаты, основанные на таблицах, перечисленных в вашем сообщении, могут только приблизиться к тому, что на самом деле происходит (например, в январе на самом деле 4-1 / 2 недели, а не 4), вы будете иметь смешанные границы ошибок в любом диапазоне месяцев -- Чем хуже, тем дальше ты экстраполируешь.Например, если вы выводите целые 12 месяцев, ваша экстраполяция будет занижена более чем на 4 недели.

Если вы используете числа с плавающей запятой или десятичные дроби, то вы сможете намного приблизиться к тому, что происходит на самом деле.Для начала: найдите общую единицу измерения (я бы предложил использовать «день») Например, 1 месяц = ​​365/12 дней;1 квартал = 365/4 дня;1 2 недели = 14 дней;и т. д.

Если вы это сделаете - то у вашего пользователя, у которого был один 1 на квартал, фактически было 1 на 91,25 дней;1 раз в неделю превращается в 1 раз в 7 дней;1 на BiWeek превращается в 1 на 14 дней.

**EDIT** -- Incidentally, you could store the per-day value in your reference table, so you didn't have to calculate it each time.  For example:
Frequency Table

Id | Frequency         | Value
-------------------------------
 1 |  Daily            | 1.0
 2 |  Weekly           | .14286
 3 |  Monthly          | .03288
 4 |  Quarterly        | .01096
 5 |  Once in 2 weeks  | .07143

Теперь по математике - (1 / 91.25 + 1/7 + 1/14) нужен общий деном (например, 91.25 * 14), поэтому он становится (14 / 1277.5 + 182.5 / 1277.5+ 91,25 / 1277,5).

Это в сумме составляет 287,75 / 1277,5 или 0,225 электронных писем в день.

Поскольку в месяце 365/12 дней, умножьте на 0,225 * (365/12), чтобы получать 6,85 электронных писем в месяц.

Ваш результат будет выглядеть примерно так:

Email blasts per month.

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative
-----------------------------------------------------
1        6.85  6.85  6.85  6.85     6.85     #xx

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

Также стоит отметить, что после того, как ВЫ выполните экстраполяцию (и границы ошибок, которые влекут за собой),Ваши потребители этого вывода будут делать СВОЮ экстраполяцию не на необработанные данные, а на ваш вывод.Так что это своего рода двойная граница ошибок.Чем точнее вы будете на раннем этапе, тем надежнее будут эти цифры на каждом последующем уровне.

3 голосов
/ 25 августа 2011

Я думаю, что у вас будет гораздо более сложная логика.Конечно, у Яна 31 день ... но в феврале нет ... и февраль меняется в зависимости от года.Затем отправляются сообщения электронной почты даже по выходным и праздничным дням или пропускаются определенные дни по разным причинам ... Если это так, то число рабочих дней в данном месяце меняется каждый год.

Следующее числополные недели в данном месяце также меняются из года в год.Что происходит с этими дополнительными 4 с половиной недели?Они идут на текущий или следующий месяц?Какой метод вы используете, чтобы определить это?Пример того, насколько это сложно, читается: http://en.wikipedia.org/wiki/ISO_week_date В частности, в той части, где говорится о первой неделе, которая на самом деле имеет 9 различных определений.

Обычно я не из тех, кто это говорит,но вам может быть лучше написать это с помощью обычного кода вместо SQL-запроса.Просто введите 'select * from emailblast, где userid = xxx' и преобразуйте его, используя различные методы кода.

2 голосов
/ 25 августа 2011

Это за какой-то данный год?Я предполагаю, что вы хотите график на текущий год.Если вам нужен будущий год, вы всегда можете изменить DECLARE @now, чтобы указать любую будущую дату.

«Один раз в 2 недели» (обычно известный как «раз в две недели») не вписывается в ежемесячные сегменты.(кроме февраля в не високосный год).Должно ли это быть изменено на «Дважды в месяц»?

Кроме того, почему бы не сохранить коэффициент в таблице частот, добавив столбец с именем «PerMonth»?Тогда вам нужно иметь дело только с ежедневными и ежеквартальными делами (и это произвольный выбор, что это произойдет только в январе, апреле и т. Д.?).

Предполагая, что что-то из этого является гибким, я хотел бы предложить следующее, предполагая это очень незначительное изменение в схеме таблицы:

USE tempdb;
GO

CREATE TABLE dbo.Frequency 
(
    Id INT PRIMARY KEY,
    Frequency VARCHAR(32),
    PerMonth TINYINT
);

CREATE TABLE dbo.EmailBlast 
(
    Id INT,
    FrequencyId INT,
    UserId INT
);

И этот пример данных:

INSERT dbo.Frequency(Id, Frequency, PerMonth)
  SELECT 1, 'Daily', NULL
  UNION ALL SELECT 2, 'Weekly', 4
  UNION ALL SELECT 3, 'Monthly', 1
  UNION ALL SELECT 4, 'Quarterly', NULL
  UNION ALL SELECT 5, 'Twice a month', 2;

INSERT dbo.EmailBlast(Id, FrequencyId, UserId)
  SELECT 1, 5, 1
  UNION ALL SELECT 2, 2, 1
  UNION ALL SELECT 3, 4, 1;

Мы можем выполнить это, используя очень сложный запрос (но нам не нужно жестко кодировать эти номера месяцев):

DECLARE @now DATE = CURRENT_TIMESTAMP;
DECLARE @Jan1 DATE = DATEADD(MONTH, 1-MONTH(@now), DATEADD(DAY, 1-DAY(@now), @now));

WITH n(m) AS 
(
    SELECT TOP 12 m = number
        FROM master.dbo.spt_values
        WHERE number > 0 GROUP BY number
),
months(MNum, MName, StartDate, NumDays) AS
(    SELECT m, mn = CONVERT(CHAR(3), DATENAME(MONTH, DATEADD(MONTH, m-1, @Jan1))),
        DATEADD(MONTH, m-1, @Jan1), 
        DATEDIFF(DAY, DATEADD(MONTH, m-1, @Jan1), DATEADD(MONTH, m, @Jan1))
    FROM n
),
grp AS
(
    SELECT UserId, MName, c = SUM (
        CASE x.Id WHEN 1 THEN NumDays
            WHEN 4 THEN CASE WHEN MNum % 3 = 1 THEN 1 ELSE 0 END
            ELSE x.PerMonth END )
    FROM months CROSS JOIN (SELECT e.UserId, f.* 
        FROM EmailBlast AS e 
        INNER JOIN Frequency AS f
        ON e.FrequencyId = f.Id) AS x
    GROUP BY UserId, MName
),
cumulative(UserId, total) AS
(
    SELECT UserId, SUM(c)
      FROM grp GROUP BY UserID
),
pivoted AS
(
    SELECT * FROM (SELECT UserId, c, MName FROM grp) AS grp 
    PIVOT(MAX(c) FOR MName IN (
        [Jan],[Feb],[Mar],[Apr],[May],[Jun],[Jul],[Aug],[Sep],[Oct],[Nov],[Dec])
    ) AS pvt
)
SELECT p.*, c.total 
    FROM pivoted AS p
    LEFT OUTER JOIN cumulative AS c
    ON p.UserId = c.UserId;

Результаты:

UserId  Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec total
1       7   6   6   7   6   6   7   6   6   7   6   6   76

Очиститьup:

DROP TABLE dbo.EmailBlast, dbo.Frequency;
GO

На самом деле предложенное мной изменение схемы на самом деле мало что дает, а просто экономит две дополнительные ветви CASE внутри grp CTE.Арахис, общий.

2 голосов
/ 25 августа 2011

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

Вы можете структурировать это так:

MONTH_NAME
DAILY_COUNT
WEEKLY_COUNT
MONTHLY_COUNT
QUARTERLY_COUNT
BIWEEKLY_COUNT

Запись для JAN будет

JAN
31
4
1
1
2

Или вы можете структурировать это так:

MONTH_NAME 
FREQUENCY_ID 
EMAIL_COUNT

и иметь несколько записей для каждого месяца:

JAN   1   31
JAN   2    4
JAN   3    1
JAN   4    1
JAN   5    2

Я позволю вам выяснить, является ли логика для получения этого лучше, чем ваша структура CASE.

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