How to get multiple rows with order by and limit - mysql

I have a mysql table, the structure of that table is as follows:
CREATE TABLE IF NOT EXISTS `salary_log` (`salary_id` int(11) NOT NULL AUTO_INCREMENT, `salary` int(11) NOT NULL, `name` varchar(200) NOT NULL, `create_date` date NOT NULL, PRIMARY KEY (`salary_id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `salary_log` (`salary_id`, `salary`, `name`, `create_date`) VALUES (1, 5000, 'a', '2013-05-02'), (2, 8000, 'a', '2013-05-08'), (3, 4500, 'b', '2013-05-10'), (4,6000, 'c', '2013-05-08'), (5, 8000, 'b', '2013-05-17');
From the above table i need to fetch the latest records of each employee. I tried below query :
SELECT * FROM `salary_log` GROUP BY `name` ORDER BY `create_date` DESC;
But it returns be wrong data. Can someone please help me in this.

Try this:
SELECT s1.*
FROM `salary_log` AS s1
INNER JOIN
(
SELECT name, MAX(create_date) AS maxDate
FROM salary_log
GROUP BY `name`
) AS s2 ON s1.name = s2.name
AND s1.create_date = s2.maxDate;

Related

Joining data from 3 separate MySQL tables

I have 3 MySQL tables: person, review & team.
I have been able to join 2 (person & review) together, however I'd like to include data from the 3rd in my result.
Can someone explain how this is done? :-)
CREATE TABLE `person` (
`id` int NOT NULL AUTO_INCREMENT,
`reference` varchar(100) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
INSERT INTO `person` (`id`, `reference`, `email`) VALUES
(1, 'PK001', 'paulk#gmail.com');
CREATE TABLE `review` (
`id` int NOT NULL AUTO_INCREMENT,
`review_type` varchar(255) NOT NULL,
`review_body` varchar(255) NOT NULL,
`person_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
INSERT INTO `review` (`id`, `review_type`, `review_body`, `person_id`) VALUES
(1, 'Personality', 'He has a great personality!', 1),
(2, 'Skills', 'He has multiple skills!', 1);
CREATE TABLE `team` (
`id` int(11) NOT NULL,
`person_id` int(11) NOT NULL,
`team_name` varchar(255) NOT NULL,
`value` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `team` (`id`, `person_id`, `team_name`, `value`) VALUES
(1, 1, 'Man Utd', 500),
(2, 1, 'Real Madrid', 1500),
(3, 1, 'Ajax', 1000);
Using the following SQL:
SELECT p.id, group_concat(r.review_body)
FROM person p
inner join review r on r.person_id = p.id
group by p.id
gives me the output:
He has a great personality!,He has multiple skills!
However I'd ultimately like my output to be:
He has multiple skills!,He has a great personality,Man Utd-500|Real Madrid-1500|Ajax-1000
Is this possible to do with MySQL ? Any guidance would be greatly appreciated.
I realise I could optimise things a lot better - but I just want to see if I can connect all 3 tables together and go from there.
To get your required concatenated output you need to modify your join query.
For that your new query looks like this:
SELECT p.id,
GROUP_CONCAT(r.review_body) AS reviews,
(SELECT GROUP_CONCAT(CONCAT(team_name, '-', value) SEPARATOR '|')
FROM team
WHERE team.person_id = p.id) AS teams
FROM person p
INNER JOIN review r ON r.person_id = p.id
GROUP BY p.id;
Result :

showing counter in mysql query

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

MySQL: Select records where joined table matches ALL values

I'm trying to find all employees with multiple skills. Here are the tables:
CREATE TABLE IF NOT EXISTS `Employee` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`Name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO `Employee` (`ID`, `Name`, `Region_ID`) VALUES (1, 'Fred Flintstone'), (2, 'Barney Rubble');
CREATE TABLE IF NOT EXISTS `Skill` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`Name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO `Skill` (`ID`, `Name`) VALUES (1, 'PHP'), (2, 'JQuery');
CREATE TABLE IF NOT EXISTS `Emp_Skills` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`Emp_ID` bigint(20) unsigned NOT NULL DEFAULT '0',
`Skill_ID` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
INSERT INTO `Emp_Skills` (`ID`, `Emp_ID`, `Skill_ID`) VALUES (1, 1, 1), (2, 1, 2), (3, 2, 1);
Here is the query I have so far:
SELECT DISTINCT(em.ID), em.Name
FROM Employee em
INNER JOIN Emp_Skills es ON es.Emp_ID = em.ID
WHERE es.Skill_ID IN ('1', '2')
This returns both employees, however, I need to find the employee that has both skills (ID 1 and 2).
Any ideas? Thanks
This will do it:
SELECT EmpId, Name
FROM
(
SELECT em.ID as EmpId, em.Name, es.ID as SkillID
FROM Employee em
INNER JOIN Emp_Skills es ON es.Emp_ID = em.ID
WHERE es.Skill_ID IN ('1', '2')
) X
GROUP BY EmpID, Name
HAVING COUNT(DISTINCT SkillID) = 2;
Fiddle here:
The distinct is just in case the same employee has the skill listed twice.
Thanks for the test data.
You can do this with aggregation and a having clause:
SELECT em.ID, em.Name
FROM Employee em INNER JOIN
Emp_Skills es
ON es.Emp_ID = em.ID
GROUP BY em.id, em.name
HAVING sum(es.Skill_id = '1') > 0 and
sum(es.Skill_id = '2') > 0;
Each condition in the having clause counts the number of rows for each employee that have a particular skill. The filter guarantees that both skills are present.

MySQL Query - How do I get a SUM with GROUP BY and WHERE condition and use LEFT OUTER JOIN?

I'm not sure how to get the result that I expect and I've tried everything. I also need to be able to get the expected results using one query.
Here are the two tables with some sample data:
--
-- Table structure for table `accounts`
--
CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(11) NOT NULL,
`name` varchar(200) NOT NULL,
`type_id` int(11) NOT NULL,
`description` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Data for table `accounts`
--
INSERT INTO `accounts` (`id`, `name`, `type_id`, `description`) VALUES
(100, 'DUES', 0, NULL),
(101, 'NET WEEKLY PAYROLL', 0, NULL),
(111, 'FEDERAL TAX DEPOSITS', 0, 'tax stuff'),
(113, 'UNITED ASSOCIATION PAYMENTS', 0, NULL),
(114, 'OFFICERS MEETING ALLOWANCES', 0, NULL);
--
-- Table structure for table `checks`
--
CREATE TABLE IF NOT EXISTS `checks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batch_id` int(11) DEFAULT NULL,
`entry_date` date NOT NULL,
`account_id` int(11) NOT NULL,
`amount` decimal(10,2) NOT NULL,
`description` varchar(200) DEFAULT NULL,
`posted` tinyint(4) NOT NULL DEFAULT '0',
`vendor_id` int(11) DEFAULT NULL,
`check_num` int(11) NOT NULL,
`voided` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
--
-- Dumping data for table `checks`
--
INSERT INTO `checks` (`id`, `batch_id`, `entry_date`, `account_id`, `amount`, `description`, `posted`, `vendor_id`, `check_num`, `voided`) VALUES
(1, NULL, '2013-01-21', 111, '77.44', 'Last Year', 0, 1, 100, 0),
(2, NULL, '2014-01-21', 111, '521.11', 'Test Stuff', 0, 1, 101, 0),
(3, NULL, '2014-01-20', 101, '121.11', 'More Tests', 0, 1, 222, 0),
(4, NULL, '2014-01-02', 101, '150.00', 'test', 0, 4, 213, 0);
I want to create a list of all accounts with a month-to-date sum as an added field. Here is the query to get the month-to-date sum without joining the accounts table:
SELECT *, SUM(amount) as mtd FROM `checks` WHERE `entry_date` > '2014-01-01' GROUP BY `account_id`
... and here is what i used to get all the accounts joined to checks table:
SELECT * FROM `accounts` LEFT OUTER JOIN `checks` ON `checks.account_id` = `accounts.id`
I just can't seem to combine these two correctly to get the expected results. Please help!
I think this solves your problem:
SELECT a.*, SUM(c.amount) as mtd
FROM accounts a left outer join
checks c
ON (a.id = c.account_id) and c.entry_date >= '2014-01-01'
GROUP BY a.account_id;
This will return all accounts, even those with no activity in January. I changed the date condition to >=, because that "feels" better as a month-to-date cutoff.

MySQL - Correct approach event counting

I want to list users which have a particular event count but I'm confused on which approach to take.
This is the database table:
CREATE TABLE `event` (
`event_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`visitor_id` int(11) DEFAULT NULL,
`key` varchar(200) DEFAULT NULL,
`value` text,
`label` varchar(200) DEFAULT '',
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`event_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `event` (`event_id`, `visitor_id`, `key`, `value`, `label`, `datetime`)
VALUES
(1, 1, 'LOGIN', NULL, '', NULL),
(2, 2, 'LOGIN', NULL, '', NULL),
(3, 1, 'VIEW_PAGE', 'HOTEL', '', NULL),
(4, 2, 'VIEW_PAGE', 'HOTEL', '', NULL),
(5, 1, 'PURCHASE_HOTEL', NULL, '', NULL);
CREATE TABLE `visitor` (
`visitor_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime DEFAULT NULL,
PRIMARY KEY (`visitor_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `visitor` (`visitor_id`, `datetime`)
VALUES
(1, NULL),
(2, NULL);
and this is my approach:
SELECT DISTINCT
t1.`visitor_id`
FROM
`visitor` t1
JOIN `event` t2 on t1.visitor_id = t2.visitor_id AND t2.`key` = 'LOGIN'
JOIN `event` t3 on t1.visitor_id = t3.visitor_id AND t3.`key` = 'VIEW_PAGE' AND t3.`value` = 'HOTEL'
WHERE ( SELECT COUNT(*) FROM `event` WHERE `event`.`key` = 'PURCHASE_HOTEL' ) > 0
this should only list visitor 1 but it does actually list visitor 2 too which does not have the PURCHASE_HOTEL event.
As you can imagine, there will be more "rules" like all the JOIN events for each particular case. Can we correct and improve this somehow?
BONUS:
What is the name of this approach?
I think this is a "set-within-sets" query. I like using aggregation with a having clause for this type of query. The following checks the three conditions you are looking for:
select visitor_id
from event e
group by visitor_id
having sum(e.key = 'LOGIN') > 0 and
sum(e.key = 'VIEW_PAGE' and e.value = 'HOTEL') > 0 and
sum(e.key = 'PURCHASE_HOTEL') > 0;
The first condition in the having clause counts the number of LOGIN records and is true when at least one is found. (If you want exactly one, change > 0 to = 0.)
The second condition checks the viewing of the hotel page.
The third counts the number of hotel purchases.