I want to build a discussion board. Therefore I have 3 tables: t_posts, t_boards, t_board_categories. Posts contain Threads (where post_post_id IS NULL) and replies to threads (where post_post_id IS NOT NULL), so it is self-referring. Now I want to show the last 5 posts (Threads and/or Replies) in all boards of all categories. The sql currently looks like this:
SELECT p.post_id,
p.post_board_id,
p.post_user_id,
p2.post_title,
bc.bc_name,
Max(p.post_date) post_date
FROM t_posts p
LEFT JOIN t_posts p2
ON ( p2.post_post_id IS NULL
AND ( p2.post_id = p.post_id
OR p.post_post_id = p2.post_id ) )
LEFT JOIN t_boards b
ON b.board_id = p.post_board_id
LEFT JOIN t_board_categories bc
ON bc.bc_id = b.board_bc_id
WHERE p.post_system_id = '1'
AND p.post_deleted = 0
AND bc.bc_deleted = 0
AND b.board_deleted = 0
GROUP BY p.post_id,
p2.post_id
ORDER BY p2.post_date DESC
LIMIT 5
The result is almost what I want, except of, that multiple replies in one thread will lead to showing only that one thread. What I want is 5 different threads to be shown with the most recent answers, not only one thread 5 times.
I join t_posts p2 for showing the original thread title.
I also tried adding DISTINCT p2.post_id to the SELECT, but it does not change anything.
Schema:
CREATE TABLE `t_board_categories` (
`bc_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`bc_name` varchar(128) COLLATE utf8_unicode_ci DEFAULT '',
`bc_prio` int(11) NOT NULL DEFAULT '0',
`bc_system_id` int(10) unsigned NOT NULL DEFAULT '0',
`bc_deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`bc_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `t_boards` (
`board_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`board_bc_id` int(10) unsigned DEFAULT NULL,
`board_name` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL,
`board_subtitle` text COLLATE utf8_unicode_ci,
`board_prio` int(11) NOT NULL DEFAULT '0',
`board_system_id` int(10) unsigned NOT NULL DEFAULT '0',
`board_deleted` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`board_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `t_posts` (
`post_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`post_user_id` bigint(20) unsigned DEFAULT NULL,
`post_title` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`post_text` text COLLATE utf8_unicode_ci NOT NULL,
`post_post_id` bigint(20) unsigned DEFAULT NULL,
`post_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`post_board_id` int(10) unsigned NOT NULL DEFAULT '0',
`post_system_id` int(10) unsigned NOT NULL DEFAULT '0',
`post_deleted` int(2) NOT NULL DEFAULT '0',
PRIMARY KEY (`post_id`)
) ENGINE=InnoDB AUTO_INCREMENT=205 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
INSERT INTO `t_board_categories` (`bc_id`, `bc_name`, `bc_prio`, `bc_system_id`, `bc_deleted`) VALUES (1, 'Test-Category', '1', '1', '0');
INSERT INTO `t_boards` (`board_id`, `board_bc_id`, `board_name`, `board_subtitle`, `board_prio`, `board_system_id`, `board_deleted`) VALUES (1, '1', 'Test-Board', 'Board Subtitle', '1', '1', '0');
INSERT INTO `t_posts` (`post_id`, `post_user_id`, `post_title`, `post_text`, `post_post_id`, `post_date`, `post_board_id`, `post_system_id`, `post_deleted`) VALUES
(1, 14, 'Thread 1', 'Test1', NULL, '2019-05-22 00:18:25', 1, 1, 0),
(2, 14, 'Thread 2', 'Test2', NULL, '2019-05-22 00:18:44', 1, 1, 0),
(3, 14, 'Thread 3', 'Test 3', NULL, '2019-05-22 00:18:51', 1, 1, 0),
(4, 19, 'Thread 4', 'Test 4', NULL, '2019-05-22 00:19:02', 1, 1, 0),
(5, 19, 'Thread 5', 'Test 5', NULL, '2019-05-22 00:19:07', 1, 1, 0),
(6, 19, 'Thread 6', 'Test 6', NULL, '2019-05-22 00:19:15', 1, 1, 0),
(7, 14, 'Reply', 'A', 5, '2019-05-22 00:19:46', 1, 1, 0),
(8, 14, 'Reply', 'B', 5, '2019-05-22 00:19:47', 1, 1, 0),
(9, 14, 'Reply', 'C', 5, '2019-05-22 00:19:49', 1, 1, 0);
Expected Result: One row for thread 1-5 with user-id and date of last reply (userid:14, date: 2019-05-22 00:19:49 for Thread 5, for Thread 1-4 last date is thread-date, userid is thread author userid)
I worked on this the whole evening, would be great to get some help. Thank you in advance!
Your sample data suggests, that post levels cannot span more than two levels (post_post_id cannot be an ID of a post where post_post_id isn't null). Assuming that, you can try to use correlated subqueries getting the sub posts and the post for a post, order them by their date descending and use LIMIT to pick the top one. Also sort the main query by the date of the last sub post descending and use LIMIT to get the top 5. Something along the lines of:
SELECT bc1.bc_id,
bc1.bc_name,
b1.board_id,
b1.board_name,
p1.post_id,
p1.post_title,
(SELECT p2.post_title
FROM t_posts p2
WHERE p1.post_id IN (p2.post_id,
p2.post_post_id)
ORDER BY p2.post_date DESC
LIMIT 1) last_post_title,
(SELECT p2.post_user_id
FROM t_posts p2
WHERE p1.post_id IN (p2.post_id,
p2.post_post_id)
ORDER BY p2.post_date DESC
LIMIT 1) last_post_user_id,
(SELECT p2.post_date
FROM t_posts p2
WHERE p1.post_id IN (p2.post_id,
p2.post_post_id)
ORDER BY p2.post_date DESC
LIMIT 1) last_post_date
FROM t_posts p1
INNER JOIN t_boards b1
ON b1.board_id = p1.post_board_id
INNER JOIN t_board_categories bc1
ON bc1.bc_id = b1.board_bc_id
WHERE p1.post_post_id IS NULL
ORDER BY last_post_date DESC
LIMIT 5;
db<>fiddle
Related
I have below query. In this I have yes and no case.
yes is accessing but else part is not working . Please have a look on this.
SELECT SalesChannel.name , count(Transaction.category_id) as count, (case when (Transaction.no_of_units > 0 and Transaction.mop > 0) THEN 'yes' ELSE 'No' END) AS Is_Present from outlets Outlet inner join transactions Transaction on Outlet.id = Transaction.outlet_id inner join sale_channels SalesChannel on SalesChannel.id = Outlet.sale_channel_id group by SalesChannel.name
the output should be as below
KU Electrical
Yes 6 2
No 1 2
6 is counter of KU and Yes refers the presence,similarly No is non presence of KU
select SalesChannel.name ,
Transaction.category_id,
count(Transaction.category_id) as count,
from outlets Outlet inner join transactions Transaction on Outlet.id = Transaction.outlet_id inner join sale_channels SalesChannel on SalesChannel.id = Outlet.sale_channel_id group by SalesChannel.name
below are three tables which i used
1. transactions
CREATE TABLE IF NOT EXISTS `transactions` (
`id` int(11) NOT NULL,
`zone_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
`sub_category_id` int(11) NOT NULL,
`brand_id` int(11) NOT NULL,
`model_id` int(11) NOT NULL,
`outlet_id` int(11) NOT NULL,
`no_of_units` int(11) NOT NULL,
`mop` decimal(10,2) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `zone_id`, `state_id`, `city_id`, `category_id`, `sub_category_id`, `brand_id`, `model_id`, `outlet_id`, `no_of_units`, `mop`) VALUES
(1, 2, 2, 2, 2, 1, 1, 1, 1, 3, '6.00'),
(2, 2, 2, 2, 2, 1, 1, 1, 1, 3, '6.00'),
(3, 1, 1, 1, 1, 1, 1, 1, 1, 4, '2.00'),
(4, 2, 2, 2, 1, 1, 1, 1, 2, 4, '2.00');
2.outlets
CREATE TABLE IF NOT EXISTS `outlets` (
`id` int(11) NOT NULL,
`outlet_code` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`zone_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
`sale_channel_id` int(11) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `outlets`
--
INSERT INTO `outlets` (`id`, `outlet_code`, `name`, `zone_id`, `state_id`, `city_id`, `sale_channel_id`, `is_active`, `created`, `modified`) VALUES
(1, '1508', 'Ashok electricals', 2, 2, 2, 1, 1, '2016-10-03 00:00:00', '2016-10-03 00:00:00'),
(2, '1233', 'vinayak electricals', 1, 1, 1, 2, 1, '2016-10-04 00:00:00', '2016-10-04 00:00:00');
3. sale_chennals
CREATE TABLE IF NOT EXISTS `sale_channels` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `sale_channels`
--
INSERT INTO `sale_channels` (`id`, `name`, `is_active`, `created`, `modified`) VALUES
(1, 'KU', 1, '2016-10-03 00:00:00', '2016-10-03 00:00:00'),
(2, 'Electricals', 1, '2016-10-04 00:00:00', '2016-10-04 00:00:00');
There is no data in tables that match for the else condition. Your condition is that "Transaction.no_of_units >0 AND Transaction.mop >0" that is not match in table value of both fields are greater than 0.
Otherwise, else condition works fine.
You are aggregating your data so as to get one row per SalesChannel.name. There may be some transaction records that result in 'Yes' and others in 'No' for a SalesChannel.name, so what then is Is_Present supposed to be?
Another issue with your query is that the sale channels are in a table. There are currently two of them, but there could be three or four or thousands sometime. A SQL query doesn't produce a result with a variable number of columns. The columns must be known beforehand. So a possible result could look like this:
Name Yes No
KU 6 1
Electrical 2 2
because you know you want it to be Yes or No only, no matter how many channels.
The query:
select
sc.name,
count(case when t.no_of_units > 0 and t.mop > 0 then 1 end) as yes,
count(case when t.no_of_units <= 0 or t.mop <= 0 then 1 end) as no
from sale_channels sc
join outlet o on o.sale_channel_id = sc.id
join transactions t on t.outlet_id = o.id;
I have mysql query given below. I which counter has been used. if i enter category Id 1 for 3 times then counter is coming 3 which is correct but with this i want if i do not enter then different coloumn should come with NO.
output should be
KU Electrical
Yes 6 2
No 1 2
In this KU and Electrical are my sale channel name. Yes means counter of enteries of KU and No means which have not entered. Please help out in this. i am struggling
select
SalesChannel.name,
Transaction.category_id,
count(Transaction.category_id) as "count"
from outlets Outlet
inner join transactions Transaction on Outlet.id = Transaction.outlet_id
inner join sale_channels SalesChannel on SalesChannel.id = Outlet.sale_channel_id
group by Transaction.category_id;
below are three tables which I used
1) transactions
CREATE TABLE IF NOT EXISTS `transactions` (
`id` int(11) NOT NULL,
`zone_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
`sub_category_id` int(11) NOT NULL,
`brand_id` int(11) NOT NULL,
`model_id` int(11) NOT NULL,
`outlet_id` int(11) NOT NULL,
`no_of_units` int(11) NOT NULL,
`mop` decimal(10,2) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `zone_id`, `state_id`, `city_id`, `category_id`, `sub_category_id`, `brand_id`, `model_id`, `outlet_id`, `no_of_units`, `mop`) VALUES
(1, 2, 2, 2, 2, 1, 1, 1, 1, 3, '6.00'),
(2, 2, 2, 2, 2, 1, 1, 1, 1, 3, '6.00'),
(3, 1, 1, 1, 1, 1, 1, 1, 1, 4, '2.00'),
(4, 2, 2, 2, 1, 1, 1, 1, 2, 4, '2.00');
2) outlets
CREATE TABLE IF NOT EXISTS `outlets` (
`id` int(11) NOT NULL,
`outlet_code` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`zone_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`city_id` int(11) NOT NULL,
`sale_channel_id` int(11) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `outlets`
--
INSERT INTO `outlets` (`id`, `outlet_code`, `name`, `zone_id`, `state_id`, `city_id`, `sale_channel_id`, `is_active`, `created`, `modified`) VALUES
(1, '1508', 'Ashok electricals', 2, 2, 2, 1, 1, '2016-10-03 00:00:00', '2016-10-03 00:00:00'),
(2, '1233', 'vinayak electricals', 1, 1, 1, 2, 1, '2016-10-04 00:00:00', '2016-10-04 00:00:00');
3) sale_chennals
CREATE TABLE IF NOT EXISTS `sale_channels` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `sale_channels`
--
INSERT INTO `sale_channels` (`id`, `name`, `is_active`, `created`, `modified`) VALUES
(1, 'KU', 1, '2016-10-03 00:00:00', '2016-10-03 00:00:00'),
(2, 'Electricals', 1, '2016-10-04 00:00:00', '2016-10-04 00:00:00');
SQL fiddle: http://sqlfiddle.com/#!9/3f497/1
You are grouping by category. That means you get one result row per category. In each of these rows you show the count and a sale channel name. This sale channel name is just one of the names found in the records for the category arbitrarily chosen.
I suppose you want to count per category and sale channel. Hence your group by clause should be group by SalesChannel.name, Transaction.category_id:
select
SalesChannel.name,
Transaction.category_id,
count(Transaction.category_id) as "count"
from outlets Outlet
inner join transactions Transaction on Outlet.id = Transaction.outlet_id
inner join sale_channels SalesChannel on SalesChannel.id = Outlet.sale_channel_id
group by SalesChannel.name, Transaction.category_id;
SQL fiddle: http://sqlfiddle.com/#!9/3f497/2
This result, however, doesn't show an entry for Electricals / category 2, because there is no transaction for this combination in the table. If you want to show a zero count for this, you'd have to create the complete result set (i.e. all combinations of channel and category, whether they have a transaction or not) first. Then you'd outer join the transactions:
select
sc.name,
c.id as category_id,
count(t.id) as "count"
from sale_channels sc
cross join categories c
left join outlets o on o.sale_channel_id = sc.id
left join transactions t on t.outlet_id = o.id and t.category_id = c.id
group by sc.name, c.id;
SQL fiddle: http://sqlfiddle.com/#!9/60e998/5
I need a sql that uses self join on max id from first table. Please look at the following image of table. I am grouping the table by service_id but I need the last message of each group. So For service group 5 message count should be 3 and last_message should be thirdMsg5. I wrote a sql below everything else is fine but it is throwing an error in the case of self join. It can't recognize msgTbl1.last_message_id. I think I am calling it before preparing it. I need help to solve this problem what would be the best sql to solve this in one query? And if possible please provide me this query in laravel query builder format.
SELECT count(msgTbl1.id) as message_count,
max(msgTbl1.id) as last_message_id,
msgTbl1.body,
msgTbl2.body as last_message,
services.name as service_name
FROM messages msgTbl1
LEFT JOIN (SELECT id, body FROM messages) AS msgTbl2
ON msgTbl2.id = msgTbl1.last_message_id
LEFT JOIN services on services.id = msgTbl1.service_id
WHERE receiver_id = 4 AND read_user = 'no'
GROUP BY msgTbl1.service_id
sql for the message table
CREATE TABLE `messages` (
`id` int(11) UNSIGNED NOT NULL,
`sender_id` int(11) UNSIGNED DEFAULT NULL,
`receiver_id` int(11) UNSIGNED DEFAULT NULL,
`service_id` int(11) UNSIGNED NOT NULL,
`sender_type` enum('user','agent','admin') NOT NULL,
`receiver_type` enum('user','agent','admin') NOT NULL,
`body` text,
`files` varchar(500) DEFAULT NULL COMMENT 'serialize',
`new_notification` enum('no','yes') NOT NULL DEFAULT 'yes',
`read_user` enum('yes','no') NOT NULL DEFAULT 'no',
`read_agent` enum('yes','no') NOT NULL DEFAULT 'no',
`status` enum('active','archive','deleted') NOT NULL DEFAULT 'active',
`created_at` datetime NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `messages` (`id`, `sender_id`, `receiver_id`, `service_id`, `sender_type`, `receiver_type`, `body`, `files`, `new_notification`, `read_user`, `read_agent`, `status`, `created_at`, `updated_at`) VALUES
(1, 22, 4, 5, 'user', 'agent', 'firstMsg5', NULL, 'yes', 'no', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:28'),
(2, 22, 4, 5, 'user', 'agent', 'secondMsg5', NULL, 'yes', 'no', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:31'),
(3, 22, 4, 9, 'user', 'agent', 'firstMsg9', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:45'),
(4, 4, 4, 9, 'agent', 'user', 'secondMsg9', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:56'),
(5, 22, 4, 5, 'user', 'agent', 'thirdMsg5', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:41:08');
Try this:
SELECT message_count,
last_message_id,
msgTbl1.body,
services.name as service_name
FROM messages msgTbl1
INNER JOIN (
SELECT MAX(id) AS last_message_id, COUNT(*) AS message_count
FROM messages
WHERE read_user = 'no'
GROUP BY service_id) AS msgTbl2
ON msgTbl1.id = msgTbl2.last_message_id
LEFT JOIN services on services.id = msgTbl1.service_id
WHERE receiver_id = 4
CREATE TABLE IF NOT EXISTS `tweets_comment_tbl` (
`tweet_comment_id` int(12) NOT NULL AUTO_INCREMENT,
`tweet_id` int(12) NOT NULL,
`tweets_comment` text NOT NULL,
`created` int(12) NOT NULL,
`changed` int(12) NOT NULL,
`uid` int(12) NOT NULL,
`userip` varchar(20) NOT NULL,
`referer` text NOT NULL,
`status` int(2) NOT NULL DEFAULT '1',
PRIMARY KEY (`tweet_comment_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
--
-- Dumping data for table `tweets_comment_tbl`
--
INSERT INTO `tweets_comment_tbl` (`tweet_comment_id`, `tweet_id`, `tweets_comment`, `created`, `changed`, `uid`, `userip`, `referer`, `status`) VALUES
(1, 1, 'COMMENT USER1', 1319395671, 1319395671, 3, '127.0.0.1', 'http://localhost/drupal_tutorial/', 1),
(2, 2, 'comment admin user', 1319395724, 1319395724, 1, '127.0.0.1', 'http://localhost/drupal_tutorial/node', 1),
(3, 2, 'USER COMMENTING HIS COMMENT', 1319395838, 1319395838, 3, '127.0.0.1', 'http://localhost/drupal_tutorial/', 1),
(4, 2, 'ADMIN COMMENTING FOR HIS COMMENT', 1319395865, 1319395865, 1, '127.0.0.1', 'http://localhost/drupal_tutorial/node', 1),
(5, 2, 'dddCOMMENT USER1: ADMIN DOING COMMENT FOR STATUS UPDATE1', 1319395905, 1319395905, 1, '127.0.0.1', 'http://localhost/drupal_tutorial/node', 1);
my second table
CREATE TABLE IF NOT EXISTS `tweets_tbl` (
`tweet_id` int(11) NOT NULL AUTO_INCREMENT,
`tweets` text NOT NULL,
`created` int(12) NOT NULL,
`changed` int(12) NOT NULL,
`uid` int(12) NOT NULL,
`userip` varchar(20) NOT NULL,
`referer` text NOT NULL,
`status` int(2) NOT NULL DEFAULT '1',
PRIMARY KEY (`tweet_id`),
KEY `tweet_id` (`tweet_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `tweets_tbl`
--
INSERT INTO `tweets_tbl` (`tweet_id`, `tweets`, `created`, `changed`, `uid`, `userip`, `referer`, `status`) VALUES
(1, 'STATUS UPDATE 1', 1319395633, 1319395633, 1, '127.0.0.1', 'http://localhost/drupal_tutorial/node', 1),
(2, 'Status update user1', 1319395696, 1319395696, 3, '127.0.0.1', 'http://localhost/drupal_tutorial/node', 1);
Trying join query
SELECT ut.picture as picture, tct.tweet_id as tweet_id, tct.tweets_comment as tweets_comment, tct.changed
FROM tweets_comment_tbl tct
RIGHT JOIN users as ut ON tct.uid=ut.uid AND tct.tweet_id=2 AND tct.status = 1
order by tct.created desc
return records for above query
picture tweet_id tweets_comment changed
1 COMMENT USER1 1319395671
NULL NULL NULL
picture-1.png NULL NULL NULL
actually what i expected is
picture tweet_id tweets_comment changed
1 COMMENT USER1 1319395671
Why query has been returning NULL records, i am not sure where i made mistake in join query.
The query returns all rows from users and joins tweets_comment_tbl based on condition in ON. If no records in tweets_comment_tbl matches userid, the record from users still included into recordset. You probably need INNER JOIN if you want to see just users with comments.
Side note :
I think it's almost always possible to avoid RIGHT JOIN; queries with LEFT JOIN are much easier to read and understand.
I'm building a bug tracker type tool for kicks.
I'm having probs with a small prob relating to version control of my data.
I have a table 'action' where I store all the data about the action (desription, who entered it, status etc). I also have a action_status table where each time the status is changed (from not asigned, in progress, complete etc) it is logged here..
What I can't seem to do is list the actions with their latest status value.
You'll note that the status table has two rows, one has been submitted, the otehr has not.. I ONLY want to see the row that has submitted = 0 (the latest date I'd presume..)
to make matters worse, each action has a revision Id and if the action text is changed, I'm creating a new entry in the action table with the same ID, but a new revision ID.. this however is working great.. but I thought I should mention in case it's interfering with my problem.
Here are my tables and some sample data:
Am I being a monkey?
CREATE TABLE IF NOT EXISTS `action` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_action` int(11) NOT NULL,
`id_priority` int(11) NOT NULL,
`revision` int(11) NOT NULL DEFAULT '1',
`reference` varchar(255) NOT NULL,
`department` int(11) NOT NULL,
`id_parent` int(11) NOT NULL DEFAULT '0',
`sort_order` int(11) NOT NULL,
`description` text NOT NULL,
`date_start` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`date_end` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`date_created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
--
-- Dumping data for table `action`
--
INSERT INTO `action` (`id`, `id_action`, `id_priority`, `revision`, `reference`, `department`, `id_parent`, `sort_order`, `description`, `date_start`, `date_end`, `date_created`) VALUES
(1, 1, 1, 1, '1', 1, 0, 2, 'Test Action revision test 1 a', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '2011-06-17 00:00:00'),
(2, 1, 1, 2, '0', 1, 0, 2, 'Test Action revision test 1 b', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '2011-06-17 00:00:00'),
(3, 2, 1, 1, '0', 1, 0, 1, 'Test Action revision test 2 a', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '2011-06-17 00:00:00'),
(4, 2, 1, 2, '0', 1, 0, 1, 'Test Action revision test 2 b', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '2011-06-17 00:00:00'),
(5, 3, 2, 1, '0', 1, 0, 0, 'Test Action revision test 3 b', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '2011-06-17 00:00:00');
-- --------------------------------------------------------
--
-- Table structure for table `action_status`
--
CREATE TABLE IF NOT EXISTS `action_status` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_department` int(11) NOT NULL,
`id_priority` int(11) NOT NULL,
`id_action` int(11) NOT NULL,
`status` int(11) NOT NULL,
`submitted` tinyint(4) NOT NULL,
`approved` tinyint(4) NOT NULL,
`published` tinyint(4) NOT NULL,
`date_now` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `action_status`
--
INSERT INTO `action_status` (`id`, `id_department`, `id_priority`, `id_action`, `status`, `submitted`, `approved`, `published`, `date_now`) VALUES
(1, 1, 1, 2, 3, 1, 1, 1, '2011-06-20 16:36:09'),
(2, 1, 1, 2, 5, 0, 0, 0, '2011-06-20 16:40:09');
CREATE TABLE IF NOT EXISTS `priority` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` text NOT NULL,
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `priority`
--
INSERT INTO `priority` (`id`, `description`) VALUES
(1, 'Test Priority'),
(2, '2nd Priority');
And my 'problem' SQL
SELECT `action`.`id_priority`, `priority`.`description` as priority, `action`.`reference`, `action`.`description` as action, `action`.`id_action`, `action`.`date_start`, `action`.`date_end`, `action`.`id_parent`, `action_status`.`status`, `action`.`revision`, `action_status`.`submitted`, `action_status`.`date_now`
FROM (`action`)
LEFT JOIN action_status ON
`action_status`.`id_action` = `action`.`id_action`
JOIN `priority` ON
`action`.`id_priority` = `priority`.`id`
WHERE
action.department = 1 AND
action.revision =(SELECT MAX(ar.revision) FROM action as ar WHERE action.id_action = ar.id_action)
GROUP BY `action`.`id_action`
ORDER BY `id_priority` asc, `id_parent` asc, `sort_order` asc
one good recommendation - if you ask for help, simplify your example as much as possible to show the basic root of your problem. No one will try to understand your complex code this way...
What I can't seem to do is list the actions with their latest status value.
You speak about latest status value, so I'd expect that you want the latest record from action_status table, but in your query you check for max revision in action table... And why you group by action_id, when you want to compaI must admit I don't understand the design at all.
Anyway, I think I was solving similar problem few weeks ago, see this MySQL feature reguest ( http://bugs.mysql.com/bug.php?id=2020 ) and my reply at '7 Jul 13:12'. See the following example, it should help you:
Suppose you have table with price for each goods in each store. For each goods, you want to see the minimal price and the related store, in which you get it for the price! Exactly the same as in your example - you want a record with max revision.
create table prices ( goods varchar(10), price double, store varchar(10) );
insert into prices values ('car', 200, 'Amazon'), ('car', 150, 'CarStore'), ('Bread', 2, 'Baker1'), ('Bread', 1, 'Baker2');
select goods, min(price), (select store from prices as p where p.goods = prices.goods
order by price limit 1) as store
from prices
group by goods;
Hope this helps. If you want to pick more columns, just pick an id instead of store and make it as a subquery and join the table again via the id.
If I understood it well and you need only actions with max revision, try something like this:
SELECT *
FROM
(SELECT `action`.`id_action`, max(`action`.`revision`),
(select id
from action as a
where a.id_action = action.id_action
order by revision desc
limit 1) as id_with_max_revision
FROM `action`
WHERE
action.department = 1
GROUP BY `action`.`id_action`
) as action_max_revision
JOIN action on action_max_revision.id_with_max_revision = action.id
LEFT JOIN action_status ON
`action_status`.`id_action` = `action`.`id_action`
JOIN `priority` ON
`action`.`id_priority` = `priority`.`id`
ORDER BY `id_priority` asc, `id_parent` asc, `sort_order` asc
The inner query selects actions with max revision, and the outer query does the other gimcrackery which is not the core of the problem :-)