3

我有两个表,一个国家表和一个天气表。我想检索过去 15 天内未下雨的所有国家/地区的名称。

天气表有一个名为“DayNum”的列,它从 1 -> 无穷大开始,并且每天增加 1,它是唯一的。该表还有一个名为“Rain”的列,它只是一个 0 或 1 的布尔值。

此外,并非所有国家/地区都是在同一天添加的,因此每个国家/地区的最大 DayNum 会有所不同。

下面的表格示例(为了便于阅读,数据被截断):

国家:

    ID     Name
     1      USA
     2      Cananda
     3      Brazil

天气

    ID    Country_id    DayNum    Rain
     1        1           1         0
     2        1           2         0
     3        1           3         1

这是我目前对查询的尝试(已经为此工作了好几天):

    SELECT countries.name, weather.daynum
    FROM countries INNER JOIN weather ON countries.id = weather.country_id
    GROUP BY countries.name
    HAVING weather.daynum > (MAX(weather.day_num) - 15) AND SUM(weather.rain) = 0;

认为这应该可行,但我遇到了严重的性能问题。我需要编写的实际查询处理不同的数据(完全相同的概念)和数百万行。此查询似乎以指数速度变慢。

任何人都可以提供任何建议吗?

我的另一个想法是以某种方式将 JOIN 限制为仅获取前 15 条记录(同时按 weather.day_num 排序),但我还没有找到在 JOIN 中执行此操作的方法(如果可能的话)。

4

4 回答 4

0

你对雨量不感兴趣,只关心它是否存在,所以...

select * from countries
left join
(
        select weather.country_id 
        from weather 
            inner join 
            (select country_id, MAX(daynum) as maxdaynum from weather group by country_id) maxday
                on weather.country_id = maxday.country_id
                and weather.daynum>maxday.maxdaynum-3
                where rain=1
        ) rainy
on countries.id = rainy.country_id
where country_id is null    

我想你已经适当地索引了你的表

于 2012-07-30T14:20:33.873 回答
0

您没有在表格中包含有关索引的任何信息,但我打赌您遇到的性能问题与国家名称字段中的 group by 有关。如果该列未编入索引,它肯定会解释您的性能问题。

话虽如此,这种情况可能需要子查询而不是内部联接。我很想以这种方式编写查询:

SELECT countries.id, countries.name 
FROM countries 
INNER JOIN 
(
    SELECT country_id 
    FROM weather 
    GROUP BY country_id 
    HAVING weather.daynum > (MAX(weather.day_num) - 15) AND SUM(weather.rain) = 0 
) AS weather
ON weather.country_id = countries.id;
于 2012-07-30T14:21:37.947 回答
0

也许您可以使用一个简单的变量来存储所需的最小天数?我不是 mySQL 开发人员,但我认为类似的东西可以解决问题:

SELECT @minDaynum := (MAX(daynum)-15) FROM weather;

SELECT DISTINCT countries.name
FROM weather
INNER JOIN countries ON weather.country_id = countries.id
WHERE
    weather.daynum >= @minDaynum AND
    weather.rain = 1;

编辑 >> 如果只有一个变量不适用于您的情况,也许可以尝试使用临时表来加快速度(虽然不确定 mysql 中临时表的性能是否真的很好......):

CREATE TEMPORARY TABLE min_daynums (country_id int, country_name, min_daynum int);
INSERT INTO min_daynum 
    SELECT countries.id, countries.name, MAX(weather.daynum)-15 
    FROM weather 
    INNER JOIN countries ON countries.id = weather.country_id
    GROUP BY countries.id, countries.name

SELECT min_daynums.country_name
FROM min_daynums
WHERE
    EXISTS(
        SELECT 1
        FROM weather
        WHERE
            weather.country_id = min_daynums.country_id
            and weather.daynum >= min_daynums.min_daynum
            and weather.rain = 1
    )

在这里,我只是将每个国家/地区的 min daynum 存储在临时表中。希望能帮助到你...

于 2012-07-30T15:04:45.873 回答
0

我有两个表,一个国家表和一个天气表。我想检索过去 15 天内未下雨的所有国家/地区的名称。

干得好:

SELECT * FROM Country
WHERE
    NOT EXISTS (
        SELECT * FROM Weather
        WHERE
            Rain = 1
            AND DayNum >= 2
            AND Country_id = Country.ID
    );

在计划英语中:对于每个国家,检查是否有比给定天数新的下雨天。如果有,则从结果中消除该国家/地区。

替换2为 15 天前的日期。指数{Country_id, DayNum, Rain}表现不错。不幸的是,MySQL 不太可能以最佳方式执行此查询,但只有这么多国家/地区,因此嵌套循环应该不会太糟糕,因为 DBMS 应该能够将内部查询作为单个索引搜索执行。

或者,考虑将其重写为 JOIN,例如:

SELECT Country.*
FROM Country LEFT JOIN Weather
    ON Country_id = Country.ID
    AND Rain = 1
    AND DayNum >= 2
GROUP BY Country.ID, Country.Name
HAVING MAX(Rain) IS NULL OR MAX(Rain) = 0;

一个有效的 SQL Fiddle 示例在这里

于 2012-07-30T15:35:26.710 回答