指定された結果セットを単一のSQLステートメントで返すには、いくつかの方法があります。
残念ながら、これらのアプローチのほとんどは、かなり扱いにくいステートメントを生成します。
よりエレガントに見えるステートメントは、大きなセットを扱うときにパフォーマンスが低下する(または耐えられない)傾向があります。また、パフォーマンスが向上する傾向のあるステートメントは、よりエレガントに見えません。
最も一般的なアプローチの3つは以下を利用します:
- 相関サブクエリ
- 不等式結合(ほぼデカルト積)
- データを2回渡す
これは、MySQLユーザー変数を使用してデータを2回パスするアプローチです。これは、基本的RANK() OVER(PARTITION ...)
に他のDBMSで使用可能な分析関数をエミュレートします。
SELECT t.id
, t.patient_id
, t.visit_id
, t.patient_result
FROM (
SELECT p.id
, p.patient_id
, p.visit_id
, p.patient_result
, @rn := if(@prev_patient_id = patient_id, @rn + 1, 1) AS rn
, @prev_patient_id := patient_id AS prev_patient_id
FROM tbl_patients p
JOIN (SELECT @rn := 0, @prev_patient_id := NULL) i
ORDER BY p.patient_id DESC, p.id DESC
) t
WHERE t.rn <= 2
これにはインラインビューが含まれることに注意してください。つまり、「派生テーブル」を作成するためにテーブル内のすべてのデータが渡されます。次に、外部クエリが派生テーブルに対して実行されます。したがって、これは基本的にデータの2回のパスです。
patient_id
このクエリは、インラインビューによって返される列の重複する値を削除することにより、パフォーマンスを向上させるために少し調整できます。しかし、私はそれを上記のように示しているので、何が起こっているのかをよりよく理解することができます。
このアプローチは、大規模なセットではかなり高価になる可能性がありますが、一般に、他のいくつかのアプローチよりもはるかに効率的です。
また、その患者の値がpatient_id
1つしかない場合、このクエリはaの行を返すことにも注意してください。id
少なくとも2行ある患者だけに戻ることを制限するものではありません。
相関サブクエリを使用して同等の結果セットを取得することもできます。
SELECT t.id
, t.patient_id
, t.visit_id
, t.patient_result
FROM tbl_patients t
WHERE ( SELECT COUNT(1) AS cnt
FROM tbl_patients p
WHERE p.patient_id = t.patient_id
AND p.id >= t.id
) <= 2
ORDER BY t.patient_id ASC, t.id ASC
これは「依存サブクエリ」を利用していることに注意してください。これは基本的に、から返される行ごとにt
、MySQLがデータベースに対して別のクエリを効果的に実行していることを意味します。したがって、これは大きなセットでは(経過時間の点で)非常に高価になる傾向があります。
別のアプローチとして、各患者の値が比較的少ない場合は、不等式の結合id
でうまくいく可能性があります。
SELECT t.id
, t.patient_id
, t.visit_id
, t.patient_result
FROM tbl_patients t
LEFT
JOIN tbl_patients p
ON p.patient_id = t.patient_id
AND t.id < p.id
GROUP
BY t.id
, t.patient_id
, t.visit_id
, t.patient_result
HAVING COUNT(1) <= 2
これにより、各患者に対してほぼデカルト積が作成されることに注意してください。各患者の値の数が限られid
ている場合、これはそれほど悪くはありません。しかし、患者が数百のid
値を持っている場合、中間結果は(O)n**2のオーダーで巨大になる可能性があります。