I get combined rows when i use join query - mysql

I use the following query:
select *
from TEST2 t1 join TEST2 t2
where t1.TID <> t2.TID
and t1.ST = t2.ST
and t1.SAL > t2.SAL
and t1.TR < t2.TR;
I have 16 attributes in my table. But when i fire the above query i get result of two rows combined in one row with 32 attributes.
I want to get the result as two different rows and not one row, i.e the combination of both in one row.
6|Mark|Murro|M|970|-3134|Denver|CO|80251|S|Y|70000|4|0|0|0|0
2|Chunho|Black|M|719|-4662|Denver|CO|80290|M|N|60000|5|0|0|0
As shown above, both the rows get combined and comes as
6|Mark|Murro|M|970|-3134|Denver|CO|80251|S|Y|70000|4|0|0|0|0|2|Chunho|Black|M|719|-4662|Denver|CO|80290|M|N|60000|5|0|0|0
Table Schema :
CREATE TABLE `TEST2` (
`TID` int(11) DEFAULT NULL,
`FN` text,
`LN` text,
`GD` text,
`AC` int(11) DEFAULT NULL,
`PH` text,
`CT` text,
`ST` text,
`ZIP` int(11) DEFAULT NULL,
`MS` text,
`CH` text,
`SAL` int(11) DEFAULT NULL,
`TR` int(11) DEFAULT NULL,
`STX` int(11) DEFAULT NULL,
`MTX` int(11) DEFAULT NULL,
`CTX` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
But i want them seperate
Please help me out

It is a little unclear what you are really trying to do, but the following should implement your logic
select t.*
from test2 t
where exists (select 1
from test2 t2
where t.tid <> t2.tid and t.st = t2.st and t.sal > t2.sal and t.tr < t2.tr
) or
exists (select 1
from test2 t2
where t.tid <> t2.tid and t.st = t2.st and t.sal < t2.sal and t.tr > t2.tr
);
Because you want both rows, it needs to check for the conditions in both directions.

Related

Optimize tables MySQL

I have a query that is executed in 35s, which is waaaaay too long.
Here are the 3 tables concerned by the query (each table is approx. 13000 lines long, and should be much longer in the future) :
Table 1 : Domains
CREATE TABLE IF NOT EXISTS `domain` (
`id_domain` int(11) NOT NULL AUTO_INCREMENT,
`domain_domain` varchar(255) NOT NULL,
`projet_domain` int(11) NOT NULL,
`date_crea_domain` int(11) NOT NULL,
`date_expi_domain` int(11) NOT NULL,
`active_domain` tinyint(1) NOT NULL,
`remarques_domain` text NOT NULL,
PRIMARY KEY (`id_domain`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Table 2 : Keywords
CREATE TABLE IF NOT EXISTS `kw` (
`id_kw` int(11) NOT NULL AUTO_INCREMENT,
`kw_kw` varchar(255) NOT NULL,
`clics_kw` int(11) NOT NULL,
`cpc_kw` float(11,3) NOT NULL,
`date_kw` int(11) NOT NULL,
PRIMARY KEY (`id_kw`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Table 3 : Linking between domain and keyword
CREATE TABLE IF NOT EXISTS `kw_domain` (
`id_kd` int(11) NOT NULL AUTO_INCREMENT,
`kw_kd` int(11) NOT NULL,
`domain_kd` int(11) NOT NULL,
`selected_kd` tinyint(1) NOT NULL,
PRIMARY KEY (`id_kd`),
KEY `kw_to_domain` (`kw_kd`,`domain_kd`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The query is as follows :
SELECT ng.*, kd.*, kg.*
FROM domain ng
LEFT JOIN kw_domain kd ON kd.domain_kd = ng.id_domain
LEFT JOIN kw kg ON kg.id_kw = kd.kw_kd
GROUP BY ng.id_domain
ORDER BY kd.selected_kd DESC, kd.id_kd DESC
Basically, it selects all domains, with, for each one of these domains, the last associated keyword.
Does anyone have an idea on how to optimize the tables or the query ?
The following will get the last keyword, according to your logic:
select ng.*,
(select kw_kd
from kw_domain kd
where kd.domain_kd = ng.id_domain and kd.selected_kd = 1
order by kd.id_kd desc
limit 1
) as kw_kd
from domain ng;
For performance, you want an index on kw_domain(domain_kd, selected_kd, kw_kd). In this case, the order of the fields matters.
You can use this as a subquery to get more information about the keyword:
select ng.*, kg.*
from (select ng.*,
(select kw_kd
from kw_domain kd
where kd.domain_kd = ng.id_domain and kd.selected_kd = 1
order by kd.id_kd desc
limit 1
) as kw_kd
from domain ng
) ng left join
kw kg
on kg.id_kw = ng.kw_kd;
In MySQL, group by can have poor performance, so this might work better, particularly with the right indexes.

How to INNER JOIN around a loop of tables

I have four tables as follows:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `categories_friends` (
`category_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
UNIQUE KEY `category_id` (`friend_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `friends` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`friend_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `ratings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`rating` tinyint(2) unsigned NOT NULL,
`public` tinyint(1) NOT NULL DEFAULT '0',
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
I am trying to perform the following query on those tables:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`category_id` = `Rating`.`category_id`
AND `JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `Rating`.`category_id`
AND `JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
The query above is not working, as it returns no results (although there is data in the tables that should return), what I'm trying to do is to find all the Ratings that were not authored by me (ID:1), but were authored by my Friends, but only if I've selected to view a specific Category for that Friend, with the resulting set being filtered by a given set of specific Categories.
The INNER JOINs loop around through Rating --> Friend --> CategoriesFreind --> Category --> back to Rating.
If I remove the additional portion of the INNER JOIN's ON clauses as follows:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
then the query will return results, but because the INNER JOIN joining the CategoriesFriend to the Rating is not being filtered by the 'JCategory'.'id' IN (4, 14) clause, it returns all Ratings by that friend instead of filtered as it should be.
Any suggestions on how to modify my query to get it to pull the filtered results?
And I'm using CakePHP, so a query that would fit into it's unique query format would be preferred although not required.
first ,why are you use the JFriend.id, does it mean something,or is it as the same as user_id?
try this one,the same logic but it's from top to bottom ,I feel:
SELECT * FROM categories as JCategory
INNER JOIN categories_friends as JCategoriesFriend ON JCategoriesFriend.category_id = JCategory.id
INNER JOIN friends AS JFriend ON JFriend.friend_id = JCategoriesFriend.friend_id
INNER JOIN ratings AS Rating ON Rating.user_id = JFriend.friend_id
WHERE JCategory.id IN (4,14) AND JFriend.user_id = 1 AND Rating.user_id <> 1 GROUP BY Rating.id
I got one result from all the data that I made for the testing.
if it does not work also,try make some correct data,maybe the data is not right...
the testing data below:
categories: id | name (14| 141414)
categories_friends: category_id| friend_id (14| 2)
friends: id | user_id | friend_id (4| 1| 2)
ratings: id | user_id | category_id | title (2| 2| 14 | 'haha')
So I wondered if the INNER JOINs were being a little too limiting and specific in their ON clauses. So I thought that maybe a LEFT JOIN would work better...
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
LEFT JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`
AND `JCategoriesFriend`.`category_id` = `Rating`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `JRatingsUser`.`id` IS NULL
AND `Rating`.`user_id` <> 1
GROUP BY `Rating`.`id`
That query worked for me.
I did away with linking to the categories table directly, and linked indirectly through the categories_friends table which sped up the query a little bit, and everything is working great.

MySQL - LEFT JOIN value of columnX where MAX(columnY)

So this is my query:
SELECT SQL_CALC_FOUND_ROWS wagons.id,
wagons.mid,
wagons.year,
wagons.make,
wagons.model,
wagons.nickname,
wagons.description,
members.first,
members.last,
wagon_photos.filename,
Count(DISTINCT( likes.id )) AS likes,
Count(DISTINCT( comments.id )) AS comments
FROM wagons
INNER JOIN members
ON members.mid = wagons.mid
LEFT JOIN wagon_photos
ON wagon_photos.wid = wagons.id
LEFT JOIN likes
ON likes.wid = wagons.id
LEFT JOIN comments
ON comments.wid = wagons.id
GROUP BY wagons.id
ORDER BY wagons.id DESC
LIMIT 10
I am trying to fetch the wagon_photos.filename where wagon_photos.default is the maximum value. (It's a boolean, where only one row (for each wagon_photos.wid) will be 1.) On the off-chance the member does not have a default photo selected, I'd like it to return the lowest wagon_photos.id if possible.
I have tried numerous queries with ORDER BY and GROUP BY, but I think it may be a little more complicated than that. If I use a WHERE clause, it left out any wagons that did not have any photos linked to it, which I do not want to do.
CREATE TABLE `wagons` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`mid` int(8) DEFAULT NULL,
`year` varchar(4) DEFAULT NULL,
`make` varchar(50) DEFAULT NULL,
`model` varchar(100) DEFAULT NULL,
`nickname` varchar(200) DEFAULT NULL,
`description` mediumtext,
`featured` int(1) DEFAULT '0',
`visibility` int(1) DEFAULT '1',
`commision` int(1) DEFAULT '1',
`feat1title` varchar(50) DEFAULT NULL,
`feat2title` varchar(50) DEFAULT NULL,
`feat3title` varchar(50) DEFAULT NULL,
`feat4title` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=617 DEFAULT CHARSET=utf8;
CREATE TABLE `wagon_photos` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`wid` int(11) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`caption` varchar(255) DEFAULT '',
`default` int(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;
Let me try and understand what you're asking. I think what you're asking is "how do I construct a query where I want to select all of the items that have the max value of a particular column". If that's the case, you want to break this down into simpler units in order to solve it. First, you'll need the max value itself:
select max(wp.default) as max_val from wagon_photos wp
This gets you the max value.
Next you can create a query to select rows where a condition is true, but leave the condition out for a moment:
select (your rows) from wagons w where (a condition will go here)
And your condition is that you want wagon_photos.default to equal the max value. So you'll need to join in that table:
select (your rows) from wagons w join wagon_photos wp
on wp.wid = w.id where wp.default = (something)
Your "something" happens to be that max_value that we created a query for in the beginning. So putting this together, you'll use a subquery which will look like this:
select (your rows) from wagons w join wagon_photos wp
on wp.wid = w.id where wp.default = (
select max(wpb.default) from wagon_photos wpb
)
I haven't set up the tables myself to run this, so I might have goofed on parentheses, and it doesn't include all your joins. But I think this should point you in the right direction.
Well I feel a little bit silly now.
I finally thought of adding AND wagon_photos.default = 1 after the LEFT JOIN for wagon_photos and it works. I just need to make sure they always have a default setup, so I ran a script that on every update to their photos, it checks for a default, and if one isn't set, set the newest upload as default.
... LEFT JOIN wagon_photos ON wagon_photos.wid = wagons.id AND wagon_photos.default = 1 ...

MySQL query for parent and child table

I need help writing a mysql query. I have two tables, one parent, one child.
Parent table: garments
`garment_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`garment_id`)
Child table: garment_images
`id` int(11) NOT NULL AUTO_INCREMENT,
`garment_id` int(11) NOT NULL,
`filename` varchar(100) NOT NULL,
`order` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
I would like query all garments, and to show the first image for each item from the 'garment_images' table.
This should be fairly close:
SELECT G.*, (
SELECT GI.id, GI.filename from garment_images as GI
WHERE GI.garment_id = G.garment_id
ORDER BY GI.order DESC
LIMIT 1)
FROM garments as G
Split the problem:
Retreive the first image id for every garment
Put the data together
Step 1:
select gi.*
from
garment_images as gi
inner join (
select min(id) as minId
from garment_images
group by garment_id
) as a on gi.garment_id = a.minId
I'll leave to you the second step (Hint: join the data from the query above with your garment table)
If "the first image" is the one with order = 1 (SQL Fiddle)
SELECT * from garments g JOIN garment_images gi
ON g.garment_id = gi.garment_id
WHERE gi.order = 1

Query to get latest training date

We have 2 tables one is driver with all the driver details and the table is dTraining.
Structure for driver is
CREATE TABLE IF NOT EXISTS `driver` (
`driverID` int(5) NOT NULL,
`clientID` int(5) NOT NULL,
`driverName` varchar(100) NOT NULL,
`driverDateOfBirth` date NOT NULL,
`driverStatus` enum('a','d','i') NOT NULL DEFAULT 'a'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Structure for dTraining is
CREATE TABLE IF NOT EXISTS `dTraining` (
`dTrainingID` int(5) NOT NULL,
`cTrainingID` int(5) NOT NULL,
`trainingID` int(11) NOT NULL,
`driverID` int(5) NOT NULL,
`clientID` int(5) NOT NULL,
`driverTrainingDate` date NOT NULL,
`driverTrainingUpdateStatus` enum('d','a') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
What I need is that when I run a query like this should give be the latest training from the many training data which the driver has. So I need help on how to run the subquery there.
SELECT driver.driverID,dTraining.driverTrainingDate
FROM driver,dTraining
Currently I have this query . Sample output will be
driverID=1 , driverTrainingDate=2013-02-01
driverID=2 , driverTrainingDate=2013-02-02
SELECT driver.driverID,b.driverTrainingDate
FROM driver
LEFT JOIN (
SELECT B1.*
FROM dTraining AS B1
LEFT JOIN dTraining AS B2
ON B1.driverID = B2.driverID
AND B1.cTrainingID = B2.cTrainingID
AND B1.driverTrainingDate< B2.driverTrainingDate
WHERE B1.cTrainingID =".$cTID." And B2.driverID IS NULL) as b
ON ( driver.driverID= b.driverID)
What I am curious why do we need to put B1.driverTrainingDate< B2.driverTrainingDate and not B1.driverTrainingDate> B2.driverTrainingDate and the need for B2.driverID IS NULL
The left join with B2 returns trainings that happened after these of B1, that's why if none exist (B2.driverID is null) you're sure B1's training is the last one.
try this, change '123' for the driverID:
select d.*,t.*
from driver as d
inner join dtraining as t
on d.driverId=t.driverId
where d.driverid in (1,2,3,4.......10000)
order by t.driverTrainingDate desc
limit 1