我应该在 SQL 过程中使用游标吗?

发布于 2024-12-21 13:17:38 字数 510 浏览 3 评论 0原文

我有一个包含计算机登录和注销事件的表。每行都是一个单独的事件,带有时间戳、计算机名称、登录或注销事件代码以及其他详细信息。我需要创建一个 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

I have a table that contains computer login and logoff events. Each row is a separate event with a timestamp, machine name, login or logoff event code and other details. I need to create a SQL procedure that goes through this table and locates corresponding login and logoff event and insert new rows into another table that contain the machine name, login time, logout time and duration time.

So, should I use a cursor to do this or is there a better way to go about this? The database is pretty huge so efficiency is certainly a concern. Any suggested pseudo code would be great as well.

[edit : pulled from comment]

Source table:

History (
      mc_id
    , hs_opcode
    , hs_time
)

Existing data interpretation:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

挖个坑埋了你 2024-12-28 13:17:38

首先,如果您可以以不需要复杂的子查询来配对行的方式对数据进行排序,那么您的查询将会更简单(并且更快)。由于 MySQL 不支持 CTE 即时执行此操作,因此您需要创建一个临时表:

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 ;

First, your query will be simpler (and faster) if you can order the data in such a way that you don't need a complex subquery to pair up the rows. Since MySQL doesn't support CTE to do this on-the-fly, you'll need to create a temporary table:

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

Then, pull and sort from your original table into the new table:

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;

You can now use this query to correlate the data:

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;

For future inserts, you can use a trigger like below to keep your duration table updated automatically:

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 ;
墨离汐 2024-12-28 13:17:38
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

如果没有您正在处理的任何代码.. 很难看出这种方法是否有用.. 可能在某些情况下您确实需要循环遍历事物.. 在某些情况下这种方法可以使用代替..

[编辑:基于海报的评论]

你可以尝试执行这个并看看你是否得到了想要的结果?

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

Without any code that you're working on.. it'll be hard to see if this approach will be any useful.. There may be some instances when you really need to loop through things.. and some instances when this approach can be used instead..

[EDIT: based on poster's comment]

Can you try executing this and see if you get the desired results?

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
            )
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文