比较多个表中的值的 SELECT 语句未返回正确的行

Posted

技术标签:

【中文标题】比较多个表中的值的 SELECT 语句未返回正确的行【英文标题】:SELECT statement that compares values across multiple tables isn't returning the correct rows 【发布时间】:2012-04-16 08:40:46 【问题描述】:

我在 SO 和网络上搜索了许多资源,试图找出我的 mysql select 语句没有返回正确的行/结果的原因。如果这里有我错过的答案,我提前道歉,并感谢有人向我指出。

一点背景。假设一个电子邮件活动应用程序具有以下表格:用户、组、活动、队列、managers_groups 和设置。每个组都有自己的设置,有一个名为“delivery_enabled”的标志 - 1 或 0。重要的一点是,当一个组由另一个组管理时,“managee”组将自动使用管理组的设置。因此,即使“managee”组的设置将 delivery_enabled 设置为 1,并且他们的管理组设置为 0,他们的设置也将被视为 0。我希望我解释清楚:/

以下语句用于从表“队列”返回行 - 队列保存等待应用程序传递的消息/电子邮件。该语句检查用户的组 settings.delivery_enabled = 1 - 考虑(通过 CASE)我们是应该使用用户自己的组设置还是他们管理组的设置。

SELECT 
t1.*
FROM 
queue t1, 
campaigns t2, 
users t3,
managers_groups t4, 
settings t5  
WHERE
( t1.campaign_status = 3 && t1.campaign_id = t2.id && t2.user_id = t3.id && t5.delivery_enabled = 1 ) && 
CASE ( SELECT 1 FROM managers_groups WHERE managee_group_id = t3.group_id )
 WHEN 1 THEN t4.manager_group_id = t5.group_id 
 ELSE t3.group_id = t5.group_id
END
GROUP BY t1.id 
ORDER BY t1.send_at ASC, t1.id ASC

我得到的结果表明,拥有管理组的组仍然使用他们自己的设置,而不是管理组的设置。我觉得 WHERE CLAUSE 中的 CASE 语句有问题,导致查询回退到 ELSE。

如果有人想尝试一下,这是我正在使用的数据。

CREATE TABLE IF NOT EXISTS `campaigns` (
  `id` int(12) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(12) NOT NULL,
  `name` varchar(150) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=17 ;

--
-- Dumping data for table `campaigns`
--

INSERT INTO `campaigns` (`id`, `user_id`, `name`) VALUES
(16, 72, 'Steve''s Campaign'),
(13, 83, 'Kelly''s Campaign'),
(14, 77, 'Narek''s Campaign'),
(15, 75, 'Cynthia''s Campaign');

-- --------------------------------------------------------

--
-- Table structure for table `groups`
--

CREATE TABLE IF NOT EXISTS `groups` (
  `id` int(12) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(150) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=110 ;

--
-- Dumping data for table `groups`
--

INSERT INTO `groups` (`id`, `name`) VALUES
(108, 'Managers 002'),
(107, 'Managers 001'),
(106, 'Members 001 - Group B'),
(104, 'Members 002 - Group A'),
(103, 'Members 001 - Group A');

-- --------------------------------------------------------

--
-- Table structure for table `managers_groups`
--

CREATE TABLE IF NOT EXISTS `managers_groups` (
  `id` int(12) unsigned NOT NULL AUTO_INCREMENT,
  `manager_group_id` int(12) NOT NULL,
  `managee_group_id` int(12) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=280 ;

--
-- Dumping data for table `managers_groups`
--

INSERT INTO `managers_groups` (`id`, `manager_group_id`, `managee_group_id`) VALUES
(274, 108, 104),
(279, 107, 103);

-- --------------------------------------------------------

--
-- Table structure for table `queue`
--

CREATE TABLE IF NOT EXISTS `queue` (
  `id` int(12) NOT NULL AUTO_INCREMENT,
  `campaign_id` int(12) NOT NULL,
  `campaign_status` int(2) NOT NULL DEFAULT '0',
  `send_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=20 ;

--
-- Dumping data for table `queue`
--

INSERT INTO `queue` (`id`, `campaign_id`, `campaign_status`, `send_at`) VALUES
(1, 16, 3, '2012-04-01 20:05:45'),
(2, 16, 3, '2012-04-01 20:05:45'),
(3, 16, 3, '2012-04-01 20:05:45'),
(4, 16, 3, '2012-04-01 20:05:45'),
(5, 15, 3, '2012-04-01 20:00:18'),
(6, 15, 3, '2012-04-01 20:00:18'),
(7, 15, 3, '2012-04-01 20:00:18'),
(8, 15, 3, '2012-04-01 20:00:18'),
(9, 15, 3, '2012-04-01 20:00:18'),
(10, 15, 3, '2012-04-01 20:00:18'),
(11, 15, 3, '2012-04-01 20:00:18'),
(12, 14, 3, '2012-04-01 20:00:06'),
(13, 14, 3, '2012-04-01 20:00:06'),
(14, 14, 3, '2012-04-01 20:00:06'),
(15, 14, 3, '2012-04-01 20:00:06'),
(16, 14, 3, '2012-04-01 20:00:06'),
(17, 14, 3, '2012-04-01 20:00:06'),
(18, 13, 3, '2012-04-01 19:59:53'),
(19, 13, 3, '2012-04-01 19:59:53');

-- --------------------------------------------------------

--
-- Table structure for table `settings`
--

CREATE TABLE IF NOT EXISTS `settings` (
  `id` int(12) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` int(12) unsigned NOT NULL,
  `delivery_enabled` int(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=22 ;

--
-- Dumping data for table `settings`
--

INSERT INTO `settings` (`id`, `group_id`, `delivery_enabled`) VALUES
(19, 107, 1),
(20, 108, 0),
(18, 106, 0),
(16, 104, 1),
(15, 103, 1);

-- --------------------------------------------------------

--
-- Table structure for table `users`
--

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(12) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` int(12) NOT NULL,
  `username` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=86 ;

--
-- Dumping data for table `users`
--

INSERT INTO `users` (`id`, `group_id`, `username`) VALUES
(83, 106, 'kelly'),
(77, 104, 'narek'),
(75, 104, 'cynthia'),
(72, 103, 'steve');

还有一点需要注意的是,我确实尝试了一个缩减版本,它只有三个表和更少的数据,这似乎工作正常,但是一旦我在更大的场景中使用了相同的方法(有更多的表可供参考)它没用。

如果需要更多信息,请告诉我。

【问题讨论】:

【参考方案1】:

处理此问题的一种更快、更容易理解的方法是继续将两个设置表加入其中,然后允许管理器设置通过使用 Coalesce() 覆盖各个设置。

Select
    queue.*,
    Coalesce(manager_group_settings.delivery_enabled, user_settings.delivery_enabled) As setting_delivery_enabled
From queue
Inner Join campaigns On queue.campaign_id = campaigns.id
Inner Join users On queue.user_id = users.id
Left Outer Join managers_groups On managers_groups.managee_group_id = users.group_id
Left Outer Join settings As user_settings On user_settings.group_id = users.group_id
Left Outer Join settings As manager_group_settings On manager_group_settings.group_id = managers_groups.managee_group_id
Group By queue.id
Order By queue.send_at, queue.id

【讨论】:

我没有针对您的表格测试代码,所以如果您发现错误,请告诉我。另请注意,我将加入您的经理和 2 个设置实例。如果既没有用户设置也没有管理组设置,则 delivery_enabled 可能为空。 很好的方法!当我对数据集运行查询时(我确实删除了第一个 Inner Join 行之后的唯一反斜杠,因为它导致了错误),我得到了一个“'on Clause'中的未知列'queue.user_id'”。我将第二个内部联接更改为“Inner Join users On campaign.user_id = users.id”并运行它。结果没有优先考虑经理组的设置。结果显示每个自己的组的设置,无论它们是否由另一个组管理。希望我说得通。 这意味着 manager_groups 的左外连接没有选择任何行。是否有可能没有 managee_group_id 等于用户的 group_id 的数据?也许我在那个连接上使用了错误的列? 只有拥有经理组的组才会在 manager_groups 中注明。如果一个组没有经理组,那么在 manager_groups 中将没有该组的记录。当 group_id 等于 managee_group_id 时,表队列和活动中可能有也可能没有数据。但是,group_id 的设置中总会有数据。 我明白了!在您的最后一个“开”子句中,您有... = managers_groups.managee_group_id。应该是... = managers_groups.manager_group_id。我现在要重新调整它的用途,只返回 'setting_delivery_enabled' 为 1 的那些,然后我应该是金色的。因此,为了查看对原始查询的更改,在您的第二个内部联接中,我将queue.user_id 更改为campaigns.user.id,然后在最后一个左外部联接中,我将managers_groups.managee_group_id 更改为managers_groups.manager_group_id。感谢您对此的耐心和勤奋!非常感谢,乔丹!【参考方案2】:

在 WHERE 子句中的 CASE 语句中:

SELECT 'has_manager' FROM managers_groups WHERE managee_group_id = t3.group_id )
 WHEN 'has_manager'

'has_manager' 是一个字符串文字。选择它总是产生字符串文字'has_manager'。因此,您的 CASE 语句只采用第一个分支。

也许你的意思是CASE (SELECT has_manager ...) WHEN 1 THEN ...

【讨论】:

很好,Hammerite。我已经进行了更改并将 '(SELECT has_manager...' 更改为 '(SELECT 1...' 因为我遇到了未知列错误。运行新查询仍然会产生不正确的结果。使用我的数据集,我得到了应该只有 4 行时返回了 17 行。当我将 't5.delivery_enabled = 1' 更改为 't5.delivery_enabled = 0' 时,当我应该只得到 15 行时,我从队列中得到了所有 19 行。奇怪。虽然不是像查询一样干净,它看起来仍然应该可以工作。

以上是关于比较多个表中的值的 SELECT 语句未返回正确的行的主要内容,如果未能解决你的问题,请参考以下文章

多个 If 语句 - 未返回正确的值

oracle语句,我想查询A表中的a字段下的值不等于B表中b的值的数据,

SQL语句

将表中的日期与 TableAU 中的多个日期范围进行比较:构建 WHERE 语句

postgresql 一个字段多个值的查询sql怎么写

在 plpgsql 函数中访问 select 语句结果