MySQL查询在UNION的每个查询上使用LIMIT和OFFSET进行搜索

时间:2015-08-06 19:06:56

标签: mysql

我想这更像是一个概念问题。

* discussion
discussionId
message
timestamp

* comment
commentId
message
timestamp

* tweet
tweetId
message
timestamp

然后在一个页面上我列出第一个让我们说10个条目,然后下一个将加载下一个10.一切正常。

我的问题是:有没有办法限制UNION的每个查询,或者我只能对UNION的结果进行LIMIT / OFFSET?我正在思考,因为我在想象每个表是否有1K(我知道数字非常低),查询将返回3K结果,然后只获取10.当行数变大时,它不会#39 ; t减慢性能?或者即使将来有更多的桌子需要我做UNION,那么获得每个的x并不是最好得到所需的10?但我无法弄清楚我是如何知道哪一个是查询的限制/偏移中的最后一个...所以我担心我必须选择所有并使用对UNION的限制。

所以不要使用

(SELECT 
    message,
    timestamp
FROM 
    discussion
)

UNION

(SELECT 
    message,
    timestamp
FROM 
    comment
)
UNION

(SELECT 
    message,
    timestamp
FROM 
    tweet
)
LIMIT $offset, $limit

是否有可能

(SELECT 
    message,
    timestamp
FROM 
    discussion
    LIMIT $offset, $limit
)

UNION

(SELECT 
    message,
    timestamp
FROM 
    comment
    LIMIT $offset, $limit
)
UNION

(SELECT 
    message,
    timestamp
FROM 
    tweet
    LIMIT $offset, $limit
)
LIMIT $offset, $limit

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

SQL Fiddle

MySQL 5.6架构设置

CREATE TABLE Message
    (`id` int, `type_id` int, `message` varchar(10), `timestamp` int)
;

INSERT INTO Message
    (`id`, `type_id`, `message`, `timestamp`)
VALUES
    (1, 1, 'message1', 12345678),
    (2, 2, 'message2', 12345679),
    (3, 3, 'message3', 12345680)
;


CREATE TABLE Type
    (`id` int, `type` varchar(10))
;

INSERT INTO Type
    (`id`, `type`)
VALUES
    (1, 'discussion'),
    (2, 'comment'),
    (3, 'tweet')
;


CREATE TABLE DiscussionDetail
    (`id` int, `message_id` int, `from_user` varchar(10), `to_user` varchar(10))
;

INSERT INTO DiscussionDetail
    (`id`, `message_id`, `from_user`, `to_user`)
VALUES
    (1, 1, 'Peter', 'Anna')
;


CREATE TABLE CommentDetail
    (`id` int, `message_id` int, `post_id` varchar(5), `user_id` varchar(5))
;

INSERT INTO CommentDetail
    (`id`, `message_id`, `post_id`, `user_id`)
VALUES
    (1, 2, 'post4', 'user6')
;


CREATE TABLE TweetDetail
    (`id` int, `message_id` int, `twitteracct` varchar(6))
;

INSERT INTO TweetDetail
    (`id`, `message_id`, `twitteracct`)
VALUES
    (1, 3, 'myacct')
;

查询1

SELECT Message.*, type,
case when dd.id is not null then from_user 
     when cd.id is not null then post_id  
     when td.id is not null then twitteracct else '' end as detail1,
case when dd.id is not null then to_user  
     when cd.id is not null then user_id else '' end as detail2
FROM Message
INNER JOIN Type ON Type_Id = Type.id
LEFT OUTER JOIN DiscussionDetail dd ON dd.message_id = Message.id AND Type_Id = 1
LEFT OUTER JOIN CommentDetail cd ON cd.message_id = Message.id AND Type_Id = 2
LEFT OUTER JOIN TweetDetail td ON td.message_id = Message.id AND Type_Id = 3
ORDER BY timestamp LIMIT 1,1

<强> Results

| id | type_id |  message | timestamp |    type | detail1 | detail2 |
|----|---------|----------|-----------|---------|---------|---------|
|  2 |       2 | message2 |  12345679 | comment |   post4 |   user6 |

您还可以为此创建一个VIEW:

CREATE VIEW AllMessages AS
SELECT Message.*, type,
case when dd.id is not null then from_user 
     when cd.id is not null then post_id  
     when td.id is not null then twitteracct else '' end as detail1,
case when dd.id is not null then to_user  
     when cd.id is not null then user_id else '' end as detail2
FROM Message
INNER JOIN Type ON Type_Id = Type.id
LEFT OUTER JOIN DiscussionDetail dd ON dd.message_id = Message.id AND Type_Id = 1
LEFT OUTER JOIN CommentDetail cd ON cd.message_id = Message.id AND Type_Id = 2
LEFT OUTER JOIN TweetDetail td ON td.message_id = Message.id AND Type_Id = 3

然后:

SELECT *
FROM AllMessages
ORDER BY timestamp LIMIT 1,1

SQL Fiddle

答案 1 :(得分:1)

我喜欢你的问题,我自己曾经问过一次。我使用了第二个检索所有内容的解决方案,然后对我需要的内容进行分页。现在,问题在于性能,所以在这里我将尝试提高检索行的性能,有几千行(这个解决方案必须进行测试,但我认为它会顺利运行)。

棘手的性能理念:创建一个新表,只使用表的ID,查找时间戳(或每个表共享的列,并用于排序),然后按此过滤。这样,您将拥有:

SELECT insert_time
FROM all_tables_order 
ORDER BY insert_time DESC
LIMIT 0,5

从那里,您获取边界(第一行和最后一行结果,采用insert_time,您可以在http://sqlfiddle.com/#!9/043c1/29中看到),然后创建具有该限制的大SQL:

SELECT *
FROM (
    SELECT t.id as id,
          t.tweet as message,
          t.insert_time as insert_time
    FROM tweets t
    WHERE insert_time <= '2015-08-06 21:53:30' 
    AND insert_time >= '2015-08-06 21:51:34'

    UNION ALL

    SELECT c.id as id,
          c.`comment` as message,
          c.insert_time as insert_time
    FROM comments c
    WHERE insert_time <= '2015-08-06 21:53:30' 
    AND insert_time >= '2015-08-06 21:51:34'

    UNION ALL

    SELECT m.id as id,
          m.message as message,
          m.insert_time as insert_time
     FROM messages m 
     WHERE insert_time <= '2015-08-06 21:53:30' 
     AND insert_time >= '2015-08-06 21:51:34'
) AS myWholeTable
ORDER BY insert_time

这应该非常快,尽管您正在进行两次查询,因为您的结果已编入索引,并且您只排序小结果:http://sqlfiddle.com/#!9/043c1/30

您的数据库架构应具有以下内容,其中的触发器用于更新索引表:

表和数据:

CREATE TABLE IF NOT EXISTS `all_tables_order` (
  `id` int(11) DEFAULT NULL,
  `insert_time` timestamp NULL DEFAULT NULL,
  `table_name` enum('comments','tweets','messages') DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `all_tables_order` (`id`, `insert_time`, `table_name`) VALUES
    (1, '2015-08-06 21:50:52', 'messages'),
    (2, '2015-08-06 21:51:34', 'comments'),
    (1, '2015-08-06 21:52:10', 'tweets'),
    (2, '2015-08-06 21:52:46', 'messages'),
    (2, '2015-08-06 21:53:07', 'tweets'),
    (3, '2015-08-06 21:53:30', 'comments'),
    (1, '2015-08-03 21:53:39', 'comments');
CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `comment` tinytext CHARACTER SET latin1,
  `insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `Índice 1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT;

INSERT INTO `comments` (`id`, `comment`, `insert_time`) VALUES
    (2, 'c1', '2015-08-06 21:51:34'),
    (3, 'c3', '2015-08-06 21:53:30'),
    (1, 'c2', '2015-08-03 21:53:39');

CREATE TABLE IF NOT EXISTS `messages` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `message` tinytext,
  `insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `Índice 1` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `messages` (`id`, `message`, `insert_time`) VALUES
    (1, 'm1', '2015-08-06 21:50:52'),
    (2, 'm2', '2015-08-06 21:52:46');

CREATE TABLE IF NOT EXISTS `tweets` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `tweet` tinytext,
  `created_by` int(11) DEFAULT NULL,
  `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  KEY `Índice 1` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT;

INSERT INTO `tweets` (`id`, `tweet`, `created_by`, `insert_time`) VALUES
    (1, 't1', 23, '2015-08-06 21:52:10'),
    (2, 't2', 25, '2015-08-06 21:53:07');

触发器:

DELIMITER //
CREATE TRIGGER `comments_before_insert` AFTER INSERT ON `comments` FOR EACH ROW BEGIN
    insert into all_tables_order set insert_time = new.insert_time, id = new.id, table_name = 'comments';
END//
DELIMITER ;

CREATE TRIGGER `messages_after_insert` AFTER INSERT ON `messages` FOR EACH ROW BEGIN
    insert into all_tables_order set insert_time = new.insert_time, id = new.id, table_name = 'messages';
END//
DELIMITER ;

DELIMITER //
CREATE TRIGGER `tweets_after_insert` AFTER INSERT ON `tweets` FOR EACH ROW BEGIN
    insert into all_tables_order set insert_time = new.insert_time, id = new.id, table_name = 'tweets';
END//
DELIMITER ;