3

国のテーブルと天気のテーブルの2つのテーブルがあります。過去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;

これでうまくいくと思いますが、パフォーマンスに深刻な問題があります。私が書く必要のある実際のクエリは、さまざまなデータ(まったく同じ概念)と数百万の行を扱います。このクエリは、指数関数的に遅くなるようです。

誰かアドバイスはありますか?

私が持っていたもう1つのアイデアは、JOINを(weather.day_numで注文しながら)上位15レコードのみを取得するように制限することでしたが、JOIN内でこれを行う方法が見つかりませんでした(可能な場合)。

4

4 に答える 4

0

国のテーブルと天気のテーブルの2つのテーブルがあります。過去15日以内に雨が降らなかった国の名前をすべて取得したいと思います。

どうぞ:

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

計画英語:各国について、指定された日数よりも新しい雨の日があるかどうかを確認します。ある場合は、結果から国を削除します。

215日前の日番号に置き換えます。{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;

実用的なSQLFiddleの例はここにあります。

于 2012-07-30T15:35:26.710 に答える
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;

編集>>あなたのケースで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
    )

ここでは、各国の最小日数を一時テーブルに保存します。それが役に立てば幸い...

于 2012-07-30T15:04:45.873 に答える
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

テーブルにインデックスに関する情報を含めていませんが、発生しているパフォーマンスの問題は、countriesnameフィールドのgroupbyに関連していると思います。その列にインデックスが付けられていない場合は、パフォーマンスの問題が確実に説明されます。

そうは言っても、これはおそらく内部結合ではなくサブクエリを必要とする状況です。私はこのようにクエリを書きたくなるでしょう:

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 に答える