Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/84.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 从具有“生效日期”的表构建每日视图_Mysql_Sql_Query Optimization - Fatal编程技术网

Mysql 从具有“生效日期”的表构建每日视图

Mysql 从具有“生效日期”的表构建每日视图,mysql,sql,query-optimization,Mysql,Sql,Query Optimization,我有一个使用开始日期或生效日期的表。表中的值从开始日期起生效,直到它被同一表中具有更晚开始日期的另一个条目覆盖 我的模式和示例数据: CREATE TABLE VALUE_DATA ( `start_date` DATE, `value` FLOAT ); INSERT INTO VALUE_DATA (start_date, value) VALUES ('2015-01-01', 10), ('2015-01-03', 20), ('2015-01-08', 30), ('

我有一个使用开始日期或生效日期的表。表中的值从开始日期起生效,直到它被同一表中具有更晚开始日期的另一个条目覆盖

我的模式和示例数据:

CREATE TABLE VALUE_DATA (
    `start_date` DATE,
    `value` FLOAT
);

INSERT INTO VALUE_DATA (start_date, value) VALUES
('2015-01-01', 10),
('2015-01-03', 20),
('2015-01-08', 30),
('2015-01-09', 15);
生成所需结果的查询:

SELECT date, value
FROM(
    SELECT date, MAX(start_date) as max_start
    FROM (
        select curdate() - INTERVAL (ones.digit + (10 * tens.digit) + (100 * hundreds.digit)) DAY as date
        from (select 0 as digit union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as ones
        cross join (select 0 as digit union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as tens
        cross join (select 0 as digit union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as hundreds
    ) DATE_TABLE
    LEFT JOIN VALUE_DATA ON (DATE_TABLE.date >= VALUE_DATA.start_date)
    WHERE DATE_TABLE.date between '2015-01-01' and '2015-01-10'
    GROUP BY date
) START_DATES LEFT JOIN VALUE_DATA ON (START_DATES.max_start = VALUE_DATA.start_date);
我创建了一个用于模拟问题的


尽管SQLFiddle提供了正确的结果,但我不认为这是最好的方法。我不得不使用的查询有点复杂。我最终希望为这个表创建一个视图,该视图包含每天的正确值,而不管它是否在开始日期,就像Fiddle生成的输出一样,以便更容易地连接到此表。显然,我想确保这一观点是尽可能快。因此,我的问题是,如何改进和优化此查询,以便在这样的视图中使用?

我将通过两个步骤来实现这一点

首先,您需要将时段结束时间带到每条记录,这将使您的行从事件变为时段:

SELECT 
  v1.start_date, 
  v2.start_date as next_start_date, 
  v1.value 
FROM 
  VALUE_DATA v1 LEFT JOIN 
  VALUE_DATA v2 ON 
    v1.start_date < v2.start_date AND 
    NOT EXISTS 
     (SELECT * FROM VALUE_DATA 
      WHERE start_date > v1.start_date and start_date < v2.start_date)

对于这种类型的视图,您需要非常小心。编写一个视图很容易,该视图擅长给出每个记录的有效日期,但在询问哪条记录在某个特定日期有效时速度较慢

因为回答第二个问题需要先回答每个日期的第一个问题,然后放弃失败

以下是确定日期并返回在该日期有效的行的合理方法

CREATE VIEW DAILY_VALUE_DATA AS (
    SELECT
        DATE_TABLE.date,
        VALUE_TABLE.value
    FROM
        DATE_TABLE
    LEFT JOIN
        VALUE_DATA
            ON  VALUE_DATA.start_date = (SELECT MAX(lookup.start_date)
                                           FROM VALUE_DATA lookup
                                          WHERE lookup.start_date <= DATE_TABLE.date
                                        )
);

SELECT * FROM DAILY_VALUE_DATA WHERE date = '2015-08-11'
编辑:

左联接的另一种替代方法是将相关子查询嵌入到SELECT块中。当您只有一个值要从目标表中提取时,这是有效的,但如果您需要从目标表中提取多个值,则效率较低

CREATE VIEW DAILY_VALUE_DATA AS (
    SELECT
        PERSON.id   AS person_id,
        DATE_TABLE.date,
        (SELECT VALUE_DATA.value
           FROM VALUE_DATA
          WHERE VALUE_DATA.start_date <= DATE_TABLE.date
            AND VALUE_DATA.person_id   = PERSON.id
       ORDER BY VALUE_DATA.start_date DESC
          LIMIT 1
        )   AS value
    FROM
        PERSON
    INNER JOIN
        DATE_TABLE
            ON  DATE_TABLE.date >=          PERSON.date_of_birth
            AND DATE_TABLE.date <  COALESCE(PERSON.date_of_death, CURDATE() + 1)
);

SELECT * FROM DAILY_VALUE_DATA WHERE person_id = 1 AND date = '2015-08-11'

如果你真的需要这个,那么在你的数据库中添加一个日历表。似乎你能改进你的查询的唯一方法就是把日期表逻辑带到别处。您可以创建一个表,其中包含域日期维度所需的所有日期。它将简化查询您如何处理结果集?我将使用结果集构建一个视图,该视图将在日期加入到其他查询中。类似于创建视图。。。选择日期时,值为。。。。我需要优化此视图以加入日期字段。在查询特定日期的视图时,这将导致性能非常差。从另一个方向来说,从记录开始是好的,这个记录在哪一天有效?但是,当询问以日期开始并确定哪些记录对该日期有效时,有许多不可搜索的语句。我添加了基于子查询的选项,该子查询使用聚合来查找下一个值,而不是不存在的值;此视图封装了您不想在其他地方处理的逻辑。但这本身就带来了间接费用。没有办法使这样的视图像在自己的查询中嵌入连接条件那样有效。至少是因为它涉及到日历表上的联接,否则就不需要。你似乎有选择;SQL开发人员需要学习如何编写这些相关的子查询联接,或者需要不同的数据模型,或者需要ETL过程来创建持久化的每日事实表,或者需要接受性能影响。
CREATE VIEW DAILY_VALUE_DATA AS (
    SELECT
        DATE_TABLE.date,
        VALUE_TABLE.value
    FROM
        DATE_TABLE
    LEFT JOIN
        VALUE_DATA
            ON  VALUE_DATA.start_date = (SELECT MAX(lookup.start_date)
                                           FROM VALUE_DATA lookup
                                          WHERE lookup.start_date <= DATE_TABLE.date
                                        )
);

SELECT * FROM DAILY_VALUE_DATA WHERE date = '2015-08-11'
CREATE VIEW DAILY_VALUE_DATA AS (
    SELECT
        PERSON.id   AS person_id,
        DATE_TABLE.date,
        VALUE_TABLE.value
    FROM
        PERSON
    INNER JOIN
        DATE_TABLE
            ON  DATE_TABLE.date >=          PERSON.date_of_birth
            AND DATE_TABLE.date <  COALESCE(PERSON.date_of_death, CURDATE() + 1)
    LEFT JOIN
        VALUE_DATA
            ON  VALUE_DATA.start_date = (SELECT MAX(lookup.start_date)
                                           FROM VALUE_DATA lookup
                                          WHERE lookup.start_date <= DATE_TABLE.date
                                            AND lookup.person_id   = PERSON.id
                                        )
);

SELECT * FROM DAILY_VALUE_DATA WHERE person_id = 1 AND date = '2015-08-11'
CREATE VIEW DAILY_VALUE_DATA AS (
    SELECT
        PERSON.id   AS person_id,
        DATE_TABLE.date,
        (SELECT VALUE_DATA.value
           FROM VALUE_DATA
          WHERE VALUE_DATA.start_date <= DATE_TABLE.date
            AND VALUE_DATA.person_id   = PERSON.id
       ORDER BY VALUE_DATA.start_date DESC
          LIMIT 1
        )   AS value
    FROM
        PERSON
    INNER JOIN
        DATE_TABLE
            ON  DATE_TABLE.date >=          PERSON.date_of_birth
            AND DATE_TABLE.date <  COALESCE(PERSON.date_of_death, CURDATE() + 1)
);

SELECT * FROM DAILY_VALUE_DATA WHERE person_id = 1 AND date = '2015-08-11'