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

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');

Related

how to count same rating from field in sql

I have a problem counting ratings in SQL. This is what my data looks like:
data
CREATE TABLE `restaurant` (
`id_restaurant` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id_restaurant`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
insert into `restaurant`(`id_restaurant`,`name`) values (1,'Mc Donald');
insert into `restaurant`(`id_restaurant`,`name`) values (2,'KFC');
CREATE TABLE `user` (
`id_user` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
insert into `user`(`id_user`,`userName`) values (1,'Audey');
CREATE TABLE `factors` (
`factor_id` int(11) NOT NULL AUTO_INCREMENT,
`factor_clean` int(11) NOT NULL DEFAULT '0',
`factor_delicious` int(11) NOT NULL DEFAULT '0',
`id_restaurant` int(11) DEFAULT NULL,
`id_user` int(11) DEFAULT NULL,
PRIMARY KEY (`factor_id`),
KEY `id_restaurant` (`id_restaurant`),
KEY `id_user` (`id_user`),
CONSTRAINT `factors_ibfk_1` FOREIGN KEY (`id_restaurant`) REFERENCES `restaurant` (`id_restaurant`),
CONSTRAINT `factors_ibfk_2` FOREIGN KEY (`id_user`) REFERENCES `user` (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
insert into `factors`(`factor_id`,`factor_clean`,`factor_delicious`,`id_restaurant`,`id_user`) values (1,1,5,1,1);
insert into `factors`(`factor_id`,`factor_clean`,`factor_delicious`,`id_restaurant`,`id_user`) values (2,0,5,1,1);
insert into `factors`(`factor_id`,`factor_clean`,`factor_delicious`,`id_restaurant`,`id_user`) values (3,1,5,1,1);
insert into `factors`(`factor_id`,`factor_clean`,`factor_delicious`,`id_restaurant`,`id_user`) values (4,3,3,1,1);
And the result should be like this, Show all ratings (1,2,3,4,5) and their count from the fields rating_clean, rating_delicious, and rating_clean
Thanks for your help.
but the result i get
SELECT COUNT(`factor_clean`+`factor_delicious`),'1' AS rating_1 FROM `factors` WHERE 1 GROUP BY `id_restaurant`
result not should like this
the result should not like that,
my question is, how to select just factor_clean and factor_delicious where factor_clean =1 and factor_delicious = 1
Use union all to unpivot the data and then aggregate:
select id_restaurant, rating, count(*)
from ((select r.id_restaurant, r.rating_clean as rating, r.date
from ratings r
) union all
(select r.id_restaurant, r.rating_delicious, r.date
from ratings r
) union all
(select r.id_restaurant, r.rating_clean2, r.date
from ratings r
)
) r
group by id_restaurant, rating
order by id_restaurant, rating;
For example this is solution for table with colums rating_delicious and rating_clean (only one!):
First of all you should create additional table, I called it factors:
CREATE TABLE `factors` (
`factor_id` int(11) NOT NULL AUTO_INCREMENT,
`factor_clean` int(11) NOT NULL DEFAULT '0',
`factor_delicious` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`factor_id`)
)
Next add two records:
INSERT INTO `factors` (`factor_id`, `factor_clean`, `factor_delicious`) VALUES (NULL, '1', '0'), (NULL, '0', '1');
Now you can join this tables and get results:
SELECT x.id_restaurant
, (x.rating_clean * f.factor_clean) + (x.rating_delicious * f.factor_delicious) AS rating
, count(*)
FROM your_table x
JOIN factors f
WHERE 1
GROUP
BY x.id_restaurant
, rating
In order to use next colum (rating_third), you should and column factor_third to factors, insert new row with 1 in this column and finally add something like your_table.rating_third*factors.factor_third to sum in SELECT

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

Specific query to get scores as a set of qualification rates using averages

Please note that there's a SELECT I've made below the code
I've created a SQL fiddle so you don't have to recreate this example in your local db.
http://sqlfiddle.com/#!9/4c657
The problem basically instead of getting rates as numbers I should get them as percentages, such as:
bad : 25%, good 10%,veryGood: 30%: excellent:45%
Currently I'm just showing numbers, I'm not sure if I should use a SELECT FROM (SELECT ...) With a GROUP BY or if there's a better way to do it. As I'm in doubt about it I'd like to have some suggestions. Thanks!
Thanks
/*What I've got so far*/
SELECT qg.name AS questionGroup,q.name AS question,(rate=1) AS bad ,(rate=2) AS regular,(rate=3) AS good, (rate=4) AS veryGood, (rate=5) AS excellent from answers AS a INNER JOIN questions AS q ON q.id=a.questionId INNER JOIN questionGroups as qg ON qg.id = q.groupId;
CREATE TABLE `guidedVisits` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `questionGroups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `questions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`groupId` int(10) unsigned NOT NULL,
`name` varchar(120) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_groupId` (`groupId`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
CREATE TABLE `answers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`visitId` int(10) unsigned NOT NULL,
`questionId` int(10) unsigned NOT NULL,
`rate` tinyint(4) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_questionId` (`questionId`),
KEY `idx_visitId` (`visitId`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
INSERT INTO `questionGroups` VALUES (1,'About us'),(2,'Facility');
INSERT INTO `questions` VALUES (1,1,'Did you like us?'),(2,1,'What do you think about our ads?'),(3,2,'Were our reception chairs comfortable?'),(4,2,'Was everything clean as you would expect?');
INSERT INTO `guidedVisits` VALUES (1,'2015-03-31'),(2,'2015-03-31');
INSERT INTO `answers` VALUES (1,1,1,3),(2,1,2,4),(3,1,3,4),(4,1,4,5),(5,1,1,5),(6,1,2,5),(7,1,3,5),(8,1,4,5);
SELECT qg.name AS questionGroup,q.name AS question, CASE WHEN AVG(rate) < 25 THEN 'bad' WHEN AVG(rate) < 35 THEN 'good' .. END AS rateDesc
FROM answers AS a INNER JOIN questions AS q
ON q.id=a.questionId INNER JOIN questionGroups as qg
ON qg.id = q.groupId
GROUP BY qg.name, q.name
SELECT r.questionGroup,r.question,r.qid,
FLOOR(AVG(r.bad)*100) AS bad, FLOOR(AVG(r.regular)*100) AS regular,
FLOOR(AVG(r.good)*100) AS good, FLOOR(AVG(r.veryGood)*100) AS veryGood,
FLOOR(AVG(r.excellent)*100) AS excellent
FROM (
SELECT q.id AS qid,qg.id AS gid,
qg.name AS questionGroup,q.name AS question,
(rate=1) AS bad ,(rate=2) AS regular,
(rate=3) AS good, (rate=4) AS veryGood,
(rate=5) AS excellent FROM answers AS a
INNER JOIN questions AS q ON q.id=a.questionId
INNER JOIN questionGroups as qg ON qg.id = q.groupId
WHERE visitId=1) AS r GROUP BY r.qid;

Internationalization of sql query

I have the following SQL tables:
-- Create syntax for TABLE 'companies'
CREATE TABLE `companies` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`country` varchar(255) NOT NULL DEFAULT '',
`city` varchar(255) NOT NULL DEFAULT '',
`address` text NOT NULL,
`logo` varchar(255) NOT NULL DEFAULT 'empty',
`size` int(11) NOT NULL DEFAULT '32',
PRIMARY KEY (`id`),
KEY `city` (`city`),
KEY `country` (`country`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
-- Create syntax for TABLE 'i18n'
CREATE TABLE `i18n` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`lang` varchar(2) DEFAULT NULL,
`word` text,
`english` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'tags'
CREATE TABLE `tags` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
-- Create syntax for TABLE 'tagsForCompany'
CREATE TABLE `tagsForCompany` (
`company` int(11) DEFAULT NULL,
`tid` int(11) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I have the following sql query (thanks to #Rockse):
SELECT c.*
FROM
(SELECT *
FROM companies
WHERE city = "<Some City>" AND country = "<Some Country>") AS c
INNER JOIN tagsForCompany AS tc ON c.id = tc.Company
INNER JOIN tags AS t ON t.id = tc.TID
WHERE t.Name REGEXP '<a keyword>'
I need to be able to search for Companies with a keyword written in another language by searching in the i18n table for a translation. So that searching for restaurant will give the same results as searching for レストラン (restaurant in Japanese) or ristorante (restaurant in italian).
Honestly I have no idea what to edit in the query, my SQL knowledge is a bit limited.
The solution was easier than I thought:
If someone would improve, I would be more than happy
SELECT c.*
FROM i18n, (SELECT *
FROM companies
WHERE city = "<some city>" AND country = "<some country>") AS c
INNER JOIN tagsForCompany AS tc ON c.id = tc.Company
INNER JOIN tags AS t ON t.id = tc.TID
WHERE t.Name = i18n.`english` AND i18n.word REGEXP "<some word>" OR t.Name REGEXP "<the same word as before>"
But for doing that I had to change all the tables to utf-8

why category name is null when select join with enry table

i have two tables ...first is entry
CREATE TABLE IF NOT EXISTS `entry` (
`entry_id` int(11) NOT NULL AUTO_INCREMENT,
`entry_cat_id` int(11) NOT NULL,
`entry_name` varchar(255) NOT NULL,
`entry_body` text NOT NULL,
`img_url` varchar(255) NOT NULL,
`image_link` varchar(255) NOT NULL,
`entry_state` tinyint(1) DEFAULT '0',
`comment_count` int(11) NOT NULL DEFAULT '0',
`entry_count` int(11) NOT NULL DEFAULT '0',
`entry_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`entry_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=41 ;
--
-- Dumping data for table `entry`
--
INSERT INTO `entry` (`entry_id`, `entry_cat_id`, `entry_name`, `entry_body`, `img_url`, `image_link`, `entry_state`, `comment_count`, `entry_count`, `entry_created`) VALUES
(27, 15, 'title fot entry', 'content', '', '', 0, 0, 0, '2013-04-14 14:47:56');
and the escond is entry_category
CREATE TABLE IF NOT EXISTS `entry_category` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`category_name` varchar(150) NOT NULL,
`slug` varchar(150) NOT NULL,
`cat_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=16 ;
--
-- Dumping data for table `entry_category`
--
INSERT INTO `entry_category` (`category_id`, `category_name`, `slug`, `cat_created`) VALUES
(10, 'rrrrrrrr', 'rrrrrrr', '0000-00-00 00:00:00'),
(15, 'gggggg', 'ttttttttt', '2012-12-10 13:47:28');
when select with inner join to get the category and entry name i see category_name is null
SELECT entry.entry_id, entry_category.category_name, entry.entry_name
FROM entry
INNER JOIN entry_category
ON entry.entry_id=entry_category.category_id;
why and what i shall do to see the the category name
join is having wrong clause
you need to join on category_id but in entry table category_id is in entry_cat_id field so you can join like this
SELECT entry.entry_id, entry_category.category_name, entry.entry_name
FROM entry
INNER JOIN entry_category
ON entry.entry_cat_id=entry_category.category_id;
I think you are joining on the wrong field. You are joining on the entry.entry_id field when I think you want to be joining on the entry.entry_cat_id field.
Try changing your query to:
SELECT entry.entry_id, entry_category.category_name, entry.entry_name
FROM entry
INNER JOIN entry_category
ON entry.entry_cat_id=entry_category.category_id;
BTW -- your existing query shouldn't return any results from your sample data. To see NULL records from your entry_category table, you'd need to use an OUTER JOIN.