2017-10-03 8 views
0

私は次のような問題のように簡略化することができ抽象的な問題を抱えている:この結果を得るためにMySQL SELECTクエリを書くには?

SELECT * FROM persons; 
+----+-------+--------+ 
| id | name | fan_of | 
+----+-------+--------+ 
| 1 | alice |  2 | 
| 2 | bob |  4 | 
| 3 | carol |  1 | 
| 4 | dave |  3 | 
| 5 | bob |  2 | 
+----+-------+--------+ 

SELECT * FROM names; 
+----+-------+--------+ 
| id | name | active | 
+----+-------+--------+ 
| 1 | alice |  1 | 
| 2 | bob |  1 | 
| 3 | carol |  0 | 
| 4 | dave |  1 | 
+----+-------+--------+ 

すべての人(行を次のように私たちが見て二つのテーブルpersonsnamesを持っていると仮定persons)テーブルは、それ自体または他の人のファンである(列の他の人idによって表される)。 namesテーブルには、アクティブまたは非アクティブの名前が含まれています。与えられたために

kオフセット、私は彼らの名前としてk+1番目のアクティブな名前を持っているか、その彼らのファンとして、これらの人々のいずれかを持っている人(personsの行を)SELECTしたいです。たとえば、オフセットが1の場合、2番目のアクティブな名前はbobです。したがって、名前がbobのすべての人と、これらのbobをファンとして持つ人を選択したいとします。この例では、 id = 4。これは私が結果を持ってしたいことを意味します

+----+------+--------+ 
| id | name | fan_of | 
+----+------+--------+ 
| 2 | bob |  4 | 
| 4 | dave |  3 | 
| 5 | bob |  2 | 
+----+------+--------+ 

私がこれまで持っていることは、次のクエリです:

1 SELECT * FROM persons WHERE 
    2  EXISTS (
    3   SELECT * FROM (
    4    SELECT * FROM names WHERE active=true LIMIT 1 OFFSET 1 
    5  ) AS selectedname WHERE (selectedname.name=persons.name) 
    6 ) 
    7  OR 
    8  EXISTS (
    9   SELECT * FROM(
10    SELECT * FROM persons WHERE EXISTS (
11     SELECT * FROM (
12      SELECT * FROM names WHERE active=true LIMIT 1 OFFSET 1 
13    ) AS selectedname WHERE (selectedname.name=persons.name) 
14   ) 
15  ) AS personswiththatname WHERE persons.id=personswiththatname.fan_of 
16 ); 

それは上から私に望ましい結果を与えるが、それがあるため非効率的であることに注意してください3-5行目と11-13行目は同じです。

I持って、次の二つの質問:

    この非効率性を回避するために何ができるか
  1. 私は実際に name条件(名前=ボブとここに行)とfan_of条件(名前=デイブとこちら行)から を来たものから来て、これらの行を区別する必要があります。この はアプリケーションコードで実行できますが、番目のアクティブな名前を調べるにはもう一度 データベースクエリが必要です。これは が遅くなる可能性があります(この方が良い解決策であれば修正してください)。私は むしろ、私がどのような出力を達成することができる

    +----+------+--------+---+ 
    | id | name | fan_of | z | 
    +----+------+--------+---+ 
    | 2 | bob |  4 | 1 | 
    | 4 | dave |  3 | 0 | 
    | 5 | bob |  2 | 1 | 
    +----+------+--------+---+ 
    

    のように区別するのに役立ちます追加の列zを好むでしょうか?

+0

を" "与えられたオフセットkに対して、k + 1番目のアクティブな名前を持つ人物(人物の行)を選んで、その人物の1人をファンとして " 実際には、k + 1番目のアクティブな名前を持つ人物、またはその名前の別の人物のファンである人物を選択したいとは限りません。 あなたの説明が私にとって意味をなさない唯一の方法です。 –

+0

@ChrisJいいえ、それは言いましたが、それは現実の例として意味をなさないと認めます。私が望むのは、その名前を持つすべての人と 'fan_of'カラムによって参照されるすべての人です。 – phinz

+0

Bobは彼のファンであり、Bobはn番目の(選択された)アクティブな名前なので、「Dave」が取得されますか? –

答えて

1

パラメータを使用して達成したい最小値を得ることができるようです(これはオプションにする必要があります)。

私はあなたが求めているものを達成するための簡単な方法を見ることができないので、これは私がこれまでに持っていたものです。(set'offset 'k')

SET @offset = 1; 
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset)); 
select 
a.* 
From persons a 
where (a.name = @name) OR (a.id IN (SELECT fan_of from persons where name = @name)); 

食べ物を食べていた頃にまだ答えが出ていない場合は、パート2を見てみましょう。

(うまくいけば正しく読んだことがあります)

P.S.私はこの文脈でよりよく読むように@name SQLを1行にとどめました。

編集:あなたの例を使用して、ソースのかなり面倒ですが機能的な指標があります。 Z = 1行がnameからのものである場合、 '0' れるfan_of

SET @offset = 1; 
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset)); 
select 
a.*,'1' as z 
From persons a 
where (a.name = @name) 
union 
select 
a.*,'0' as z 
From persons a 
where (a.id IN (SELECT fan_of from persons where name = @name)); 

は異なるIDクエリ:

SET @offset = 1; 
SET @name = (SELECT name FROM (select name, @rank := @rank +1 as Rank from names n, (SELECT @rank := 0) r where active !=0) as activeRanked where activeRanked.rank = (1 + @offset)); 
SELECT id, name, fan_of, z FROM 
(select 
distinct a.id, 
a.name, 
a.fan_of, 
1 as z 
From persons a 
where (a.name = @name) 
union 
select 
distinct a.id, 
a.name, 
a.fan_of, 
0 as z 
From persons a 
where (a.id IN (SELECT fan_of from persons where name = @name)) 
ORDER BY z desc) qry 
GROUP BY id; 

これが生成する:あなたの例に基づい

+----+------+--------+---+ 
| id | name | fan_of | z | 
+----+------+--------+---+ 
| 2 | bob | 4 | 1 | 
| 5 | bob | 2 | 1 | 
| 4 | dave | 3 | 0 | 
+----+------+--------+---+ 
+0

ありがとう、私は明日試してみて、この答えを今のところ私のタイムゾーンでは遅く、MySQLの初心者です(前にパラメータを使用していない人)。 – phinz

+0

問題はありません - 私パート2を達成するためのオプションが追加されました。最も複雑ではありませんが、機能的です。 –

+0

それは動作します!しかし、 'id' = 2と' name' = bobの行を二重に選択しないようにする方法はありますか? 'z' = 1で2回、' z' = 0でもう1回リストされます。私は 'z' = 1の行だけを必要とします。 – phinz

関連する問題