Get data sorted even if some references are NULL - mysql

I want to order my results after a name. Thereby multiple tables are necessary. Now I have the problem that I want to sort the name even if there is a null in the column. Below you find a sample database which should represent the problem:
My tables
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`desc` varchar(255) NOT NULL,
`price` decimal(10,0) NOT NULL,
`manufacturer_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
CREATE TABLE IF NOT EXISTS `manufacturer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`desc` varchar(255) NOT NULL,
`website` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
My data:
INSERT INTO `products` (`id`, `desc`, `price`, `manufacturer_id`) VALUES
(1, 'book', 12, 1),
(2, 'cup', 4, 2),
(3, 'Arbitrary product', 100, NULL);
INSERT INTO `manufacturer` (`id`, `title`, `desc`, `website`) VALUES
(1, 'Publisher', 'Lorem ipsum', 'www.stackoverflow.com'),
(2, 'Cup producer', 'Lorem ipsum', 'www.cup.com');
If I do a SELECT * FROM products than I would get three results. If I want to order it I have a query like
SELECT p.desc, p.price, m.title
FROM products p, manufacturer m
WHERE p.manufacturer_id = m.id
ORDER BY m.title
This gives me only two results because of the null value in products. Is it possible to sort the table products after the manufacturer title even there is a null in it?

You did a join by saying p.manufacturer_id = m.id
But you didn't specify it so by default it's an inner join,
You want to have a left join where your 'left' table is the products table
SELECT p.desc, p.price, m.title
FROM products AS p
LEFT JOIN manufacturer AS m ON m.id = p.manufacturer_id
ORDER BY m.title
Have a look at this image for a better understanding
http://www.codeproject.com/KB/database/Visual_SQL_Joins/Visual_SQL_JOINS_V2.png

Use LEFT JOIN
Try this:
SELECT p.desc, p.price, m.title
FROM products p LEFT OUTER JOIN manufacturer m
ON p.manufacturer_id = m.id
ORDER BY m.title

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 :

Mysql query across three tables?

I'm building a site that allows users to upload posters of television productions they have made. Other users can add themselves to the posters if they were involved with the production and their names get listed below the poster too.
I am having problems writing a mysql query that will allow me to list all the uploaded posters but also any of the users that have listed themselves as being involved with the production. I have made this sql fiddle that might help. The current query displays all the uploaded posters but not those who have added themselves to the poster. Any ideas?
The query
SELECT tbl_uploads.file_name, tbl_users.user_id, tbl_users.user_name, tbl_collab.collab_userid, tbl_collab.collab_username
FROM tbl_uploads
left join tbl_collab on tbl_collab.file_name = tbl_uploads.file_name
left join tbl_users on tbl_uploads.user_id = tbl_users.user_id
group by tbl_uploads.file_name
The tables
CREATE TABLE IF NOT EXISTS `tbl_users` (
`user_id` int(11) NOT NULL,
`user_name` varchar(25) NOT NULL,
`user_email` varchar(60) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `tbl_users` (`user_id`, `user_name`,`user_email`) VALUES
(2, 'julian', 'julian#email.com'),
(3, 'bob', 'bob#email.com'),
(4, 'sue', 'sue#email.com');
CREATE TABLE IF NOT EXISTS `tbl_uploads` (
`id` int(10) NOT NULL,
`file_name` varchar(100) NOT NULL,
`user_id` int(11) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=latin1;
INSERT INTO `tbl_uploads` (`id`, `file_name`, `user_id`) VALUES
('7', 'Julians Picture','2' ),
('13', 'Julians 2nd picture','2' ),
('14', 'Bobs Picture','3' ),
('15', 'Another Picture','3' );
CREATE TABLE IF NOT EXISTS `tbl_collab` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`collab_username` varchar(255) NOT NULL,
`file_name` varchar(255) NOT NULL,
`collab_userid` varchar(255) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;
INSERT INTO `tbl_collab` (`id`,`file_name`,`collab_userid`, `user_id`,`collab_username`) VALUES ('1','Bobs Picture','4','4','Sue' ), ('2','Another Picture','3','3','Bob' )
,('3','Bobs Picture','2','2','Julian' );
This did what I was looking for. GROUP_CONCAT did the trick
SELECT up.file_name, GROUP_CONCAT(c.collab_username)
FROM tbl_uploads up
LEFT JOIN tbl_users p ON up.user_id = p.user_id
LEFT JOIN tbl_collab c ON up.file_name = c.file_name
GROUP BY up.file_name

Avoid duplicate entries when joining multiple tables (MySQL)

(please see the database structure I'm testing with at the bottom of this post.)
I execute this query:
SELECT m.title, GROUP_CONCAT(DISTINCT(d.name) SEPARATOR ',') d FROM movies m
INNER JOIN movies_seen s
ON s.object_id = m.id
LEFT JOIN movies_directors_connections dc
ON dc.movie_id = m.id
LEFT JOIN movies_directors d
ON d.id = dc.director_id
With this result:
title | d
Pulp Fiction | Quentin Tarantino,George Butler,Robert Fiore
But I'm trying to get this:
title | d
Pulp Fiction | Quentin Tarantino
Pumping Iron | George Butler,Robert Fiore
And suggestions? :)
CREATE TABLE `movies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(90) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 ;
CREATE TABLE `movies_seen` (
`object_id` int(10) NOT NULL DEFAULT '0',
`date` varchar(10) NOT NULL DEFAULT '0');
CREATE TABLE `movies_directors` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 ;
CREATE TABLE IF NOT EXISTS `movies_directors_connections` (
`movie_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
`director_id` mediumint(8) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM;
And then some test data:
INSERT INTO `movies` (`id`, `title`) VALUES
(1, 'Pulp Fiction'), (2, 'Pumping Iron');
INSERT INTO `movies_seen` (`object_id`, `date`) VALUES
(1, 1359511222), (2, 1359511223);
INSERT INTO `movies_directors` (`id`, `name`) VALUES
(1, 'Quentin Tarantino'),
(2, 'George Butler'),
(3, 'Robert Fiore');
INSERT INTO `movies_directors_connections` (`movie_id`, `director_id`) VALUES
(1, 1), (2, 2), (2, 3);
you just need to add GROUP BY clause
SELECT m.title,
GROUP_CONCAT(DISTINCT(d.name) SEPARATOR ',') d
FROM movies m
INNER JOIN movies_seen s
ON s.object_id = m.id
LEFT JOIN movies_directors_connections dc
ON dc.movie_id = m.id
LEFT JOIN movies_directors d
ON d.id = dc.director_id
GROUP BY m.title
SQLFiddle Demo
OTHER LINK
MySQL GROUP BY clause

Join two tables, matching a column with multiple values

I am trying to get a product matching some custom parameters.
So I have to three tables - products, parameters and parametersitems.
Products table:
CREATE TABLE `products` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT
`Title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`Content` longtext COLLATE utf8_unicode_ci NOT NULL,
`Price` float(10,2) unsigned NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Parameter table:
CREATE TABLE `parameters` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Label` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Parameter items table:
CREATE TABLE `parametersitems` (
`ProductID` int(10) unsigned NOT NULL DEFAULT '0',
`ParameterID` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`ProductID`,`ParameterID`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
So my question is how can I get only the products matching all the parameters.
The only way I could think of is joining the parameteritems table couple of times.
For example, here is a query to get the products matching two parameters:
SELECT
products.*
FROM
products
INNER JOIN
parametersitems AS paritems1
ON
paritems1.ItemID = products.ID
AND paritems1.ParameterID = 7
INNER JOIN
parametersitems AS paritems2
ON
paritems2.ItemID = products.ID
AND paritems2.ParameterID = 11
My only concern is that the SELECT query will get slower and slower if there more parameters selected.
So is there a better way to handle this problem?
Thank you
Adjust the value tested in the HAVING clause to match the number of values listed in the IN clause.
SELECT p.*
FROM products p
WHERE p.ID IN (SELECT pi.ItemID
FROM parameteritems pi
WHERE pi.ItemID = p.ID
AND pi.ParameterID IN (7,11)
GROUP BY pi.ItemID
HAVING COUNT(DISTINCT pi.ParameterID) = 2)
select p.*
from products p
inner join (
select ItemID
from parametersitems
where ParameterID in (7, 11)
group by ItemID
having count(distinct ParameterID) = 2
) pm on p.ID = pm.ItemID
SELECT
p.ID, p.Title, p.Content, p.Price
FROM
products AS p
INNER JOIN
parametersitems AS pi ON pi.ProductID = p.ID
GROUP BY
p.ID, p.Title, p.Content, p.Price
HAVING COUNT(DISTINCT pi.ParameterID) = (SELECT COUNT(ID) FROM parameters);
This will always get you products matching every parameter no matter how many parameters you add. (This could become bogus if you delete a parameter without deleting the corresponding rows in paramatersitems. This is what constraints are for.)

Is it possible to achieve this selection with a single query?

This one has been haunting me for quite a while now..
I have been developing my own CMS using a MySQL database; each uploaded image is assigned to a category, according to which part of the site it is related to (I need to do this since each category has its own way to handle images).
I have several tables for the various entities, an 'images' table, and an associative table: 'images_assoc', their basic structure is as follows:
CREATE TABLE `images` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL default '',
`link` varchar(255) NOT NULL default '',
`idcategory` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `idcategory` (`idcategory`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
INSERT INTO `images` (`id`, `name`, `link`, `idcategory`) VALUES (1, 'some name', 'foo.jpg', 1);
CREATE TABLE `images_assoc` (
`id` int(11) NOT NULL auto_increment,
`idimage` int(11) NOT NULL default '0',
`idelement` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `idimage` (`idimage`,`idelement`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
INSERT INTO `images_assoc` (`id`, `idimage`, `idelement`) VALUES (1, 1, 2);
CREATE TABLE v`some_entity` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(250) NOT NULL,
`description` text NOT NULL,
-- some other data
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
What I need to do, in the various pages of the site, is to retrieve a list of the page elements together with their related image(s). I have not yet been able to do it with one single select. What I am doing right now is to run a select for the page elements and then run a query for each single element to retrieve any associated image, with a query like this:
SELECT i.id, i.link, i.name
FROM images_assoc AS ia, images AS i
WHERE ia.idelement = '1'
AND i.idcategory = '1'
AND i.id = ia.idimage
one solution I initially came up with was:
SELECT t. * , i.id, i.link, i.name
FROM (
(
(
contents AS t
)
LEFT JOIN images_assoc AS ia ON t.id = ia.idelement
)
LEFT JOIN images AS i ON i.id = ia.idimage
)
WHERE i.idcategory = '1'
AND i.id = ia.idimage
but it left out any element with no associated image, which is the exact contrary of the purpose of the left join.
Later, I tried changing the query to this:
SELECT t. * , i.id, i.link, i.name
FROM (
(
(
contents AS t
)
LEFT JOIN images_assoc AS ia ON t.id = ia.idelement
)
LEFT JOIN images AS i ON ( i.id = ia.idimage
AND i.idcategoriy = '1' )
)
But still, the query is faulty: I end up with a cross-join-like result, since the category restriction is applied later..
Does anyone have any suggestions?
Any tips regarding the database structure are welcome as well..
well your idcategory condition can never match unless there is a corresponding counterpart in the other table, thats why your results with no corresponding image "disappear", left join is behaving correctly.
try this:
sql query:
SELECT some_entity.title, some_entity.description, some_entity.id as entityid, images.id as imageid, images.link, images.name
FROM
some_entity
LEFT JOIN images_assoc ON (some_entity.id = images_assoc.idelement)
LEFT JOIN (SELECT * FROM images WHERE idcategory=1) images ON (images.id = images_assoc.idimage)
or probably better (the obe only to illustrate why it didnt work):
SELECT some_entity.title, some_entity.description, some_entity.id AS entityid, images.id AS imageid, images.link, images.name
FROM some_entity
LEFT JOIN images_assoc ON ( some_entity.id = images_assoc.idelement )
LEFT JOIN images ON ( images.id = images_assoc.idimage )
WHERE images.idcategory = '1' OR images.idcategory IS NULL
test data:
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
CREATE TABLE IF NOT EXISTS `images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`link` varchar(255) NOT NULL DEFAULT '',
`idcategory` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idcategory` (`idcategory`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=124 ;
CREATE TABLE IF NOT EXISTS `some_entity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(250) NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=322 ;
CREATE TABLE IF NOT EXISTS `images_assoc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`idimage` int(11) NOT NULL DEFAULT '0',
`idelement` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idimage` (`idimage`,`idelement`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
INSERT INTO `images` (`id`, `name`, `link`, `idcategory`) VALUES
(123, 'some name', 'foo.jpg', 1);
INSERT INTO `images_assoc` (`id`, `idimage`, `idelement`) VALUES
(1, 123, 321);
INSERT INTO `some_entity` (`id`, `title`, `description`) VALUES
(321, 'test', 'test');