how to do group by and order by in one query? - mysql

I have two table one is 'tb_student' and other is 'tb_fees'
create query for 'tb_student'
CREATE TABLE `tb_student` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`class` varchar(255) NOT NULL,
`created_on` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
)
create query for 'tb_fees'
CREATE TABLE `tb_fees` (
`id` int(11) NOT NULL auto_increment,
`email` varchar(255) NOT NULL,
`amount` varchar(255) NOT NULL,
`created_on` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
)
In first table i am storing the student details and in other table storing the fees details
I want to select student details from 'tb_student' and last add fee from 'tb_fees' only for those student which are in class 6
so i tried this
SELECT *
FROM tb_student s INNER JOIN
tb_fees f on
s.email =f.email
WHERE s.class = 6 GROUP BY s.email ORDER BY f.created_on DESC
This will give result only the first created how to get last created values
fees table
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (5,'ram#gmail.com','5000','2013-05-01 14:20:15');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (6,'Sam#gmail.com','5000','2013-05-02 14:20:23');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (7,'jak#gmail.com','5000','2013-05-03 14:20:30');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (8,'Sam#gmail.com','5000','2013-05-29 14:20:35');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (9,'ram#gmail.com','5000','2013-05-30 14:20:39');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (10,'jak#gmail.com','5000','2013-05-30 14:36:13');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (11,'rose#gmail.com','5000','2013-05-30 14:36:15');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (12,'nim#gmail.com','5000','2013-05-30 14:36:15');
Student table values
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (5,'Ram','ram#gmail.com','6','2013-04-30 14:00:56');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (6,'Sam','Sam#gmail.com','6','2013-03-30 14:01:30');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (7,'Nimmy','nim#gmail.com','7','2013-04-30 13:59:59');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (8,'jak','jak#gmail.com','6','2013-03-30 14:07:32');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (9,'rose','rose#gmail.com','5','2013-04-30 14:07:51');
Thank you

To get the latest fees something like this:-
SELECT s.* , f.*
FROM tb_student s
INNER JOIN
(SELECT email, MAX(created_on) AS created_on
FROM tb_fees
GROUP BY email) Sub1
ON s.email = sub1.email
INNER JOIN tb_fees f
ON s.email = f.email AND Sub1.created_on = f.created_on
WHERE s.class = 6
By the way, you probably want indexes on the email fields (or better, use the tb_student id field on the tb_fees table instead of the email field and index it)

Use MAX group function
SELECT s.*, f.amount,MAX(f.created_on)
FROM tb_student s
INNER JOIN
tb_fees f
ON
s.email =f.email
WHERE s.class = 6
GROUP BY s.email

Related

Left Join Specific Row

Think this is probably fairly simple but cannot find the correct search terms, so if this is duplicated then great cause im sure there will be an answer somewhere.
I have the following tables setup
CREATE TABLE IF NOT EXISTS `customer` (
`id` int(6) unsigned auto_increment NOT NULL,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `billing_run` (
`id` int(6) unsigned auto_increment NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `invoice` (
`id` int(6) unsigned auto_increment NOT NULL,
`billing_run_id` int(6) unsigned NOT NULL,
`customer_id` int(6) unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (billing_run_id) REFERENCES billing_run(id),
FOREIGN KEY (customer_id) REFERENCES customer(id)
) DEFAULT CHARSET=utf8;
with the following data
insert into customer (name) values ('test customer');
insert into billing_run (date) values ('2019-01-01 12:00:00');
insert into billing_run (date) values ('2019-02-01 12:00:00');
insert into billing_run (date) values ('2019-03-01 12:00:00');
insert into invoice (customer_id,billing_run_id) values (1,1);
SQLFiddle here -> http://sqlfiddle.com/#!9/a54162/5
And i want to get the customer records that do not have an invoice related to billing_run with id of 2
My query
select c.id from customer c
left join invoice i on i.customer_id = c.id
left join billing_run br on br.id = i.billing_run_id and br.id = 2
where i.id is null
returns 0 records. Why ?
First you join the table customer (1 row) with the table invoice (1 row).
This join will return 1 row because there is a match between the columns in the ON clause:
on i.customer_id = c.id
(both i.customer_id and c.id have the value 1 in your sample data).
So there is not any row with i.id is null.
The next join to the table billing_run does not affect the first 2 joined tables.
So the condition:
where i.id is null
returns no rows.
The correct condition (which you had in the original fiddle) is:
where br.id is null
because the join to the table billing_run will return a non matching row for the condition:
on br.id = i.billing_run_id and br.id = 2
because there is no i.billing_run_id = 2 in invoice.
You will want to do an exclusive where clause this will return the 1 row that you want.
select * from customer c
where c.id not in (Select customer_id from invoice i LEFT JOIN billing_run br on
i.billing_run_id=br.id WHERE br.id=2 and br.id is not null)
http://sqlfiddle.com/#!9/a54162/14
You don't need the billing_run table. So I think you intend:
select c.id
from customer c left join
invoice i
on i.customer_id = c.id and i.billing_run_id = 2
where i.id is null

SQL Select from two tables, get null values for columns if WHERE clause is false

http://sqlfiddle.com/#!9/cfd41ef/1
I want to get every row from table person and if the clause WHERE is false I want to get null values. Is there a way to do this? Thanks in advance.
CREATE TABLE `pet` (
`owner_id` INT(11) NULL DEFAULT NULL,
`pet_type` ENUM('DOG','CAT') NULL DEFAULT NULL,
`pet_name` VARCHAR(50) NULL DEFAULT NULL
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
CREATE TABLE `person` (
`id` INT(11) NULL DEFAULT NULL,
`first_name` VARCHAR(50) NULL DEFAULT NULL,
`last_name` VARCHAR(50) NULL DEFAULT NULL
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
INSERT INTO `person` (`id`, `first_name`, `last_name`) VALUES
(1, 'qwe', 'asd'),
(2, 'asd', 'fgh'),
(3, 'zxc', 'vbn');
INSERT INTO `pet` (`owner_id`, `pet_type`, `pet_name`) VALUES
(1, 'DOG', 'rex');
SELECT person.*, pet.pet_name FROM person LEFT JOIN pet ON person.id = pet.owner_id WHERE pet.pet_type = 'DOG'
Thank you Lukasz Szozda and Yogesh Sharma for your answer moving the condition to ON does what I wanted
You could move condition to ON:
SELECT person.*, pet.pet_name
FROM person
LEFT JOIN pet
ON person.id = pet.owner_id
AND pet.pet_type = 'DOG';
SQLFiddle Demo
Move to ON clause instead of going with where clause :
SELECT person.*, pet.pet_name
FROM person LEFT JOIN
pet
ON person.id = pet.owner_id and pet.pet_type = 'DOG';
You where clause turns into inner join which is the problem.

Using Join and Insert Statement in MySQL

I have an insert statement like this
INSERT INTO T_WorkContents ( user_id, working_date, working_start_time,
working_end_time, break_time, holiday_mark,
overtime_hours, overtime_reason, work_detail,
remarks, regist_user, regist_date,
update_user, update_date )
VALUES ( '00005', '2015-02-05', '8:0',
'17:0', '0:0', '0',
'0:0', NULL, NULL,
NULL, '00005', current_timestamp(),
'00005', current_timestamp()
);
I want to check whether the user is already deleted when insert the new row by join the table workcontent with the table user info on user_id and the condition is where userinfo.delete_flag = 0.
is there any way to do it?
I'm not sure I understand your question. But if you would like to only perform the insertion if the corresponding user exists and is marked as not deleted in your other table this should work:
INSERT INTO T_WorkContents ( user_id, working_date, working_start_time,
working_end_time, break_time, holiday_mark,
overtime_hours, overtime_reason, work_detail,
remarks, regist_user, regist_date,
update_user, update_date )
SELECT '00005', '2015-02-05', '8:0',
'17:0', '0:0', '0',
'0:0', NULL, NULL,
NULL, '00005', current_timestamp(),
'00005', current_timestamp()
FROM userinfo ui
WHERE ui.user_id = '00005' && ui.delete_flag = 0
If the userinfo doesn't contain a row with a user_id equal to 00005 and a delete_flag that is set to 0 then the select will return no rows and the insert query will in turn affect no rows.
You can use WHERE EXISTS. Try below
INSERT INTO funds (field1, field2)
values (x, xx)
WHERE EXISTS (
SELECT *
FROM table
WHERE userid = your_user_id
);

How implementation my wants with MySQL JOIN

_
Hello everyone!
I have table
CREATE TABLE `labels` (
`id` INT NULL AUTO_INCREMENT DEFAULT NULL,
`name` VARCHAR(250) NULL DEFAULT NULL,
`score` INT NULL DEFAULT NULL,
`before_score` INT NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
And I Have This Table
CREATE TABLE `scores` (
`id` INT NULL AUTO_INCREMENT DEFAULT NULL,
`name_id` INT NULL DEFAULT NULL,
`score` INT NULL DEFAULT NULL,
`date` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`)
);
And i want have result where labels.score - have value last scores.score sorted by scores.date and labels.before_score where have value penultimate scores.score sorted by scores.date. Can I do This Only on Mysql slq and how?
Thanks.
ADD
For example i have this data on first table:
INSERT INTO `labels` (id, name, score, before_score) VALUES (1, 'John', 200, 123);
INSERT INTO `labels` (id, name, score, before_score) VALUES (2, 'Eddie', 2000, 2000);
INSERT INTO `labels` (id, name, score, before_score) VALUES (3, 'Bob', 400, 3101);
And second table
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('1','1','12','2013-07-10');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('2','2','2000','2013-05-04');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('3','3','654','2012-09-12');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('4','1','123','2013-12-17');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('5','1','200','2014-04-25');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('6','3','3101','2013-12-02');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('6','2','2000','2015-12-02');
INSERT INTO `scores` (`id`,`name_id`,`score`,`date`) VALUES ('6','3','400','2013-12-02');
If I understand correctly, you need the last two scores for each name_id.
I would tackle this with temporary tables:
Step 1. Last score:
create temporary table temp_score1
select name_id, max(`date`) as lastDate
from scores
group by name_id;
-- Add the appropriate indexes
alter table temp_score1
add unique index idx_name_id(name_id),
add index idx_lastDate(lastDate);
Step 2. Penultimate score. The idea is exactly the same, but using temp_score1 to filter the data:
create temporary table temp_score2
select s.name_id, max(`date`) as penultimateDate
from scores as s
inner join temp_score1 as t on s.nameId = t.nameId
where s.`date` < t.lastDate
group by name_id;
-- Add the appropriate indexes
alter table temp_score2
add unique index idx_name_id(name_id),
add index idx_penultimateDate(penultimateDate);
Step 3. Put it all together.
select
l.id, l.name,
s1.lastScore, s2.penultimateScore
from
`labels` as l
left join temp_score1 as s1 on l.id = s1.name_id
left join temp_score2 as s2 on l.id = s2.name_id
You can put this three steps inside a stored procedure.
Hope this helps you.

Missing SQL entries when combining LEFT/INNER JOIN + GROUP BY

I have the following MySQL structure (minimized a lot):
CREATE TABLE `site_movies` (
`id` int(10),
`title` varchar(90),
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
INSERT INTO `site_movies` VALUES(1, 'Borrowers, The');
INSERT INTO `site_movies` VALUES(2, 'Louis C.K.: Chewed Up');
INSERT INTO `site_movies` VALUES(3, 'Louis C.K.: Shameless');
INSERT INTO `site_movies` VALUES(4, 'Vinni-Pukh');
CREATE TABLE `site_movies_directors` (
`id` mediumint(8),
`name` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
CREATE TABLE `site_movies_directors_connections` (
`movie_id` mediumint(8),
`director_id` mediumint(8)
) ENGINE=MyISAM;
CREATE TABLE `site_movies_seen` (
`object_id` int(10),
`date` varchar(10),
`rating` tinyint(2)
) ENGINE=MyISAM;
INSERT INTO `site_movies_seen` VALUES(1, '0', 4);
INSERT INTO `site_movies_seen` VALUES(2, '1293821757', 5);
INSERT INTO `site_movies_seen` VALUES(3, '1293821758', 7);
INSERT INTO `site_movies_seen` VALUES(4, '0', 6);
And then the following query (also minimized a lot):
SELECT m.title, s.date
FROM site_movies_seen s
INNER JOIN site_movies m ON s.object_id = m.id
LEFT JOIN site_movies_directors_connections AS mdc ON ( m.id = mdc.movie_id )
GROUP BY mdc.movie_id, s.date
ORDER BY s.date ASC
Prints:
title date
Borrowers, The 0
Louis C.K.: Chewed Up 1293821757
Louis C.K.: Shameless 1293821758
Notice that "Vinni-Pukh" is missing because it is the second entry in the _seen table with date = 0. How can I include all entires, even when several entires have the same timestamp?
Change your group by statement to this:
GROUP BY m.id, s.date
Your join condition says m.id = mdc.movie_id, so you might think that these two fields are always equal and therefore it doesn't matter whether you write m.id or mdc.movie_id in your GROUP BY. This is not true because you are using a LEFT JOIN, not an INNER JOIN. This means that mdc.movie_id can be NULL, and all NULL entries go into the same group.
Also, since you aren't selecting any columns from the site_movies_directors_connections you should omit it from the query completely.
SELECT m.title, s.date
FROM site_movies_seen s
INNER JOIN site_movies m ON s.object_id = m.id
GROUP BY m.id, s.date
ORDER BY s.date