mysql - 跟踪访问与动态目标的数据库

标签 mysql sql database database-design

我正在设计一个新的数据库来跟踪帐户实现的访问量与月度目标。最终报告应按开始/结束日期和一个帐户要求,以显示其间的月份以及每月目标和访问总和。 当我知道帐户数量超过 10,000 并且目标应该每月更改或不更改唯一更改的帐户目标(即每个目标将有开始和结束日期。如果没有结束日期那么目标总是有效的)。此时我迷路了,需要帮助

为简单起见,我假设我有一个包含日期周期和最简单情况的表格,如下所示

accounts
+----+---------+ 
|id  | name    |
+----+---------+ 
| 1  | account1|
| 2  | account2|
+----+---------+

targets
+---+------------+------------+-----------+----------------+
|id | account_id | start_date | end_date  | monthly_target |
+---+------------+------------+-----------+----------------+
|1  | 1          | 1-1-2016   | 31-1-2016 | 5              |
|2  | 1          | 1-2-2016   | 31-5-2016 | 4              |
|3  | 1          | 1-7-2016   | null      | 7              |
|4  | 2          | 1-1-2016   | null      | 10             |
+---+------------+------------+-----------+----------------+

visits
+---+-----------+------------+
|id | date      | account_id |
+----------------------------+
|1  | 15-1-2016 | 1          |
|2  | 20-1-2016 | 1          |
|3  | 10-5-2016 | 1          |
|3  | 20-5-2016 | 1          |
|4  | 20-5-2016 | 2          |
+---+-----------+------------+

calendar (Optional)
----------+----------+
|start    | end      |
----------+----------+
|1-1-2016 | 31-1-2016|
|1-2-2016 | 29-2-2016|
|1-3-2016 | 31-3-2016|
|1-4-2016 | 30-4-2016|
|1-5-2016 | 31-5-2016|
|1-6-2016 | 30-6-2016|
|1-7-2016 | 31-7-2016|
|1-8-2016 | 31-7-2016|
+---------+----------+

2016 年 1 月 4 日至 2016 年 7 月 31 日 account1 覆盖范围的预期报告

+---------+-----------+--------+----+
|start    | end       | target | sum|
+---------+-----------+--------+----+
|1-4-2016 | 30-4-2016 | 4      | 0  |
|1-5-2016 | 31-5-2016 | 4      | 2  |
|1-6-2016 | 30-6-2016 | 0      | 0  |
|1-7-2016 | 31-7-2016 | 7      | 0  |
+---------+-----------+--------+----+

如果它导致问题,我可以接受更改我的初始设计,但假设目标表的设计是系统管理员最实用的设计。

我需要生成最终报告所需的 SQL 方面的帮助。

最佳答案

我修改了 targets 中的日期范围以具有明确的结束日期,即使这意味着年底。这样,避免空值,sql 就可以正常运行了。它还使用 ISO 8601日期标准。它是在一个带有 3 个参数的存储过程中实现的:account_id、开始和结束日期。

别名 v,派生表,防止重复计数,而不是针对访问表的平整 LEFT JOIN。例如,如果没有该策略,2 将是错误的 7。所以它使用了 LAST_DAY()功能。

架构:

create table accounts
(   id int not null,
    name varchar(100) not null
);
insert accounts values
(1,'account1'),
(2,'account2');

-- drop table targets;
create table targets
(   id int not null,
    account_id int not null,
    start_date date not null,
    end_date date not null,
    monthly_target int not null
);
-- truncate targets;
insert targets values
(1,1,'2016-01-01','2016-01-31',5),
(2,1,'2016-02-01','2016-05-31',4),
(3,1,'2016-07-01','2016-12-31',7),
(4,2,'2016-01-01','2016-12-31',10);

create table visits
(   id int not null,
    date date not null,
    account_id int not null
);
-- truncate visits;
insert visits values
(1,'2016-01-15',1),
(2,'2016-01-20',1),
(3,'2016-05-10',1),
(4,'2016-05-20',1),
(5,'2016-05-20',2);


create table calendar
(   start date not null,
    end date not null
);
insert calendar values
('2016-01-01','2016-01-31'),
('2016-02-01','2016-02-29'),
('2016-03-01','2016-03-31'),
('2016-04-01','2016-04-30'),
('2016-05-01','2016-05-31'),
('2016-06-01','2016-06-30'),
('2016-07-01','2016-07-31'),
('2016-08-01','2016-08-31'),
('2016-09-01','2016-09-30'),
('2016-10-01','2016-10-31'),
('2016-11-01','2016-11-30'),
('2016-12-01','2016-12-31');

存储过程:

DROP PROCEDURE IF EXISTS uspGetRangeReport007;
DELIMITER $$
CREATE PROCEDURE uspGetRangeReport007
(   p_account_id INT,
    p_start DATE,
    p_end DATE
)
BEGIN
    SELECT c.start,c.end,
    IFNULL(t.monthly_target,0) as target,
    -- IFNULL(sum(v.id),0) as visits
    IFNULL(v.theCount,0) as visits
    FROM calendar c
    LEFT JOIN targets t
    ON account_id=p_account_id 
    AND c.start BETWEEN t.start_date AND t.end_date
    AND c.end BETWEEN t.start_date AND t.end_date
    LEFT JOIN 
    (   SELECT LAST_DAY(date) as lastDayOfMonth,
        count(id) as theCount
        FROM VISITS
        WHERE account_id=p_account_id
        GROUP BY LAST_DAY(date)
    ) v
    ON v.lastDayOfMonth BETWEEN c.start AND c.end
    WHERE c.start BETWEEN p_start AND p_end
    AND c.end BETWEEN p_start AND p_end
    GROUP BY c.start,c.end,t.monthly_target
    ORDER BY c.start;
END;$$
DELIMITER ;

测试:

call uspGetRangeReport007(1,'2016-04-01','2016-07-31'); 
+------------+------------+--------+--------+
| start      | end        | target | visits |
+------------+------------+--------+--------+
| 2016-04-01 | 2016-04-30 |      4 |      0 |
| 2016-05-01 | 2016-05-31 |      4 |      2 |
| 2016-06-01 | 2016-06-30 |      0 |      0 |
| 2016-07-01 | 2016-07-31 |      7 |      0 |
+------------+------------+--------+--------+

关于mysql - 跟踪访问与动态目标的数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39441667/

相关文章:

php - 评论区sql注入(inject)

sql - 如何从 SQL Server 表中检索特定行?

sql - 如何删除 sqlite 中的重复行,其中包括 ID 的每一列都是重复的?

database - 位图索引有何帮助?

php - 如何将自托管的 wordpress 和 live 站点的已安装插件升级到最新可用版本而不会遇到任何问题?

php - 从 PHP MySQL 列中的所有行读取数据

mysql - 数据库 - 违反唯一约束的处理

php - 是否可以针对大型 Web 表单中的每个输入字段更改触发查询?

mysql - 复杂的mysql查询问题

mysql - 使用新列中更新记录的历史管理员更新表