3


1

SQLの達人のための簡単な質問。

特に、2つの列を含むテーブルがあります。min_numberとmax_number最小数と最大数の間の最初のサイズの_n_ sizeの穴を見つけるクエリを作成しようとして失敗しました。

最小最大1 100 200 2。 250 300 3。 330 400

サイズ50の穴を見つけたい場合は、1行の最大200桁が返され(その行と最小2行の間に50桁の穴があります)、20桁の穴は300行の最大2秒を返します。 適切なサイズの穴が存在しない場合は、最後の最大値(400)が返されます。

ありがとう

6 Answer


1


_編集:最終的な答えは下にあります。

なぜそんなに多くのSQL質問がテーブル名を忘れるのですか?

-- Buggy: should reference (lo.max + 1)
SELECT lo.max + 1 AS min_range
    FROM example lo, example hi
    WHERE hi.min - (lo.max - 1) >= 40   -- Example won't work with 50
      AND NOT EXISTS (SELECT * FROM example AS mid
                         WHERE mid.min > lo.max
                           AND mid.max < hi.min
                     )

NOT EXISTS句は非常に重要です - 隣接範囲のみを考慮していることを保証します。

これは「十分に大きなギャップがある」ケースを扱います。

名目上、UNION句を使用して「十分に大きなギャップはありません」と対処できます。

...
UNION
SELECT MAX(max)+1
    FROM example
    WHERE NOT EXISTS(
        SELECT lo.max + 1 AS min_range
            FROM example lo, example hi
            WHERE hi.min - (lo.max - 1) >= 40   -- Example won't work with 50
              AND NOT EXISTS (SELECT * FROM example AS mid
                                 WHERE mid.min > lo.max
                                   AND mid.max < hi.min
                             )
            )

内側のSELECTは最初のインデントされたものの直接の書き起こしです。

'' '' '

上記のSQLはテストされていません。 最初の部分は(特にテストデータに)作用します - しかし、複数の答えを生み出すことができます。 だから、それを修正する必要があります(修正、私は、2×2のエラーを考えています):

SELECT MIN(lo.max + 1) AS min_range
    FROM example lo, example hi
    WHERE hi.min - (lo.max + 1) >= 40   -- Example won't work with 50
      AND NOT EXISTS (SELECT * FROM example AS mid
                         WHERE mid.min > lo.max
                           AND mid.max < hi.min
                     )

UNION条項は私にいくらかの悲しみを与えています…​私が期待する答えを生み出さない。

構文的には、私はそれを修正しなければなりませんでした:

SELECT MIN(lo.max + 1) AS min_range
    FROM example lo, example hi
    WHERE hi.min - (lo.max + 1) >= 40   -- Example won't work with 50
      AND NOT EXISTS (SELECT * FROM example AS mid
                         WHERE mid.min > lo.max
                           AND mid.max < hi.min
                     )
UNION
SELECT MAX(solo.max)+1
    FROM example AS solo
    WHERE NOT EXISTS(
        SELECT MIN(lo.max + 1) AS min_range
            FROM example lo, example hi
            WHERE hi.min - (lo.max - 1) >= 40   -- Example won't work with 50
              AND NOT EXISTS (SELECT * FROM example AS mid
                                 WHERE mid.min > lo.max
                                   AND mid.max < hi.min
                             )
            )

これは、カラム名としてキーワードMAXが使われるという問題を回避します(私はおそらく solo.max`の代わりに example.max`を書いたかもしれません)。 しかし、それは私が期待する答えを私に作り出していません。

'' '' '

確かにこの場合、UNIONはORと等価です。そして、この問い合わせは私が欲しい答えを生み出すようです:

SELECT MIN(lo.max + 1) AS min_range
    FROM example lo, example hi
    WHERE (hi.min - (lo.max + 1) >= 40
           AND NOT EXISTS (SELECT * FROM example AS mid
                              WHERE mid.min > lo.max
                                AND mid.max < hi.min
                          )
          )
       OR lo.max = (SELECT MAX(solo.max) FROM Example AS Solo)
;

OR句が hi.max`ではなく lo.max`を引用することは非常に重要です。そうでなければ、あなたは間違った答えを得る。

'' '' '

OK - SQLはMINの動作を誤って定義しているので、UNIONバージョンは運命づけられています。 具体的には、一致する行がない場合、MINは、行を返さずに、値NULLを持つ単一行を返します。 つまり、行が見つからない場合、UNIONの最初の句はNULLを返します。 2番目の句は、NOT EXISTS内のSELECTからMINを省略することで '修正’できますが、それでもステートメントから2行(NULLと正しい値)になります。これは実際には受け入れられません。 そのため、ORバージョンを使用するのがORバージョンです。そしてSQL値はNULL値で再度噛み込まれます。

FROM句のテーブル式でUNIONをフレーミングすることで、NULLを厳密に回避することができます。 これはやや簡単になります。

SELECT MIN(min_range)
    FROM (SELECT (lo.max + 1) AS min_range
              FROM example lo, example hi
              WHERE hi.min - (lo.max + 1) >= 49
                AND NOT EXISTS (SELECT * FROM example AS mid
                                   WHERE mid.min > lo.max
                                     AND mid.max < hi.min
                               )
          UNION
          SELECT MAX(solo.max + 1) AS min_range
              FROM example AS solo
         );

UNIONの前半は、ゼロを含む任意の数のスロットを返すことができます。 2番目は常に値を返します(テーブルに行がある限り)。 その後、外部クエリはこれらの値のうち最も低い値を選択します。

このバージョンは、もちろん、行を割り当てるために使用することができます。

INSERT INTO Example(min, max)
    SELECT MIN(min_range) AS min, MIN(min_range) + (50 - 1) AS max
        FROM (SELECT (lo.max + 1) AS min_range
                  FROM example lo, example hi
                  WHERE hi.min - (lo.max + 1) >= 50
                    AND NOT EXISTS (SELECT * FROM example mid
                                       WHERE mid.min > lo.max
                                         AND mid.max < hi.min
                                   )
              UNION
              SELECT MAX(solo.max + 1) AS min_range
                  FROM example AS solo
             );


1


My_TableからのMIN(T1.max_value)を選択します。T1左外部のMy_Table T2にT2.min_valueを選択します。(T1.max_value 1)AND(T1.max_value @ range)WHERE T2.idはNULLです。

割り当てるIDを探しているので、max_valueとmin_valueを完全に除外した値の範囲が必要だと思います。

NOT EXISTS句を使用して上記のクエリを実行することもできます。 両方で試してみて、どちらがあなたにとってより良いパフォーマンスを発揮するかを確かめてください。

考慮すべきもう一つのことは、あなたは本当にIDを再利用する必要がありますか? あなたのID値はとても高くなり、あなたの利用可能な範囲はあなたがそれをする必要があるようにあまりにも低くなるでしょうか? システムの詳細はわかりませんが、実際には存在しない問題を解決するために、多大な労力を費やしてから余分な処理を追加するように思われるかもしれません。


1


myTableからmin(n 1)を選択し、n 1はINにしない(myTableからnを選択)
  • Rドハティ


0


個人的には、SQL-AIUIでは、最悪の場合、O(n ^ 2)でテーブルを効果的にスキャンしなくても、異なる行にわたって分析を実行するのは困難です。 ただし、ストアドプロシージャを使用する方がおそらく簡単でしょう。

私の解決策は、可能であれば、新しい行が挿入されるたびに前の行がその行の最大値と新しい行の最小値の差で更新されるようにデータベーススキーマとコードを変更することです。その差分値はそれ自身の列に格納されます。

十分大きなギャップがある最初の行を検索すると、比較的簡単になります。


0


MySQLにhttp://www.oracle.com/technology/oramag/oracle/04-jan / o14tech_sql.html [モデル句]とありますか。 もしそうなら、あなたはそれでクエリでそれを行うことができます。


0


"20の穴は最大300行など2行を返すでしょう"私はあなたの論理には従っていません - 最大2行(300)と最小3行(330)の差は30です(どちらかを含む場合)最小値または最大値、そうでない場合は29。 これは、指定された値の「以上」の最初のギャップを探しているということですか、それとも完全に一致する必要がありますか? それが「以上」であるならば、確かに返される最初のマッチはそれと2行目の間に20以上のギャップがある行1です。

いずれにしても、例が示すように、テーブルに何らかの種類の行IDがある場合は、次のようにして試すことができます(行ID、MinVal、およびMaxVal列を含むテーブルMyTableに、例のデータが入力されているとします)。

SELECT TOP 1 a.RowID、a.MinVal、a.MaxVal、 - これはISNULL(b.MinVal、9999)として返す値です。AS MinVal_NextRow、ISNULL(b.MinVal、9999) -  a.MaxVal AS Diff MyTableの左からMyTable bのa.RowID =(b.RowID  -  1)WHERE(ISNULL(b.MinVal、9999) -  a.MaxVal)= 20

この例では、ギャップがちょうど20になる最初の行を選択します。 少なくとも20の最初のギャップを探していたら、WHERE句を次のように変更できます。

WHERE(ISNULL(b.MinVal、9999) -  a.MaxVal)> = 20

クエリは、行が利用可能な最後の行であるときに任意の大きな数(9999)を代入します - 適切なサイズのギャップがない場合は、これが最後の(最大の)MaxValを返します。 あなたはこの数字をあなたのデータにとって意味のあるものに調整する必要があるでしょう。 データ内の可能な値よりも大きい。