3

PostgreSQLで、ある基準に基づいて行を選択したいのですが、基準に一致する行がない場合、最初の行を返したいです。テーブルには実際には序数の列が含まれているため、タスクはより簡単になるはずです (最初の行は序数が 0 の行です)。例えば:

SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%' OR ord = 0
LIMIT 1;

しかし、この場合、一致するレコードの順序を保証する方法はなく、それらを順序付けるものは何もありません。SELECT単一のステートメントを使用してこれを行う方法は何でしょうか?

4

4 に答える 4

5

あなたは正しい軌道に乗っています。を追加するだけorder byです:

SELECT street, zip, city
FROM address
WHERE street LIKE 'Test%' OR ord = 0
ORDER BY (CASE WHEN street LIKE 'Test%' THEN 1 ELSE 0 END) DESC
LIMIT 1;

または、代わりに:

ORDER BY ord DESC

これらのいずれも、ord = 0行を最後に配置します。

編集:

Erwin は、インデックスの使用の観点からはORWHERE句内の an は最善のアプローチではないという良い点を挙げています。答えを次のように変更します。

SELECT *
FROM ((SELECT street, zip, city
       FROM address
       WHERE street LIKE 'Test%'
       LIMIT 1
      )
      UNION ALL
      (SELECT street, zip, city
       FROM address
       WHERE ord = 0
       LIMIT 1
      )
     ) t
ORDER BY (CASE WHEN street LIKE 'Test%' THEN 1 ELSE 0 END) DESC
LIMIT 1;

これにより、クエリで 2 つのインデックス (streetおよびord) を使用できるようになります。これは、LIKEパターンがワイルドカードで始まっていないためです。LIKEパターンがワイルドカードで始まる場合でも、この形式のクエリは完全なテーブル スキャンを実行します。

于 2015-06-10T10:43:07.453 に答える
3

いくつかの基準に基づいて行を選択したいが、基準に一致する行がない場合は最初の行を返したい

より短い(そして正しい)

WHERE実際には句はまったく必要ありません。

SELECT street, zip, city
FROM   address
ORDER  BY street !~~ 'Test%', ord
LIMIT  1;

!~~の Postgres 演算子ですNOT LIKE。どちらでも使用できます。ロジックを (NOT LIKEの代わりにLIKE) 反転することで、デフォルトASCの並べ替え順序と NULL の並べ替えを最後に使用できるようになったことに注意してください。これは重要な場合があります。読む。

これは短くなります (ただし、必ずしも高速であるとは限りません)。また、 @Gordon によって現在受け入れられている回答とは微妙に異なります (より信頼性が高くなります) 。

で並べ替えるbooleanときは、それがどのように機能するかを理解する必要があります。

現在受け入れられている回答ではORDER BY <boolean expression> DESC、NULL を最初にソートする を使用しています。このような場合、通常は次を追加する必要がありますNULLS LAST

street定義されている場合NOT NULL、これは明らかに無関係ですが、質問では定義されていません。(常にWHEREテーブル定義を提供してください。)現在受け入れられている回答では、句で NULL 値を除外することで問題を回避しています。

他の RDBMS (MySQL、Oracle など) には、booleanPostgres のような適切な型がないため、それらの製品の使用者からの誤ったアドバイスがよく見られます。

現在のクエリ (および現在受け入れられている回答)には、句が必要ですWHERE- または少なくともNULLS LAST. ORDER BYどちらでも違う表現をする必要はありません。

さらに重要なのは、複数の行が一致している場合street(これは予想されることです)、返される行は任意であり、呼び出し間で変更される可能性があります。これは一般的に望ましくない効果です。このクエリは、タイを破る最小の行を選択しord、安定した結果を生成します。

この形式は、 の行の存在に依存しないという点でも柔軟性がありord = 0ます。代わりに、最小の行ordがいずれかの方法で選択されます。

インデックスで高速化

(それでも正しいです。) 大きなテーブルの場合、次のインデックスを使用すると、このクエリのパフォーマンスが大幅に向上します。

CREATE INDEX address_street_pattern_ops_idx ON address(street text_pattern_ops);

詳細な説明:

未定義の詳細によっては、インデックスに列を追加するのに費用がかかる場合があります。
このインデックスを使用した最速のクエリ:

(
SELECT street, zip, city
FROM   address
WHERE  street LIKE 'Test%'
ORDER  BY ord  -- or something else?
-- LIMIT 1  -- you *could* add LIMIT 1 in each leg
)
UNION ALL
(
SELECT street, zip, city
FROM   address
ORDER  BY ord
-- LIMIT 1  -- .. but that's not improving anything in *this* case
)
LIMIT  1

ところで、これは単一のステートメントです。

これはより冗長ですが、より単純なクエリ プランを使用できます。最初の行が十分な行を生成する場合 (この場合は 1) 、 2 番目SELECTの行UNION ALLは実行されません。SELECTでテストすると、クエリ プランにEXPLAIN ANALYZE表示されます。(never executed)

詳細:

の評価UNION ALL

ゴードンのコメントへの返信。ドキュメントごと:

同じステートメント 内の複数UNIONの演算子は、括弧で特に指定されていない限り、左から右に評価されます。SELECT

大胆強調鉱山。
またLIMIT、十分な行が見つかるとすぐに Postgres の評価を停止します。そのため(never executed)、 の出力に が表示されますEXPLAIN ANALYZE

ORDER BYfinal の前にアウターを追加すると、LIMITこの最適化は不可能になります。次に、どの行が最初にソートされるかを確認するために、すべての行を収集する必要があります。

于 2015-06-12T22:31:26.463 に答える
0

次のことができます。

SELECT street, zip, city
FROM address
WHERE (EXISTS(SELECT * FROM address WHERE street LIKE 'Test%') AND street LIKE 'Test%') OR 
      (NOT EXISTS(SELECT * FROM address  WHERE street LIKE 'Test%') AND ord = 0)
于 2015-06-10T10:50:03.543 に答える