合并一行中的两行 - 脏和慢的过程

时间:2013-12-14 12:27:16

标签: mysql sql stored-procedures join merge

我这里有一些mysql查询的问题。我知道如何修复它,但我想知道是否有更快更好的解决方案。 (我确定有。)

这就是我的表格的样子。

TABLE logger

id  keys    text            type_id
 1  2csi    consectetuer    1
 2  3vjk    lorem ipsum     2
 3  2csi    consectetuer    1
 4  3vjk    lorem ipsum     2
 5  j49a    consectetuer    1

我的记录器变得疯狂,记录的值键和文本作为单独的行。 90%使用正确的type_id。

TABLE broken_logger

id  keys    text            type_id
 1          consectetuer    1
 2  0a2v                    0
 3          lorem ipsum     2
 4  2csi                    1
 5          consectetuer    1
 6  3vjk                    2
 7          lorem ipsum     2
 8  3vjk                    2
 9          dolor sit amet  2
10          consectetuer    1
11  2csi                    1
12          lorem ipsum     2
13          nibh euismod    1
14          consectetuer    1
15  7kl4                    0
16  j49a                    1
17          Ut wisi enim    1
18          volutpat        2
19          sed diam        1
20  7kl4                    0

文本用于排序或合并无用,它用于以后的分析。所以它必须与具有相同type_id的键连接。 Type_id == 0是错误,应该被忽略。它只在带键的行中,没有文本行的type_id = 0。

我尝试了JOINS和UNION ALL,但老实说我不知道​​如何使用(OUTER | RIGHT | LEFT | CROSS)JOIN将一个键合并为一个文本。我总是为每个文本获取具有相同type_id的每个键。而且我不想将所有这些结合起来。只需将行与文本合并,而不使用带键但没有文本的行的键。

所以我尝试了这个脏程序:

BEGIN
    DECLARE num INT DEFAULT 0;
    DECLARE i INT DEFAULT 0;
    DECLARE k INT DEFAULT 1;
    DECLARE pid INT DEFAULT 0;
    DECLARE pkey VARCHAR(4) DEFAULT '';

-- all type_ids
WHILE k <= 2 DO

    -- select numer of keys for this type_id 
    SELECT count(*) INTO num
        FROM broken_logger
        WHERE type_id = k
        AND text = ''
        AND key != '';

    WHILE i < num DO

        -- select only one key for update and key id for later deletion
        SELECT id, key INTO pid, pkey
        FROM broken_logger
        WHERE type_id = k
        AND text = ''
        AND key != ''
        LIMIT 1;

        -- update only one text entry with one key
        UPDATE broken_logger
        SET key = pkey
        WHERE type_id = k
        AND text != ''
        AND key = ''
        LIMIT 1;

        -- delete used key
        DELETE broken_logger
        WHERE id = pid;

        SET i = i + 1;
    END WHILE;
    SET i = 0;
    SET k = k + 1;
END WHILE;
END;

它正在工作,但它无效且非常慢。我尝试了很多事情,我认为我是在圈子里跑。有人可以指点我或帮助JOINS / UNIONS让这个任务更干净吗?

1 个答案:

答案 0 :(得分:1)

如果我理解你的问题,键和文本按类型顺序出现(虽然可能混合使用不同的类型,以便类型1键后面跟着类型2文本,然后键入1文本),你只需要一个有效的把他们加在一起的方式。

为了解决这个问题,我使用了我在另一个Stack Overflow问题(Ranking by Group in MySQL)上找到的MySQL排名函数,它允许我按类型ID排名。它通过type_id对结果进行排序,然后递增cnt直到type_id更改为止。我从使用排名函数的查询中创建了两个表和密钥和文本表,然后将它们连接在一起以生成下面的结果。

<强> SQL

SET @prev := 0;
SET @cnt := 1;

CREATE TABLE sequentialkeys AS (
  SELECT  id, `key`, type_id,
        IF(@prev <> type_id, @cnt := 1, @cnt := @cnt + 1) AS rank, @prev := type_id
  FROM    logger
  WHERE type_id != 0 AND `key` != ''
  ORDER BY type_id, id
 );

SET @prev := 0;
SET @cnt := 1;

CREATE TABLE sequentialtext AS (
  SELECT  id, `text`, type_id,
        IF(@prev <> type_id, @cnt := 1, @cnt := @cnt + 1) AS rank, @prev := type_id
  FROM    logger
  WHERE type_id != 0 AND `text` != ''
  ORDER BY type_id, id
 );

SET @cnt := 0;
SELECT @cnt := @cnt + 1 AS id, a.*
FROM (
  SELECT k.key, t.text, t.type_id 
  FROM sequentialkeys AS k
  INNER JOIN sequentialtext AS t ON t.type_id = k.type_id AND t.rank = k.rank
  ORDER BY t.id
 ) AS a

SQLFiddle

<强>结果

ID  KEY     TEXT            TYPE_ID
1   2csi    consectetuer    1
2   3vjk    lorem ipsum     2
3   2csi    consectetuer    1
4   3vjk    lorem ipsum     2
5   j49a    consectetuer    1
  • 我无法在SQLFiddle中创建临时文件,但你当然可以这样做,而不是创建永久文件。否则,只需删除sequentialkeyssequentialtext
  • 我编辑了我的答案以保留订购,以防这是一项要求。