How implementation my wants with MySQL JOIN - mysql

_
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.

Related

mysql query to join five tables

want to join tables
i have five tables like this
Table-1 named as software
CREATE TABLE IF NOT EXISTS `software` (
`software_name` varchar(50) NOT NULL,
`software_version` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `software` (`software_name`, `software_version`) VALUES
('freemap', '1.0'),
('freegps', '1.2');
Table-2 named as cms
CREATE TABLE IF NOT EXISTS `cms` (
`cms_name` varchar(50) NOT NULL,
`cms_product` varchar(50) NOT NULL,
`cms_version` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `cms` (`cms_name`, `cms_product`, `cms_version`) VALUES
('org:freemap:1.0', 'freemap', '1.0'),
('org:freegps:1.0', 'freegps', '1.2');
Table-3 named as cms_to_sve
CREATE TABLE IF NOT EXISTS `cms_to_sve` (
`cms_id` varchar(50) NOT NULL,
`sw_vul_id` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `cms_to_sve` (`cms_id`, `sw_vul_id`) VALUES
('org:freemap:1.0', '423'),
('org:freemap:1.0', '424'),
('org:freemap:1.0', '425'),
('org:freemap:1.0', '426'),
('org:freegps:1.2', '940'),
('org:freegps:1.2', '941');
Table-4 named as software_details
CREATE TABLE IF NOT EXISTS `software_details` (
`sw_id` varchar(50) NOT NULL,
`sve_id` varchar(50) NOT NULL,
`score` varchar(50) NOT NULL,
`ratio` varchar(50) NOT NULL,
`swe_id` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `software_details` (`sw_id`, `sve_id`, `score`, `ratio`, `swe_id`) VALUES
('423', '2001-1991', '5', 'high', '320'),
('424', '2004-1996', '7.5', 'medium', '460'),
('425', '2008-9001', '8', 'low', '122'),
('426', '2012-0002', '4', 'high', '128'),
('940', '2003-1993', '6', 'medium', '424'),
('941', '2006-1994', '3', 'high', '112');
Table-5 named as swe
CREATE TABLE IF NOT EXISTS `swe` (
`swe_name` varchar(50) NOT NULL,
`swe_id` varchar(50) NOT NULL,
`swe_des` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `swe` (`swe_name`, `swe_id`, `swe_des`) VALUES
('ref software', '320', 'hello'),
('ref complicated', '480', 'hi welcome'),
('ref contact', '122', 'how are you'),
('ref admire', '123', 'who is that'),
('ref super', '424', 'well join us'),
('ref nice', '112', 'cheers');
i want to join these five tables
i have few hints
need to compare table 1 and table 2 (that is table
software and table cms)
compare software_name with cms_product and
software_version with cms_version
these should relate with cms_name
with second table column cms_name has to join with the third
table common column cms_id
where in third table cms_id must be equal to sw_vul_id
then join fourth table
now join fourth table from third table using sw_vul_id is equal
to sw_id and get the remaining column values
join fifth table and fourth table by swe-id and get the other
column values
finally i want to have an output like this
i need query for this .
Just like PeteCon said in his comment , left join can be used, hope this will help.
select tbl1.software_name, tbl1.software_version, tbl4.sve_id, tbl4.score, tbl4.ratio, tbl5.swe_id, tbl5.swe_name, tbl5.swe_des
from software tbl1
left join cms tbl2 on tbl1.software_name = tbl2.cms_product and tbl1.software_version = tbl2.cms_version
left join cms_to_sve tbl3 on tbl2.cms_name = tbl3.cms_id
left join software_details tbl4 on tbl3.sw_vul_id = tbl4.sw_id
left join swe tbl5 on tbl4.swe_id = tbl5.swe_id
http://sqlfiddle.com/#!9/3735e7/1
I think some of your sample data is not sufficient to get the result that you want, but in your real database the query should give you something like you want.

Taking the most commonly occuring (modal) value for several columns

I am cleaning records that have poorly recorded and inconsistent socio-demographic information over time, for the same person. I want to take the most commonly occuring value (the mode) for each person.
One way to do that is to partition by id and then count how many times each value occurs, retaining the highest count for each id:
DROP TABLE dbo.table
SELECT DISTINCT [id], [ethnic_group] AS [ethnic_mode], ct INTO dbo.table
FROM (
SELECT row_number() OVER (PARTITION BY [id] ORDER BY count([ethnic_group]) DESC) as rn, count([ethnic_group]) as ct, [ethnic_group], [id]
FROM
dbo.mytable GROUP BY [id], [ethnic_group]) ranked
where rn = 1
ORDER BY ct DESC
But I want to do this for several variables (ethnic group, income group and several more).
How can I select the mode for several variables within one statement and insert into one table (rather than creating separate tables for each variable)?
The table below illustrates an example of what I want to do:
DROP TABLE mytable;
CREATE TABLE mytable(
id VARCHAR(2) NOT NULL PRIMARY KEY
,ethnic_group VARCHAR(12) NOT NULL
,ethnic_mode VARCHAR(11) NOT NULL
,income VARCHAR(6) NOT NULL
,income_mode VARCHAR(11) NOT NULL
);
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('id','ethnic_group','ethnic_mode','income','income_mode');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','white','white','middle','middle');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','white','white','middle','middle');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','mixed','white','high','middle');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','asian','asian','middle','middle');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','mixed','asian','middle','middle');
INSERT INTO mytable(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','asian','asian','middle','middle');
I would use sub-queries to accomplish this in 1 insert statement.
Here is an example based on the table structure from your illustration:
/* This is the original table and contains duplicate ID's */
DECLARE #source_table TABLE(
id VARCHAR(2) NOT NULL
,ethnic_group VARCHAR(12) NULL
,ethnic_mode VARCHAR(11) NULL
,income VARCHAR(6) NULL
,income_mode VARCHAR(11) NULL
);
/* This is the destination table and will not contain duplicate ID's */
DECLARE #destination_table TABLE(
id VARCHAR(2) NOT NULL PRIMARY KEY
,ethnic_group VARCHAR(12) NULL
,income VARCHAR(6) NULL
);
/* Populate the source table with data */
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','white','white','middle','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','white','white','middle','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('1','mixed','white','high','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','asian','asian','middle','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','mixed','asian','middle','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('2','asian','asian','middle','middle');
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('3','asian', NULL, NULL, NULL);
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('3',NULL, NULL,'middle', NULL);
INSERT INTO #source_table(id,ethnic_group,ethnic_mode,income,income_mode) VALUES ('3',NULL, NULL, NULL, NULL);
/* Insert from source into destination (removing duplicates) */
INSERT INTO #destination_table
(
id
, ethnic_group
, income
)
SELECT st.id
, (
SELECT TOP 1 ethnic_group
FROM #source_table sub_st
WHERE sub_st.id = st.id
GROUP BY ethnic_group
ORDER BY COUNT(sub_st.id) DESC
)
, (
SELECT TOP 1 income
FROM #source_table sub_st
WHERE sub_st.id = st.id
GROUP BY income
ORDER BY COUNT(sub_st.id) DESC
)
FROM #source_table st
GROUP BY st.id
/* View the destination to see there are no duplicates */
SELECT id
, ethnic_group
, income
FROM #destination_table

How to use INSERT ... SELECT with a particular column auto-incrementing, starting at 1?

I am using INSERT ... SELECT to insert a data from specific columns from specific rows from a view into a table. Here's the target table:
CREATE TABLE IF NOT EXISTS `queue` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`customerId` int(11) NOT NULL,
`productId` int(11) NOT NULL,
`priority` int(11) NOT NULL,
PRIMARY KEY (`ID`),
KEY `customerId` (`customerId`),
KEY `productId` (`productId`),
KEY `priority` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
The INSERT ... SELECT SQL I have works, but I would like to improve it if possible, as follows: I would like the inserted rows to start with 1 in the priority column, and each subsequent row to increment the priority value by 1. So, if three rows were inserted, the first would be priority 1, the second 2, and the third 3.
A exception to the "start at 1" rule: if there are existing rows in the target table for the specified customer, I would like the inserted rows to start with MAX(priority)+1 for that customer.
I thought I could use a subquery, but here's the problem: sometimes the subquery returns NULL (when there are no records in the queue table for the specified customer), which breaks the insert, as the priority column does not allow nulls.
I tried to CAST the column to an integer, but that still gave me NULL back when there are no records with that customer ID in the table.
I've hardcoded the customer ID in this example, but naturally in my application that would be an input parameter.
INSERT INTO `queue`
(
`customerId`,
`productId`,
`priority`,
`status`,
`orderId`)
SELECT
123, -- This is the customer ID
`PRODUCT_NO`,
(SELECT (MAX(`priority`)+1) FROM `queue` WHERE `customerId` = 123),
'queued',
null
FROM
`queue_eligible_products_view`
Is there a way to do this in one SQL statement, or a small number of SQL statements, i.e., less than SQL statement per row?
I do not think I can set the priority column to auto_increment, as this column is not necessarily unique, and the auto_increment attribute is used to generate a unique identity for new rows.
As Barmar mentions in the comments : use IFNULL to handle your sub query returning null. Hence:
INSERT INTO `queue`
(
`customerId`,
`productId`,
`priority`,
`status`,
`orderId`)
SELECT
123, -- This is the customer ID
`PRODUCT_NO`,
IFNULL((SELECT (MAX(`priority`)+1) FROM `queue` WHERE `customerId` = 123),1),
'queued',
null
FROM
`queue_eligible_products_view`
Here's how to do the incrementing:
INSERT INTO queue (customerId, productId, priority, status, orderId)
SELECT 123, product_no, #priority := #priority + 1, 'queued', null
FROM queue_eligible_products_view
JOIN (SELECT #priority := IFNULL(MAX(priority), 0)
FROM queue
WHERE customerId = 123) var

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

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

MySQL Select Records from 2 not-related Tables Ordering by Timestamp

I'd like to collect data from 2 different mysql tables ordering the result by a timestamp but without merging the columns of the 2 tables in a single row.
T_ONE(one_id,one_someinfo,one_ts)
T_TWO(two_id,two_otherinfo,two_ts)
Notice that the field two_otherinfo is not the same as one_someinfo, the only columns in common are id and timestamp.
The result should be a mix of the two tables ordered by the timestamp but each row, depending on the timestamp, should contain only the respective columns of the table.
For example, if the newest record comes from T_TWO that row should have the T_ONE one_someinfo column empty.
I just need to order the latest news from T_ONE and the latest messages posted on T_TWO so the tables are not related. I'd like to avoid using 2 queries and then merging and ordering the results by timestamp with PHP. Does anyone know a solution to this? Thanks in advance
This is the structure of the table
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL auto_increment,
`fromid` int(10) NOT NULL,
`toteam` int(10) NOT NULL,
`banned` tinyint(1) NOT NULL default '0',
`replyid` int(15) default NULL,
`cont` mediumtext NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE `stars` (
`id` int(10) unsigned NOT NULL auto_increment,
`daynum` int(10) NOT NULL,
`userid` int(10) NOT NULL,
`vote` tinyint(2) NOT NULL default '3',
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#1', 1222222220);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#2', 1222222221);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#3', 1222222223);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(3, 160, 4, 1222222222);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(4, 180, 3, 1222222224);
The result ordering by timestamp DESC should be the second record of table stars with timestamp 1222222224 then the third record of table posts with timestamp 1222222223 and following... Since the tables have got different fields from each other, the first row of the result should contain the columns of the table stars while the columns of table posts should be empty.
The columns of a UNION must be the same name and datatype on every row. In fact, declare column aliases in the first UNION subquery, because it ignores any attempt to rename the column in subsequent subqueries.
If you need the columns from the two subqueries to be different, put in NULL as placeholders. Here's an example, fetching the common columns id and timestamp, and then fetching one custom column from each of the subqueries.
(SELECT p.id, p.timestamp AS ts, p.fromid, NULL AS daynum FROM posts)
UNION
(SELECT s.id, s.timestamp, NULL, s.daynum, FROM stars)
ORDER BY ts DESC
Also put the subqueries in parentheses, so the last ORDER BY applies to the whole result of the UNION, not just to the last subquery.
SELECT one_id AS id, one_someinfo AS someinfo, one_ts AS ts
UNION
SELECT two_id AS id, two_someinfo AS someinfo, two_ts AS ts
ORDER BY ts
SELECT one_id AS id
, one_someinfo AS one_someinfo
, NULL AS two_someinfo
, one_ts AS ts
FROM t_ONE
UNION ALL
SELECT two_id
, NULL
, two_someinfo
, two_ts
FROM t_TWO
ORDER BY ts