2010-12-07 7 views
4

例として以下の表を使用し、リストされたクエリをベースクエリとして使用して、最大IDを持つ行のみを選択する方法を追加します。 2番目のクエリを実行する必要はありません!MySQLは最大IDを持ち、他の条件と一致する行を選択します

TABLE VEHICLES 

id  vehicleName 
----- -------- 
1  cool car 
2  cool car 
3  cool bus 
4  cool bus 
5  cool bus 
6  car 
7  truck 
8  motorcycle 
9  scooter 
10  scooter 
11  bus 

TABLE VEHICLE NAMES 

nameId vehicleName 
------ ------- 
1  cool car 
2  cool bus 
3  car 
4  truck 
5  motorcycle 
6  scooter 
7  bus 

TABLE VEHICLE ATTRIBUTES 

nameId attribute 
------ --------- 
1  FAST 
1  SMALL 
1  SHINY 
2  BIG 
2  SLOW 
3  EXPENSIVE 
4  SHINY 
5  FAST 
5  SMALL 
6  SHINY 
6  SMALL 
7  SMALL 

とベースクエリ:

select a.* 
    from vehicle   a 
    join vehicle_names b using(vehicleName) 
    join vehicle_attribs c using(nameId) 
where c.attribute in('SMALL', 'SHINY') 
and a.vehicleName like '%coo%' 
group 
    by a.id 
having count(distinct c.attribute) = 2; 

それでは、私が達成したいことは、特定の属性を持つ行を選択することで、名前が、IDがどこにあるかと一致する名前ごとに一つだけのエントリと一致最高!

したがって、この例ではワーキング溶液は、以下の行を返します:

id  vehicleName 
----- -------- 
2  cool car 
10  scooter 

それは現時点ではID

上の最大のいくつかの並べ替えを使用していた場合、私はクールな車のためのすべてのエントリを取得しますスクーター。

私の実際のデータベースは、同様の構造に従っており、数千ものエントリを持っているので、上記のようなクエリは3000以上の結果を簡単に返すことができます。結果を自分のサイトの検索で使用するため、実行時間を低く抑えるために結果を100行に制限します。私が同じ名前でIDが異なる "車両"を繰り返しているのは、常に新しいモデルが追加されているからです。しかし、車名での検索では、古いカードをIDの最も高いものだけ戻したくない!

正解は、私が現在使用しているクエリを適合させ、名前が一致する行だけを返しますが、最も高いIDを持ちます。

これが不可能な場合は、検索の実行時間を大幅に増やすことなく、私が望むものをどのように達成できるかについての提案をいただければ幸いです。

+0

あなたは ''(名前ID、属性)にvehicle_attribs' 'にインデックスを持っていますか? 'LIKE '%cool%''を使ってvehicle_namesを検索するとインデックスが使われない – ajreal

+0

@ajreal vehicle_attribsの(nameId、attribute)にPRIMARY KEYがあります。 – Tristan

答えて

4

、ここで私はどうなるのか:

select a.* 
from vehicle a 
    left join vehicle a2 on (a.vehicleName = a2.vehicleName and a.id < a2.id) 
    join vehicle_names b on (a.vehicleName = b.vehicleName) 
    join vehicle_attribs c using(nameId) 
where c.attribute in('SMALL', 'SHINY') 
    and a.vehicleName like '%coo%' 
    and a2.id is null 
group by a.id 
having count(distinct c.attribute) = 2;

収量:

:他が言ったように

+----+-------------+ 
| id | vehicleName | 
+----+-------------+ 
| 2 | cool car | 
| 10 | scooter  | 
+----+-------------+ 
2 rows in set (0.00 sec) 

、正規化は、いくつかのレベルで行うことができます

現在のvehicle_namesテーブルをプライマリルックアップテーブルとして維持すると、次のように変更されます。

次のクエリにつながっ0
update vehicle a 
    inner join vehicle_names b using (vehicleName) 
set a.vehicleName = b.nameId; 
alter table vehicle change column vehicleName nameId int; 

create table attribs (
    attribId int auto_increment primary key, 
    attribute varchar(20), 
    unique key attribute (attribute) 
); 
insert into attribs (attribute) 
    select distinct attribute from vehicle_attribs; 
update vehicle_attribs a 
    inner join attribs b using (attribute) 
set a.attribute=b.attribId; 
alter table vehicle_attribs change column attribute attribId int; 

select a.id, b.vehicleName 
from vehicle a 
    left join vehicle a2 on (a.nameId = a2.nameId and a.id < a2.id) 
    join vehicle_names b on (a.nameId = b.nameId) 
    join vehicle_attribs c on (a.nameId=c.nameId) 
    inner join attribs d using (attribId) 
where d.attribute in ('SMALL', 'SHINY') 
    and b.vehicleName like '%coo%' 
    and a2.id is null 
group by a.id 
having count(distinct d.attribute) = 2;
+0

各グループからの単一のレコードが必要なときのランキングの良い方法、私はそれを考えていません。私は変数を含むソリューションを提案しますが、クエリの結果がクエリキャッシュにキャッシュされる可能性があるため、これははるかに優れています。 – newtover

3

テーブルは、しかし、これは、これを行うためにあなたを容易にし、正規化されたと思われるしません:

select max(id), vehicleName 
from VEHICLES 
group by vehicleName 
having count(*)>=2; 
+0

テーブルは本物とはまったく異なるサンプルです!グループはマックスにとって重要ですか?しかし、私が実際に答えているのは、上記でmax行だけを選択するために私がクエリに追加するものです。ありがとう – Tristan

1

私は、私は完全にあなたのモデルを理解していないが、彼らが立つよう次のクエリが要件を満たしています。最初のサブクエリは、車両の最新バージョンを検出します。 2番目のクエリは、 "and"条件を満たします。次に、私はvehiclename(これはキーですか?)に関するクエリに参加します。

select a.id 
     ,a.vehiclename 
    from (select a.vehicleName, max(id) as id 
      from vehicle a 
     where vehicleName like '%coo%' 
     group by vehicleName 
     ) as a 
    join (select b.vehiclename 
      from vehicle_names b 
      join vehicle_attribs c using(nameId) 
     where c.attribute in('SMALL', 'SHINY') 
     group by b.vehiclename 
     having count(distinct c.attribute) = 2 
     ) as b on (a.vehicleName = b.vehicleName); 

この「最新車両」ロジックはあなたが多くのことを行う必要があります何かである場合は、小さな提案は、各車両の最新バージョンを返す(下記参照)のビューを作成することです。次に、find-max-queryではなくviewを使用することができます。これは純粋に使いやすさのためであり、パフォーマンス上の利点はありません。あなたは

1)列を追加することができ、モデルの適切な再設計に行かなくても

select * 
    from vehicle a 
where id = (select max(b.id) 
       from vehicle b 
       where a.vehiclename = b.vehiclename); 
+0

VIEWSは索引付けされていないので、OPが持つレコードの量で照会結果を高速化することはできません。 – Danosaure

+0

@Danosaure:私の提案は、使いやすさを向上させるためのものであることを明確にするために、私の答えを編集します。 – Ronnis

0

は、アプリケーションが管理することができることをIsLatest。

これは、あなたがそのような取引で

UPDATE a 
SET IsLatest = 0 
WHERE IsLatest = 1 

INSERT new a 

UPDATE a 
SET IsLatest = 1 
WHERE nameId = @last_inserted_id 

としてクエリを発行するために新しいエントリを追加するときに必要なのは完璧ではないですが、あなたは(最後に見ない、次の問題まで)質問満足させます あなたは、単一のSでそれを行うことができますまたはトリガ

2)あなたは、クエリ

SELECT MAX(nameId) 
FROM a 
WHERE vehicleName = @name 

3を発行する前に、別の方法として、あなたがmax_idを見つけることができます) QL、そしてそれは実際に私はあなたのGROUP BYおよびHAVING削除した

select a.* 
    from vehicle   a 
    join vehicle_names b ON a.vehicleName = b.vehicleName 
    join vehicle_attribs c ON b.nameId = c.nameId AND c.attribute = 'SMALL' 
    join vehicle_attribs d ON b.nameId = c.nameId AND d.attribute = 'SHINY' 
    join vehicle   notmax ON a.vehicleName = b.vehicleName AND a.nameid < notmax.nameid 
where a.vehicleName like '%coo%' 
     AND notmax.id IS NULL 

でまともな速さを持っており、他のとそれを交換しなければならない(vehicleName、名前ID)のインデックスを提供する(名前IDごとに1つだけの属性が可能であると仮定して)参加。

私はまた、グループごとに最大値を見つける方法の1つを使用しました。それは、テーブル自体を結合し、同じ名前のIDが大きいレコードがない行を除外することです。

他にも 'max per group sql'を検索する方法があります。 hereも参照してください。あなたのロジックを維持したい場合は

+0

ニース、私は "小さなと光沢のある"を結合として実装することは考えていませんでした。 – Ronnis

関連する問題