sql >> データベース >  >> RDS >> Database

SQLBETWEEN-値の範囲をスキャンするためのスマートなヒント

    SQL BETWEENは、テストする値の範囲を指定するために使用される演算子です。戻り値は、包括的または範囲内にすることができます。または、その前にNOT演算子を追加すると、範囲外になる可能性があります。日付、時刻付きの日付、数字、文字列に対して機能します。

    次のWHERE句で使用できます:

    • SELECT、
    • 挿入(SELECTを使用)
    • 更新、
    • およびDELETE。

    また、GROUPBYとともにHAVING句に対しても機能します。

    ただし、注意しないと、SQL BETWEENを使用すると、特に日付と時間の関係で、気が狂う可能性があります。

    ただし、心配する必要はありません。 SQLBETWEENを使用する際の落とし穴に対処する例があります。しかしその前に、私が使用したサンプルデータは NOAAからのものでした 。それらから無料で気象データをリクエストすることができます。 2010年の米国の1時間ごとの気温レコードを使用しました。次に、SQL ServerManagementStudioを使用してCSVデータをSQLServerにインポートしました。列の名前を変更し、非クラスター化インデックスを追加しました。

    始めましょう。

    日付と時刻でSQLを使用する

    これは、SQLBETWEENを処理するときに最も検索されるアイテムである必要があります。例を使用して、その仕組みを説明します。

    ヒント#1:DATETIME列の場合、日付と時刻の両方を指定します

    間違った使用法

    この点を強調するために、間違った使用法から始めましょう。次のBETWEENとDATETIME列の使用は、予期しない結果をもたらします。

    
    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour BETWEEN '01/01/2010' AND '01/02/2010'
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    このクエリは、シカゴのオヘア国際空港近くの気象観測所から2日間のデータを返します。低い値(01/01/2010)と高い値(01/02/2010)の間の範囲に気付くことができます。これが図1の結果セットです。

    図12つの日付の間のSQLを使用したクエリの結果セット。

    しかし、どこに問題があるのでしょうか?

    2日間の1時間ごとの記録になるはずです。そのため、結果セットには48レコードが含まれている必要があります。ただし、24個しかないことに注意してください。問題は DateHourの時間要素にあります。 桁。 DATETIME列で時刻を指定しない場合、00:00または12:00AMと見なされます。また、データは2010年1月1日の午前12:00ではなく午前01:00に開始されたことに注意してください。

    したがって、内部的には、SQLServerはDateHourを使用していました。 ’01/01/2010 00:00:00.000′ AND ’01 / 02/2010 00:00:00.000′ 。どうやってわかりますか?

    日付は実際には文字列です

    そうです。

    一重引用符で囲まれた日付値は、実際には日付ではなく文字列です 。 SQL Serverは、暗黙的な変換を使用して文字列をDATETIMEに変換します。変換後、時間部分が日付に追加されます。

    実際の実行計画を含めるで調べてみましょう 。 Ctrl-Mを押します SQL Server Management Studioで、前の例を再実行します。

    実行プランが表示されたら、インデックスシークを右クリックします。 演算子を選択し、プロパティを選択します 。図2を参照してください。

    図2文字列のDATETIMEへの暗黙の変換。これは、BETWEENを使用したクエリの実行プランに隠されています。

    次に、述語を探すを展開します 。図2の四角で囲まれた部分は、2つの文字列のDATETIMEへの暗黙の変換を示しています。 暗黙の変換は内部で行われるため 、初心者は、結果セットでの期待が満たされない理由を混乱させます。

    正しい使用法

    以下の例では、2010年1月2日の午前8時から午後12時までの1時間ごとのレコードが返されます。

    
    SELECT * FROM TemperatureData
    WHERE DateHour BETWEEN '01/02/2010 08:00' AND '01/02/2010 12:00'
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    特に日付が同じ場合は、時間部分を指定する必要があります。または、期待した結果が得られません。

    一日の記録を返すために、これは機能しません:

    
    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour = '06/01/2010'
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    2010年6月1日の午前12:00のレコードを1つだけ返します。ただし、指定された時間でBETWEENを使用すると、1日の1時間ごとの記録を返すことができます。次の例を参照してください。

    
    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour BETWEEN '06/01/2010 00:00' AND '06/01/2010 23:00'
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    23:00までのみ指定したことに注意してください。データが1日の任意の時刻を使用する場合は、範囲の高い方の値で23:59または11:59PMを使用します。必要な場合も秒を指定してください。

    ヒント#2:DATEデータ型を検討する

    時間部分が必要ない場合は、代わりにDATEデータ型を検討してください。そして、あなたは上記のトラブルを避けるでしょう。

    SQL BETWEEN with Numbers

    数字に移りましょう。

    ヒント#3:非整数値の小数部分を含める

    
    SELECT
     DateHour
    ,[Hourly_Heating_Degree_Hours]
    FROM TemperatureData
    WHERE DateHour BETWEEN '06/01/2010' AND '06/5/2010 23:00'
    AND [Hourly_Heating_Degree_Hours] BETWEEN 5.0 AND 7.0
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    数字を含む別の条件が追加されていることに注意してください。結果はさらに5度と7度に制限されます。

    DECIMAL、MONEY、またはFLOATデータ型を使用する場合は、52.00や10.0000のように、ゼロであっても小数点以下の部分を指定します。 このようにして、暗黙の変換を回避します ターゲットのDECIMAL、MONEY、またはFLOATデータ型に。

    文字列を使用したSQL

    ヒント#4:文字列の場合、範囲は照合に基づいています

    文字列の場合、BETWEENはアルファベット順に値を評価します。 「A」が最小で「Z」が最大です。また、一般的に評価は照合に基づいているとも言えます。 SQLServerがサポートする言語は英語だけではないためです。 照合 並べ替え規則、大文字と小文字、およびアクセントの感度を提供します。 AdventureWorksを使用してみましょう この例のデータベース。以下のコードと図3の結果を確認してください。

    
    USE AdventureWorks
    GO
    
    SELECT 
     LastName
    ,FirstName
    ,MiddleName
    FROM Person.Person
    WHERE Lastname BETWEEN 'Spanaway' AND 'Splane'
    ORDER BY LastName;
    

    図3文字列でBETWEENを使用したクエリの結果セット。

    範囲は姓をカバーしますSpanaway 。しかし、 Splaneはどこにありますか ?データベースには存在しません。したがって、結果は Spicerまでしか到達しませんでした 。

    サポートされているすべてのデータ型のヒント間のSQL

    日付、数値、文字列のいずれにBETWEENを使用している場合でも、知っておくべき一般的なことがあります。これは常識かもしれませんが、それでも誤って起こります。これがどのように発生するかを読んでください。

    ヒント#5:開始値と終了値の両方をNULLにすることはできません

    BETWEENには、範囲の開始値と終了値が必要です。それぞれにNULL以外の値が必要です。以下にNULLの終了値を持つ例があります。

    
    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour BETWEEN '01/01/2010' AND NULL;
    


    これは、アプリまたはストアドプロシージャからSELECTステートメントを呼び出し、それを適切に検証しなかった場合に発生する可能性があります。

    ヒント#6:開始値を終了値より大きくすることはできません

    両方の値がNULLでない場合も何も返されませんが、範囲が逆になります。これが例です。

    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour BETWEEN '01/30/2010' AND '01/01/2010';
    


    日付を除いて、次の式も結果を返しません:

    • 100と-200の間の値。 -200は100よりも低いためです。
    • 「動物園の飼育係」と「会計士」の間で働きます。 「Z」は「A」よりも大きいためです。

    ヒント#7:範囲の値は同じデータ型である必要があります

    場合によっては、ユーザーインターフェイスコントロールに予期しない出力が表示されることがあります。または、間違ったプロパティを取得しただけです。また、SQL Serverに渡す前にチェックしないと、次のような状況が発生する可能性があります。

    
    SELECT 
     DateHour
    ,Hourly_Heating_Degree_Hours
    FROM TemperatureData
    WHERE DateHour BETWEEN '06/01/2010' AND 'Saturday, June 5, 2010'
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    

    文字列から日付への変換エラーが発生します。

    したがって、ヒント#5から#7の教訓は、範囲の開始値と終了値を検証することです。 。

    ヒント#8:値を除外するにはNOTBETWEENを使用します

    別の例を考えてみましょう。

    
    SELECT
     MONTH(DateHour) AS [Month] 
    ,round(AVG([Hourly_Heating_Degree_Hours]),2) AS AverageTemperature
    FROM TemperatureData
    WHERE DateHour BETWEEN '01/01/2010 00:00' AND '06/30/2010 23:00'
    AND DateHour NOT BETWEEN '05/01/2010 00:00' AND '05/31/2010 23:00'
    AND Latitude = 41.995
    AND Longitude = -87.9336
    GROUP BY MONTH(DateHour);
    


    これにより、1月から6月までの月平均が返されますが、5月は除外されます。 2010年5月のレコードを除外することは、NOTBETWEENによって可能になります。これが図4の結果セットです。

    図4NOTBETWEENを使用したクエリの結果セット。

    他の演算子と比較したSQL

    ヒント#9:範囲ではなくリストが必要な場合はINを使用

    IN演算子は、値がリストまたはサブクエリのいずれかの値と一致するかどうかを判別します。一方、NOT INを使用すると、値が一致しないかどうかがチェックされます。

    BETWEEN演算子とIN演算子はどちらも、複数の値に基づいてデータをフィルタリングします。ただし、違いは、一致する値のセットにあります。 BETWEENは範囲を使用します。ただし、INは、リストのコンマ区切り値またはサブクエリの行を使用します。

    以下の例を確認してください。

    SELECT
     DateHour
    ,[Hourly_Heating_Degree_Hours]
    FROM TemperatureData
    WHERE DateHour BETWEEN '06/01/2010' AND '06/5/2010 23:00'
    AND [Hourly_Heating_Degree_Hours] IN (5.2, 6, 7, 3.7)
    AND Latitude = 41.995
    AND Longitude = -87.9336;
    


    INで使用される値のリストを見てください。増加する値のリストである必要はありません。リストの最後の値(3.7)も、数値の中で最も小さい値です。

    ヒント#10:BETWEENまたは> =with <=

    から選択します

    実行時に、SQLServerはBETWEENを<=演算子を使用して>=に変換します。どうやってわかりますか?

    以下のコードを見てください。

    
    SELECT
     DateHour
    ,AVG(Hourly_Heating_Degree_Hours) AS AverageTemp
    FROM TemperatureData
    WHERE DateHour BETWEEN '01/01/2010 08:00' AND '01/01/2010 12:00'
    GROUP BY DateHour;
    
    SELECT
     DateHour
    ,AVG(Hourly_Heating_Degree_Hours) AS AverageTemp
    FROM TemperatureData
    WHERE DateHour >= '01/01/2010 08:00' 
    AND DateHour <= '01/01/2010 12:00'
    GROUP BY DateHour;
    


    どちらのクエリも、図5のクエリと同じ結果セットになります。

    図5BETWEENまたは>=と<=のいずれかを使用した結果セット。

    図6に示すように、これらにも同じ実行プランがあります。

    図6 BETWEEN、>=および<=演算子の使用を比較する2つのクエリの実行プラン。

    しかし、これが問題です。

    最初のインデックスに注目してください シーク 図6の演算子。次に、述語の検索を参照してください。 。 BETWEENキーワードが表示されますか?ありませんよね? <=演算子で>=に変換されるためです。これらは、 Seek Predicatesに存在する演算子です。 。

    しかし、それだけではありません。

    2番目のインデックスシークにマウスを合わせると 演算子を使用すると、最初のインデックスシークと同じプロパティが表示されます。 。

    したがって、BETWEEN演算子は<=演算子を使用した>=へのショートカットのようです。 。後者を使用する場合は、さらに入力します。 BETWEENを数値と文字列で使用した場合にも、同じ変換が行われることがわかります。

    結局、BETWEENまたは>=および<=演算子を使用するかどうかはあなた次第です。 BETWEENの変換にかかる変換時間はごくわずかです。ただし、それでもその余分な無視できる時間を必要としない場合は、>=および<=演算子を使用してください。

    ボトムライン

    SQL BETWEENは、範囲を含むデータをフェッチするのに適しています。そして、それはそれほど難しくはありません。 DATETIME値でさえ、BETWEENで管理できます。時間の部分を適切にカバーしていることを確認してください。また、>=と<=を使用するのと同じです。どちらを使用するかはあなた次第です。

    このページをブックマークして、必要なときに日付、数字、文字列のヒント間のSQLを取得できます。

    BETWEENを使用して説明しなかったトリックがある場合は、コメントセクションでそれらを共有できます。この記事が気に入ったら、ソーシャルメディアボタンを押して共有してください。

    みなさん、コーディングを楽しんでください!


    1. 例外の取得ORA-00942:表またはビューが存在しません-既存の表に挿入する場合

    2. MySQLWorkbenchを使用して新しいデータベース図を作成する方法

    3. PHP-ログインシステムでメンバー専用ページを保護する

    4. SQLクエリの履歴を調べる