2

私は次のMySQLクエリを持っています:

SELECT from_unixtime( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` <> 0 AND `last` < '2012-10-01 00:00:00';

そして、次の結果が得られます。

1054-不明な列'最後の'の'where句'

lastこれはもちろん仮想列であり、存在しないため、WHEREステートメントでは使用できません。「lastlogin」にはUNIXタイムスタンプが含まれています。

ただし、whereステートメントで仮想列を使用する適切な方法はありますか?

ご協力いただきありがとうございます。

4

3 に答える 3

4

これは機能するはずです:

SELECT a.`last`
FROM   (SELECT From_unixtime(`lastlogin`) AS `last` 
        FROM   `players` 
        WHERE  `lastlogin` <> 0) a 
WHERE  a.`last` < '2012-10-01 00:00:00'; 
于 2013-01-25T23:10:42.750 に答える
4

TL; DR

選択した回答のクエリは「機能するはずです」。些細なセットでもパフォーマンスは良好です。しかし、大きなセットでは、それは素晴らしいパフォーマンスになるでしょう。

最高のパフォーマンスを得るには、範囲条件で裸の列を参照するクエリを使用します。このようなもの:

SELECT from_unixtime( `lastlogin` ) AS `last`
  FROM `players`
 WHERE `lastlogin` > 0
   AND `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00')

元の回答

(クエリ内の)列エイリアスlastは、(クエリ内の)WHERE句で参照できません。これはMySQLの構文規則です。(基本的に、このルールの理由は、WHERE句の述語が処理されるときに、そのエイリアスによって参照される式が使用できないためです。)

HAVINGただし、その列エイリアスは、句の述語で参照できます。句は、実行プランのHAVINGほぼ最後に処理されます。これは機能しますが、パフォーマンスの観点から問題があります。

SELECT from_unixtime( `lastlogin` ) AS `last`
  FROM `players` WHERE `lastlogin` <> 0 
 HAVING `last` < '2012-10-01 00:00:00';

その実行プランは、インラインビューを使用して(派生テーブルを作成するため)、派生テーブルに対してSELECTを実行するのとほぼ同じです(これはパフォーマンスの観点からも問題があります)。

SELECT d.*
  FROM (
         SELECT from_unixtime( `lastlogin` ) AS `last`
         FROM `players` WHERE `lastlogin` <> 0 
       ) d
WHERE `last` < '2012-10-01 00:00:00';

last内部クエリ(dとしてエイリアスされたインラインビュー)が最初に実行され、結果セットがMyISAMテーブルとして保存されるため、ここで列エイリアスを参照できます。次に、外部クエリがMyISAMテーブルに対して実行されます。また、このクエリでは、lastはテーブルから列名を参照しているため、WHERE句で参照できます。

重要な注意点:

そういうわけであなたが尋ねた質問に答えます。だが、

あなたは本当にそれをしたくありません!

このクエリは基本的にテーブルのコピーを作成し、そのコピーに対してクエリを実行するため、このアプローチでは大きなセットに対して問題のあるパフォーマンスが見られます。そのfrom_unixtime関数は、テーブル内のすべての行に対して実行されます(lastloginがNULLまたはゼロに等しい行を除く)。

パフォーマンスの観点からは、単一のクエリ(インラインビューなし)を使用し、WHERE句の裸の列に述語を使用する方がはるかに優れています。

理想的にlastloginは、DATETIMEまたはTIMESTAMPデータ型として格納されます。ただし、その列が整数である場合は、述語のリテラル定数を整数に変換してから、それを裸の列と比較する方がはるかに優れています(パフォーマンスの面で)。これにより、MySQLは、リテラルと比較するために、すべての行で関数をlastlogin実行するのではなく、少なくとも先頭の列としてあるインデックスで範囲スキャンを実行することを検討できます。from_unixtime

UNIX_TIMESTAMP()MySQLは、DATETIMEを整数値に変換するための便利な関数を提供します(これは基本的にFROM_UNIXTIME()関数の逆ですが、いくつかの注意点があります。

最高のパフォーマンスを得るには、次の形式のクエリが必要です。

SELECT from_unixtime( `lastlogin` ) AS `last`
  FROM `players`
 WHERE `lastlogin` <> 0
   AND `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00')
于 2013-01-25T23:49:02.723 に答える
2

サブクエリで列エイリアスを定義すると、列エイリアスを参照できますが、サブクエリはすべての行の中間結果セットを生成する必要があり、外部クエリの条件によってそれらを破棄する必要があるため、これは効率的ではありません。結果セットをできるだけ早く一致する行に減らす方が効率的です。

列がunixtime形式の場合lastloginは、WHERE句の定数式と比較できます。

SELECT FROM_UNIXTIME( `lastlogin` ) AS `last`
FROM `players` WHERE `lastlogin` < UNIX_TIMESTAMP('2012-10-01 00:00:00');

これは、その列のインデックスの恩恵を受け、結果セットのサイズを減らすことができます。

lastlogin <> 0とにかくゼロの日付を保存するべきではないので、私は取り出しました。「値なし」を意味する特別な値としてゼロを使用している場合は、代わりにNULLを使用する必要があります。


コメントを再確認してください:はい、正しい関数名はUNIX_TIMESTAMP()です。訂正ありがとうございます、私は上記を編集しました。

于 2013-01-25T23:20:23.873 に答える