2011-12-15 2 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

Позвольте мне посмотреть, смогу ли я быть яснее. У меня есть таблица под названием History с столбцами mc_id, hs_opcode и hs_time. Для события входа будет уникальный mc_id, hs_opcode = 1, а hs_time - метка времени. В случае выхода из системы; уникальный mc_id, hs_opcode = 2, а hs_time - метка времени. Мне нужно обработать каждое событие входа во всей таблице и выполнить поиск соответствующего события выхода из системы. Таким образом, для события выхода из системы это будет следующая запись выхода для этой машины после события входа в систему; mc_id будет равно, hs_opcode = 2, а метка времени будет больше. Затем я вставляю это в новую таблицу. – jomille

+0

Ответ на ваш вопрос: нет, вам не нужно использовать курсоры для этого. Фактически, использование курсора, вероятно, будет намного медленнее, чем предлагаемое ниже решение на основе набора. –

ответ

2

Во-первых, ваш запрос будет проще (и быстрее), если вы можете заказать данные таким образом, что вам не нужен сложный подзапрос паре строк. Поскольку MySQL не поддерживает КТР, чтобы сделать это на лету, вам необходимо создать временную таблицу:

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 
); 

Затем вытяните и сортировки из исходной таблицы в новую таблицу:

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 записей, и для запуска потребовалось некоторое время. Полная база данных составляет более 2 миллионов записей. Я не думаю, что этот подход будет работать для базы данных такого размера. Любые другие идеи? – jomille

Смежные вопросы