0

序章

場合によっては、結合の代わりに意図的にスカラー サブクエリを使用して、複数の行が見つからないことを確認できます。たとえば、いくつかのperson行の国籍を検索するために、このクエリを使用する場合があります。

select p.name, c.iso from person p join person_country_map pcm on p.id = pcm.person join country c on pcm.country = c.id where p.id in (1, 2, 3)

ここで、person_country_mapが関数マッピングではないとします。特定の人物が複数の国にマップされる場合があるため、結合によって複数の行が検出される場合があります。または、実際には、少なくともデータベースの制約に関する限り、人物がマッピング テーブルにまったく含まれていない可能性があります。

しかし、この特定のクエリでは、クエリを実行している人物が 1 つの国しか持っていないことをたまたま知っています。それが私のコードの基礎となる仮定です。しかし、可能であればその仮定を確認したいと思います。何かがうまくいかず、複数の国を持つ人、または国のマッピング行がない人に対してこのクエリを実行しようとした場合、それは死ぬでしょう。

最大 1 行の安全性チェックを追加する

複数の行をチェックするには、結合をスカラー サブクエリとして書き直すことができます。

select p.name, ( select c.iso from person_country_map pcm join country c on pc.country = c.id where pcm.person = p.id ) as iso from person p where p.id in (1, 2, 3)

現在、照会された人物が 2 つ以上の国にマップされている場合、DBMS はエラーを返します。単純な結合のように、同じ人物に対して複数のエントリが返されることはありません。そのため、行がアプリケーションに返される前であっても、このエラー ケースがチェックされていることを知っていると、少し楽に眠ることができます。もちろん、慎重なプログラマーとして、アプリケーションもチェックインすることがあります。

行が見つからない 場合に安全性をチェックすることは可能ですか?

しかし、 person_country_map に人物の行がない場合はどうなるでしょうか? その場合、スカラー サブクエリは null を返すため、左結合とほぼ同等になります。

(議論のために、 person_country_map.country から country.id への外部キーと、country.id の一意のインデックスを想定して、特定の結合が常に成功し、正確に 1 つの国の行を見つけるようにします。)

私の質問

1 つだけの結果が必要であることを SQL で表現できる方法はありますか? 単純なスカラー サブクエリは「0 または 1」です。言えるようになりたい

select 42, (select exactly one x from t where id = 55)

サブクエリが行を返さない場合、クエリは実行時に失敗します。もちろん、上記の構文は架空のものであり、それほど簡単ではないと確信しています。

私は MSSQL 2008 R2 を使用しています。実際、このコードはストアド プロシージャに含まれているため、必要に応じて TSQL を使用できます。(もちろん、ビュー定義でも使用できるため、通常の宣言型 SQL の方が望ましいです。)もちろん、チェックを実行することもexists、値を選択して TSQL 変数に入れ、null かどうかを明示的にチェックすることもできます。結果を一時テーブルに選択し、そのテーブルにチェックとして一意のインデックスを作成することもできます。しかし、サブクエリが正確に 1 つの行を返すという私の仮定をマークし、その仮定を DBMS にチェックさせる、これ以上読みやすく洗練された方法はありませんか?

4

2 に答える 2

0

MSSQL ではisnull、最初の引数が null の場合にのみ 2 番目の引数を評価するように見えます。したがって、一般的に言えることは、

select isnull(x, 0/0)

null 以外の場合は戻りx、null になる場合は終了するクエリを提供します。これをスカラー サブクエリに適用すると、

select 42, isnull((select x from t where id = 55), 0/0)

select xサブクエリによって正確に 1 行が検出されることが保証されます。複数の場合、DBMS 自体がエラーを生成します。行がない場合、ゼロによる除算がトリガーされます。

これを元の例に適用すると、次のコードになります。

select p.name, -- Get the unique country code of this person. -- Although the database constraints do not guarantee it in general, -- for this particular query we expect exactly one row. Check that. -- isnull(( select c.iso from person_country_map pcm join country c on pc.country = c.id where pcm.person = p.id ), 0/0) as iso from person p where p.id in (1, 2, 3)

エラー メッセージを改善するには、ゼロ除算の代わりに変換の失敗を使用できます。

select 42, isnull((select x from t where id = 55), convert(int, 'No row found'))

ただしconvert、サブクエリからフェッチしている値自体がint.

于 2015-03-06T18:27:11.217 に答える