我查询子查询大约需要15秒才能完成。即使我不加入2张桌子,它也很慢,所以不是连接耗费时间。 jos_payplans_subscription表有8200行,大小为3.3 MB。
某些用户同时拥有有效和已过期的成员资格。我想找到所有用户如何不活动(状态= 1603)并且没有其他活动成员资格。
查询有效 - 只是很慢。
问题:我可以做得更好吗?
由于
SELECT c.id, c.name, c.username, c.email, a.plan_id, d.title, a.subscription_date, a.expiration_date
FROM jos_payplans_subscription a
LEFT JOIN jos_users c ON c.id = a.user_id
LEFT JOIN jos_payplans_plan d ON d.plan_id = a.plan_id
WHERE a.status = 1603
AND a.user_id NOT IN (SELECT b.user_id FROM jos_payplans_subscription b WHERE b.status = 1601)
这里是3个表的创建表
CREATE TABLE `jos_payplans_subscription` (
`subscription_id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`plan_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
`total` decimal(15,5) DEFAULT '0.00000',
`subscription_date` datetime DEFAULT '0000-00-00 00:00:00',
`expiration_date` datetime DEFAULT '0000-00-00 00:00:00',
`cancel_date` datetime DEFAULT '0000-00-00 00:00:00',
`checked_out` int(11) DEFAULT '0',
`checked_out_time` datetime DEFAULT '0000-00-00 00:00:00',
`modified_date` datetime DEFAULT '0000-00-00 00:00:00',
`params` text NOT NULL,
PRIMARY KEY (`subscription_id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=15103 DEFAULT CHARSET=utf8
CREATE TABLE `jos_payplans_plan` (
`plan_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`published` tinyint(1) DEFAULT '1',
`visible` tinyint(1) DEFAULT '1',
`ordering` int(11) DEFAULT '0',
`checked_out` int(11) DEFAULT '0',
`checked_out_time` datetime DEFAULT '0000-00-00 00:00:00',
`modified_date` datetime DEFAULT '0000-00-00 00:00:00',
`description` text,
`details` text,
`params` text,
PRIMARY KEY (`plan_id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=28 DEFAULT CHARSET=utf8
CREATE TABLE `jos_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(400) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`username` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`password` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`block` tinyint(4) NOT NULL DEFAULT '0',
`sendEmail` tinyint(4) DEFAULT '0',
`registerDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`lastvisitDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`activation` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`params` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`lastResetTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Date of last password reset',
`resetCount` int(11) NOT NULL DEFAULT '0' COMMENT 'Count of password resets since lastResetTime',
`otpKey` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Two factor authentication encrypted keys',
`otep` varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'One time emergency passwords',
`requireReset` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Require user to reset password on next login',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_block` (`block`) USING BTREE,
KEY `username` (`username`) USING BTREE,
KEY `email` (`email`) USING BTREE,
KEY `idx_name` (`name`(100)) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=23158 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
答案 0 :(得分:1)
我认为以下查询应该适合您。它使用LEFT JOIN而不是NOT IN,并且通常表现得更快。
SELECT c.id, c.name, c.username, c.email, a.plan_id, d.title, a.subscription_date, a.expiration_date
FROM jos_payplans_subscription a
LEFT JOIN jos_users c
ON c.id = a.user_id
LEFT JOIN jos_payplans_plan d
ON d.plan_id = a.plan_id
LEFT JOIN jos_payplans_subscription b
ON b.status = 1601
AND b.user_id = a.user_id
WHERE a.status = 1603
AND b.plan_id IS NULL
这假定jos_payplans_subscription.plan_id
不能为NULL。如果可以的话,选择任何其他非NULL的列用于查询的b.plan_id IS NULL
部分。
如果查询仍然太慢,那么我们需要查看您的索引和列数据类型,因此为您的问题涉及的所有表添加SHOW CREATE TABLE tablename
。
答案 1 :(得分:1)
如果您的表中没有索引,请不要忘记在搜索字段上添加索引。
答案 2 :(得分:0)
由于我们正在寻找只有1603会员资格的所有用户,我们可以使用EXCEPT尝试使用WITH子句。
编辑:好的,在MySql中没有EXCEPT,因此查询没有多大帮助。
WITH
users_1601 as (
SELECT user_id FROM jos_payplans_subscription WHERE status = 1601
),
subs_1603_only as (
SELECT a.user_id, a.plan_id, a.subscription_date, a.expiration_date
FROM jos_payplans_subscription a
LEFT JOIN users_1601 b ON a.user_id = b.user_id
WHERE a.status = 1603
AND b.user_id is NULL
)
SELECT
c.id, c.name, c.username, c.email,
subs.plan_id, subs.subscription_date, subs.expiration_date,
d.title
FROM subs_1603_only subs
LEFT JOIN jos_users c ON c.id = subs.user_id
LEFT JOIN jos_payplans_plan d ON d.plan_id = subs.plan_id
实际上,我只是把事情拖了一下。假设其他实践(例如正确的索引)已完成,我认为此查询的目标是确保不对主查询中的每一行执行子查询(状态1601的user_ids)。有时将其拉入WITH子句会有所帮助。
答案 3 :(得分:0)
NOT IN ( SELECT id ... )
更改为NOT EXISTS ( SELECT * ... AND b.user_id = a.user_id )
。原因:没有必要收集所有' ids,只需要看看是否存在。JOINing
之前收集您想要的ID c
和d
。也就是SELECT ... FROM ( SELECT ... ) LEFT JOIN c ... LEFT JOIN d ...