2011-12-15 3 views
1

私はコンピュータのログインとログオフイベントを含むテーブルを持っています。各行は、タイムスタンプ、マシン名、ログインまたはログオフのイベントコードなどの別個のイベントです。私はこのテーブルを通過するSQLプロシージャーを作成し、対応するログインおよびログオフ・イベントを探し出し、マシン名、ログイン時間、ログアウト時間および継続時間を含む別の表に新しい行を挿入する必要があります。SQLプロシージャでカーソルを使用する必要がありますか?

これを行うにはカーソルを使用する必要がありますか、これについてはもっと良い方法がありますか?データベースは非常に巨大なので、効率は確かに関心事です。提案された擬似コードも同様に優れています。

[編集:コメントから引き出さ]

ソーステーブル:

History (
     mc_id 
    , hs_opcode 
    , hs_time 
) 

既存データの解釈:あなたが取り組んでいる任意のコードがなければ

Login_Event = unique mc_id, hs_opcode = 1, and hs_time is the timestamp 
Logout_Event = unique mc_id, hs_opcode = 2, and hs_time is the timestamp 
+0

私がもっと分かりやすいかどうかを見てみましょう。私は、列mc_id、hs_opcode、およびhs_timeを持つHistoryというテーブルを持っています。ログインイベントの場合は、一意のmc_id、hs_opcode = 1、hs_timeがタイムスタンプです。ログアウトイベント。一意のmc_id、hs_opcode = 2、hs_timeはタイムスタンプです。私はテーブル全体で各ログインイベントを処理し、対応するログアウトイベントを検索する必要があります。したがって、ログアウトイベントの場合は、ログインイベント後のそのマシンの次のログアウトレコードになります。 mc_idは等しく、hs_opcode = 2になり、タイムスタンプは大きくなります。それから私はこれを新しいテーブルに挿入します。 – jomille

+0

あなたの質問に対する答えはいいえです。これにはカーソルを使う必要はありません。実際には、カーソルの使用は、以下で提案するセットベースのソリューションよりもはるかに遅くなる可能性があります。 –

答えて

2

まずあなたが行をペアリングするために複雑な副問合せを必要としないような方法でデータを注文することができれば、あなたのクエリが単純(かつ迅速)になります。

:新しいテーブルに元のテーブルから引き出し、並べ替え、その後

CREATE TABLE history_ordered (
    seq INT NOT NULL PRIMARY KEY AUTO_INCREMENT, 
    hs_id INT, 
    mc_id VARCHAR(255), 
    mc_loggedinuser VARCHAR(255), 
    hs_time DATETIME, 
    hs_opcode INT 
); 

:MySQLはオンザフライでこれを行うには、CTEをサポートしていませんので、あなたは一時テーブルを作成する必要がありますあなたは今のデータを相関させるために、このクエリを使用することができます

INSERT INTO history_ordered (
    hs_id, mc_id, mc_loggedinuser, 
    hs_time, hs_opcode) 
SELECT 
    hs_id, mc_id, mc_loggedinuser, 
    hs_time, hs_opcode 
FROM history ORDER BY mc_id, hs_time; 

:将来の挿入のために

SELECT li.mc_id, 
     li.mc_loggedinuser, 
     li.hs_time as login_time, 
     lo.hs_time as logout_time 
FROM history_ordered AS li 
JOIN history_ordered AS lo 
    ON lo.seq = li.seq + 1 
    AND li.hs_opcode = 1; 

を、あなたはあなたの持続テーブルが自動的に更新を維持するために、次のようにトリガーを使用することができます。

DELIMITER $$ 
CREATE TRIGGER `match_login` AFTER INSERT ON `history` 
FOR EACH ROW 
BEGIN 
IF NEW.hs_opcode = 2 THEN 
    DECLARE _user VARCHAR(255); 
    DECLARE _login DATETIME; 
    SELECT mc_loggedinuser, hs_time FROM history 
    WHERE hs_time = (
    SELECT MAX(hs_time) FROM history 
    WHERE hs_opcode = 1 
    AND mc_id = NEW.mc_id 
) INTO _user, _login; 
    INSERT INTO login_duration 
    SET machine = NEW.mc_id, 
    logout = NEW.hs_time, 
    user = _user, 
    login = _login; 
END IF; 
END$$ 
DELIMITER ; 
+0

+1 ---実行時間は、私のポストのようなサブクエリ – Nonym

2
CREATE TABLE dummy (fields you'll select data into, + additional fields as needed) 

INSERT INTO dummy (columns from your source) 
SELECT * FROM <all the tables where you need data for your target data set> 

UPDATE dummy SET col1 = CASE WHEN this = this THEN that, etc 

INSERT INTO targetTable 
SELECT all columns FROM dummy 

..このアプローチが有益なものになるかどうかは分かりません。本当に必要なときがあります.. ..物事を通じてOOPといくつかの例では、このアプローチを代わりに使用することができたときに

[EDIT:ポスターのコメントに基づい]

はこれを実行しようとすると、あなたが望む結果を得るかどうかを確認することはできますか?

INSERT INTO <your_target_table_here_with_the_three_columns_required> 
SELECT li.mc_id, li.hs_time AS login_time, lo.hs_time AS logout_time 
FROM 
    history AS li 
    INNER JOIN history AS lo 
     ON li.mc_id = lo.mc_id 
      AND li.hs_opcode = 1 
      AND lo.hs_opcode = 2 
      AND lo.hs_time = (
       SELECT min(hs_time) AS hs_time 
       FROM history 
       WHERE hs_time > li.hs_time 
       AND mc_id = li.mc_id 
      ) 
+0

これらの追加情報は役に立ちますか? – jomille

+0

私の投稿を更新しました。それを確認してください... – Nonym

+0

私はそれが動作すると信じて!しかし、私はすべてのレコードのかなり小さなサブセットでそれを試しました。約3000件のレコードがあり、実行にはしばらく時間がかかりました。データベース全体は200万レコードを超えています。私はこのアプローチが、そのサイズのデータ​​ベースでうまくいくとは想像もしません。他のアイデア? – jomille

関連する問題