5


3

クエリの最適化で助けが必要

「incoming tours(id、name)」と「incoming_tours_cities(id_parrent、id_city)」の2つのテーブルがあります

最初のテーブルの「id」は一意であり、最初のテーブルの一意の行ごとに、2番目のテーブルの「id_city」のリストがあります(つまり、 2番目のテーブルの「id_parrent」は、最初のテーブルの「id」と同じです)

例えば

incoming_tours

|--id--|------name-----|
|---1--|---first_tour--|
|---2--|--second_tour--|
|---3--|--thirth_tour--|
|---4--|--hourth_tour--|

incoming_tours_cities

|-id_parrent-|-id_city-|
|------1-----|---4-----|
|------1-----|---5-----|
|------1-----|---27----|
|------1-----|---74----|
|------2-----|---1-----|
|------2-----|---5-----|
........................

つまり、 first_tour`には都市のリストがあります-( "4"、 "5"、 "27"、 "74") `

そして、 second_tour`には都市のリストがあります-( "1"、 "5") `

'' '' '

私には2つの値があると仮定しましょう-4`と 74`:

ここで、最初のテーブルからすべての行を取得する必要があります。ここで、* both の値は都市のリストにあります。 つまり、 first_tour *のみを返す必要があります(4および74が都市のリストにあるため)

だから、私は次のクエリを書いた

SELECT t.name
FROM `incoming_tours` t
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id
AND tc0.id_city = '4'
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id
AND tc1.id_city = '74'

そしてそれはうまくいきます。

しかし、クエリを動的に生成し、結合の数が大きい(約15)場合、クエリの速度が低下します。

i.e. 私が走ろうとするとき

SELECT t.name
FROM `incoming_tours` t
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id
AND tc0.id_city = '4'
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id
AND tc1.id_city = '74'
.........................................................
JOIN `incoming_tours_cities` tc15 ON tc15.id_parrent = t.id
AND tc15.id_city = 'some_value'

クエリの実行は 45s(テーブルにインデックスを設定しているにもかかわらず)

最適化するにはどうすればよいですか?

どうもありがとう

6 Answer


6


SELECT t.name
FROM incoming_tours t INNER JOIN
  ( SELECT id_parrent
    FROM incoming_tours_cities
    WHERE id IN (4, 74)
    GROUP BY id_parrent
    HAVING count(id_city) = 2) resultset
  ON resultset.id_parrent = t.id

ただし、合計都市数の数を変更する必要があります。


2


SELECT name
FROM (
      SELECT DISTINCT(incoming_tours.name) AS name,
             COUNT(incoming_tours_cities.id_city) AS c
      FROM incoming_tours
           JOIN incoming_tours_cities
                ON incoming_tours.id=incoming_tours_cities.id_parrent
      WHERE incoming_tours_cities.id_city IN(4,74)
            HAVING c=2
      ) t1;

検索する id_city`のカウントに合わせて c = 2`を変更する必要がありますが、クエリを動的に生成するので、問題はないはずです。


1


私はこれがうまくいくと確信していますが、それが最適であるかどうかはあまりわかりません。

SELECT * FROM incoming_tours
WHERE
id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=4)
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=74)
...
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=some_value)


0


ただのヒント。 「WHERE」句で「IN」演算子を使用する場合、制約を尊重しないツアーの実行中に、演算子「AND」の短絡によって不要な「JOIN」が削除されることを期待できます。


0


そのクエリを行う奇妙な方法のようです、ここに

SELECT t.name FROM `incoming_tours` as t WHERE t.id IN (SELECT id_parrent FROM `incoming_tours_cities` as tc WHERE tc.id_city IN ('4','74'));

私はそれをしますが、テストされていません…​

EDIT:サブクエリにテーブルエイリアスを追加しました


0


CTEを使用してこのクエリを記述し、クエリにテストデータを含めています。 代わりに、実際のテーブルを照会するように変更する必要があります。 大規模なデータセットでどのように機能するかわからない…​

Declare @numCities int = 2

;with incoming_tours(id, name) AS
(
    select 1, 'first_tour' union all
    select 2, 'second_tour' union all
    select 3, 'third_tour' union all
    select 4, 'fourth_tour'
)
, incoming_tours_cities(id_parent, id_city) AS
(
    select 1, 4 union all
    select 1, 5 union all
    select 1, 27 union all
    select 1, 74 union all
    select 2, 1 union all
    select 2, 5
)
, cityIds(id_city) AS
(
    select 4
    union all select 5
    /* Add all city ids you need to check in this table */
)
, common_cities(id_city, tour_id, tour_name) AS
(
    select c.id_city,  it.id, it.name
    from cityIds C, Incoming_tours_cities tc, incoming_tours it
    where C.id_city = tc.id_city
    and tc.id_parent = it.id
)
, tours_with_all_cities(id_city) As
(
    select tour_id from common_cities
    group by tour_id
    having COUNT(id_city) = @numCities
)
select it.name from incoming_tours it, tours_with_all_cities tic
where it.id = tic.id_city