Thursday, 30 August 2018

The MySQL: SELECT statement that compares values ​​across tables does not return the correct rows

I've searched a number of resources on SO and the web to try and figure out why my MySQL select statement isn't returning the correct rows/results. I apologize in advance if there's an answer here I missed and appreciate someone pointing it out to me.

A little background. Assume an email campaign application that has the tables: users, groups, campaigns, queue, managers_groups and settings. Each group has their own settings that has a flag called 'delivery_enabled' - 1 or 0. An important note is when a group is managed by another group, the 'managee' group automatically will use the managing group's settings. Thus , even if the 'managee' group's settings have delivery_enabled set to 1 and their managing group's is set to 0, theirs will also be treated as 0. I hope I explained that clearly :/
The following statement is used to return rows from the table 'queue' - the queue holds the messages/emails pending delivery by the application. The statement checks to see if the user's group settings.delivery_enabled = 1 - taking into consideration (via CASE) whether or not we should be using the user's own group settings or their managing group's settings.
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

The results I'm getting show that a group that has a managing group still uses their own settings and not the managing group's. I feel like something is wrong with my CASE statement in the WHERE CLAUSE, causing the query to fall-back on the ELSE.
If someone wants to try it out, here's the data I'm using for this.
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');

One more note is that I did try a scaled back version with only three tables and far less data which seemed to work fine, but once I used the same methodology in the larger scenario (which has more tables to reference against) it didn't work.
If more info is needed, let me know. Thanks in advance for those who can lend a hand. Much appreciated!

A faster and easier to understand way to handle this would be to just go ahead and left join both settings tables in, and then allow the manager settings to override the individual settings through the use of 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

0 comments:

Post a Comment