I have 4 tables as below.
1. emp
- emp_id
- name
- contact_id
2. contact
- contact_id
- name
3. emp_projects
- prj_id
- emp_id
4. project
- prj_id
- name
Relationships :-
emp to projects - One to Many
I need a query joining all these tables.
Conditions
contact_id column in emp table can be empty so get contact details only if the column is not empty
Employee can be part of Zero or many projects.
Query needs to return the project name as well.
Query that I currently have,
select e.,ep. from emp e left outer join emp_projects ep on e.emp_id = ep.emp_id where e.emp_id=1
Need to join contacts and projects table as well here.
Sample data,
1. emp
| 1 | Mike | 1 |
| 2 | John | - |
2. contact
| 1 | Alex |
3. emp_projects
| 1 | 1 |
| 2 | 1 |
4. projects
| 1 | Test |
| 2 | Mail |
Expected output :-
For emp id 1,
1(emp_id), Mike(emp_name), 1(contact_id), Alex(contact_name), 1(proj_id), Test(Prj_Name)
1(emp_id), Mike(emp_name), 1(contact_id), Alex(contact_name), 2(proj_id), Email(Prj_Name)
For emp id 2, ( Contact column is empty for emp 2 and emp 2 is not associated with any project )
1(emp_id), John(emp_name), -(contact_id), -(contact_name), -(proj_id), -(Prj_Name)
Query and DDL script is added below. 1 recommendation, use the primary key in the mapping table or have a composite key ( emp_id, project_id )
select
employee.id as employee_id,
employee.name as employee_name,
contact.id as contact_id,
contact.name as contact_name,
project.id as project_id,
project.name as project_name
FROM employee employee
LEFT OUTER JOIN contact contact on contact.id = employee.contact_id
LEFT OUTER JOIN employee_projects employee_project on employee_project.employee_id = employee.id
LEFT OUTER JOIN project project on project.id = employee_project.project_id
CREATE TABLE IF NOT EXISTS `employee`
( `id` int(6) unsigned NOT NULL
, `contact_id` int(6) unsigned NULL
, `name` varchar(200) NOT NULL
, PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `employee` (`id`, `contact_id`, `name`) VALUES
('1', '1', 'Mike'),
('2', null, 'John');
CREATE TABLE IF NOT EXISTS `contact`
( `id` int(6) unsigned NOT NULL
, `name` varchar(200) NOT NULL
, PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `contact`
(`id`, `name`) VALUES
('1', 'Alex');
CREATE TABLE IF NOT EXISTS `project`
( `id` int(6) unsigned NOT NULL
, `name` varchar(200) NOT NULL
, PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `project` (`id`, `name`) VALUES
('1','Test'),
('2', 'Mail');
CREATE TABLE IF NOT EXISTS `employee_projects`
( `id` int(6) unsigned NOT NULL
, `employee_id` int(6) unsigned NOT NULL
, `project_id` int(6) unsigned NOT NULL
, PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `employee_projects`
(`id`, `project_id`, `employee_id`) VALUES
('1', '1', '1'),
('2', '2', '1');
I think the below query will to the trick:
select e.,ep.,c.,p.
from emp e
left outer join emp_projects ep on e.emp_id = ep.emp_id
left outer join contact c on c.emp_id = e.emp_id
left outer join project p on p.proj_id = ep.proj_id
where e.emp_id=1
Related
We have an issue using a counting combination with inner/left join that we cannot figure out how to solve.
We would appreciate any help on the matter!
We have 4 tables in the example:
1: providers: Including 2 providers
2: providers_categories: Including 2 categories. 1 provider can be in multiple categories (this seems to be causing the issue)
3: connections_providers: connecting the providers to the categories
4: reviews_providers: currently we have included 1 rating per provider
Goal: to output the review count from the table reviews_providers.
Issue: Provider 2 is included in 2 categories. The review count is doubled: 1 count for each provider category: A total of 2 reviews are printed even though only 1 entry exists.
Thank you!
Code:
SELECT prov.id, prov.title, prov_cat.title AS category, AVG(reviews.rating) AS rating, COUNT(reviews.rating) AS count
FROM connections_providers_categories conn
INNER JOIN providers_categories prov_cat
ON prov_cat.id = conn.category_id
LEFT JOIN reviews_providers reviews
ON reviews.provider_id = conn.provider_id
INNER JOIN providers prov
ON prov.id = conn.provider_id
GROUP BY prov.id
ORDER BY prov.title ASC
CREATE TABLE `connections_providers_categories` (
`provider_id` int(4) UNSIGNED NOT NULL,
`category_id` int(4) UNSIGNED NOT NULL
) ENGINE=MyISAM DEFAULT;
INSERT INTO `connections_providers_categories` (`provider_id`, `category_id`) VALUES
(1, 1),
(2, 1),
(2, 2);
CREATE TABLE `providers` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT;
INSERT INTO `providers` (`id`, `title`) VALUES
(1, 'Provider 1'),
(2, 'Provider 2');
CREATE TABLE `providers_categories` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` varchar(60) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT;
INSERT INTO `providers_categories` (`id`, `title`) VALUES
(1, 'Category 1'),
(2, 'Category 2');
CREATE TABLE `reviews_providers` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`provider_id` int(4) UNSIGNED NOT NULL,
`rating` enum('1','2','3','4','5') DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT;
INSERT INTO `reviews_providers` (`id`, `provider_id`, `rating`) VALUES
(1, 2, '5'),
(2, 1, '3');
Our question might resemble the following question, but we do not find the answer / see that it is the same case even thought both questions include multiple counts: count is multiplied after adding left join
It seems we might need a subquery, but we are not sure how to do this.
Any suggestions?
Thanks!
you can use subquery top get your result
SELECT prov.id, prov.title, GROUP_CONCAT(prov_cat.title) AS category, reviews.rating , reviews.count
FROM connections_providers_categories conn
INNER JOIN providers_categories prov_cat
ON prov_cat.id = conn.category_id
LEFT JOIN (SELECT provider_id, AVG(rating) AS rating, COUNT(provider_id) AS count FROM reviews_providers GROUP BY provider_id) reviews
ON reviews.provider_id = conn.provider_id
INNER JOIN providers prov
ON prov.id = conn.provider_id
GROUP BY prov.id,prov.title
ORDER BY prov.title ASC
id | title | category | rating | count
-: | :--------- | :-------------------- | -----: | ----:
1 | Provider 1 | Category 1 | 3 | 1
2 | Provider 2 | Category 2,Category 1 | 5 | 1
db<>fiddle here
I use MySQL.
I have three tables:
Book, Publisher, Description.
In those tables I have 2 books, 3 publishers and 1 description from publisher #1
Description contains three fields book_id, publisher_id, description
I would like to select all records where description has been provided by the publisher or NULLs otherwise (if description was not provided by the publisher);
In other words I need the following output:
book_id
publisher_id
description
1
1
blah
1
2
NULL
1
3
NULL
2
1
NULL
2
2
NULL
2
3
NULL
Below is SQL I’m using:
CREATE TABLE `book` (
`id` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `publisher` (
`id` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `description` (
`book_id` int(11) NOT NULL,
`publisher_id` int(11) NOT NULL,
`description` varchar(256) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `book` (`id`) VALUES (1), (2);
INSERT INTO `publisher` (`id`) VALUES (1), (2), (3);
INSERT INTO `description` (`book_id`, `publisher_id`, `description`) VALUES ('1', '1', 'blah');
The SQL I'm using looks like this but it doesn't return the results I need:
SELECT b.id book_id, p.id publisher_id, d.description FROM description d
RIGHT JOIN book b ON d.book_id=b.id
RIGHT JOIN publisher p ON publisher_id=p.id
Your help will be greatly appriciated
It sounds like you want a cross join between books and publishers and then to left join the description:
SELECT b.id book_id, p.id as publisher_id, d.description
FROM book b CROSS JOIN
publisher p LEFT JOIN
description d
ON d.book_id = b.id AND d.publisher_id = p.id
ORDER BY b.id, p.id;
Here is a db<>fiddle.
I'm having trouble implementing a JOIN query on these two tables.
Product Table contains products.
ProductInstance table contains version of product with pricing that were changed over time.
There should exactly be one row for each storeId even if it's null but should contain highest productInstanceId.
My Query can get rows with largest Ids for each product, but ignores StoreIds.
Select P.id as productId, PI.id as productInstanceId, P.name, PI.storeId, PI.price from products as P
LEFT JOIN productInstances AS PI
ON PI.productId = P.id
LEFT JOIN productInstances AS PI2
ON (PI2.productId = P.id and (PI.id < PI2.id ) )
WHERE PI2.id IS NULL
AND PI.id IS NOT NULL
Create Tables:
CREATE SCHEMA IF NOT EXISTS `TestDB` DEFAULT CHARACTER SET utf8 ;
USE `TestDB` ;
-- -----------------------------------------------------
-- Table `TestDB`.`products`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `TestDB`.`products` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
`description` VARCHAR(45) NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `TestDB`.`productInstances`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `TestDB`.`productInstances` (
`id` INT NOT NULL AUTO_INCREMENT,
`storeId` INT NULL,
`productId` INT NOT NULL,
`price` INT NOT NULL,
PRIMARY KEY (`id`, `productId`),
INDEX `fk_productInstances_products_idx` (`productId` ASC),
CONSTRAINT `fk_productInstances_products`
FOREIGN KEY (`productId`)
REFERENCES `TestDB`.`products` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
INSERT TEST DATA USING THIS QUERY
INSERT INTO `TestDB`.`products` (`name`) VALUES ('Product 1');
INSERT INTO `TestDB`.`products` (`name`) VALUES ('Product 2');
INSERT INTO `TestDB`.`productInstances` (`storeId`, `productId`, `price`) VALUES ('1', '1', '111');
INSERT INTO `TestDB`.`productInstances` (`productId`, `price`) VALUES ('1', '122');
INSERT INTO `TestDB`.`productInstances` (`productId`, `price`) VALUES ('1', '133');
INSERT INTO `TestDB`.`productInstances` (`storeId`, `productId`, `price`) VALUES ('1', '1', '115');
INSERT INTO `TestDB`.`productInstances` (`storeId`, `productId`, `price`) VALUES ('2', '1', '155');
For MySql 8.0+ you can use ROW_NUMBER() window function to get the max id for each productid and storeid:
select p.id productId,
pi.id productInstanceId,
p.name,
pi.storeId,
pi.price
from products p
left join (
select *, row_number() over (partition by storeId, productId order by id desc) rn
from productInstances
) pi on pi.productId = p.id and pi.rn = 1
For previous versions you can do the same with a correlated subquery:
select p.id as productId,
pi.id as productInstanceId,
p.name,
pi.storeId,
pi.price
from products p
left join (
select pi.* from productInstances pi
where pi.id = (
select max(id)
from productInstances
where productId = pi.productId and storeId <=> pi.storeId
)
) pi on pi.productId = p.id
See the demo.
Results:
> productId | productInstanceId | name | storeId | price
> --------: | ----------------: | :-------- | ------: | ----:
> 1 | 3 | Product 1 | null | 133
> 1 | 4 | Product 1 | 1 | 115
> 1 | 5 | Product 1 | 2 | 155
> 2 | null | Product 2 | null | null
I need some help building a SQL to fetch something like a "FULL OUTER JOIN" over four tables. I have this structure and cannot really modify much on it, cause its a already in use database:
-- ----------------------------
-- Table structure for article
-- ----------------------------
CREATE TABLE `article` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `article` VALUES (1, 'Coffeemaker');
INSERT INTO `article` VALUES (2, 'Toaster');
-- ----------------------------
-- Table structure for language
-- ----------------------------
CREATE TABLE `language` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `language` VALUES (1, 'German');
INSERT INTO `language` VALUES (2, 'English');
-- ----------------------------
-- Table structure for property
-- ----------------------------
CREATE TABLE `property` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `property` VALUES (1, 'DescriptionText');
INSERT INTO `property` VALUES (2, 'EAN-Code');
-- ----------------------------
-- Table structure for data
-- ----------------------------
CREATE TABLE `data` (
`ArticleID` int(10) UNSIGNED NOT NULL,
`PropertyID` int(10) UNSIGNED NOT NULL,
`LanguageID` int(10) UNSIGNED NOT NULL,
`Value` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ArticleID`, `PropertyID`, `LanguageID`) USING BTREE,
CONSTRAINT `FK_ArticleID` FOREIGN KEY (`ArticleID`) REFERENCES `article` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_LanguageID` FOREIGN KEY (`LanguageID`) REFERENCES `language` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_PropertyID` FOREIGN KEY (`PropertyID`) REFERENCES `property` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB;
INSERT INTO `data` VALUES (1, 1, 1, 'Eine Kaffemaschine');
INSERT INTO `data` VALUES (2, 1, 2, 'A toaster');
SQL: http://sqlfiddle.com/#!9/91dc8/1
What i want to get is a new VIEW which contains a join over all entity-tables showing a row for all articles, all properties and all languages but using the already existing data if available or null if not.
Is it possible? How would the SQL look like?
You seem to be after this...
SELECT a.id articleid
, p.id propertyid
, l.id languageid
, d.value
FROM article a
CROSS -- optional keyword
JOIN property p
CROSS -- optional keyword
JOIN language l
LEFT -- not optional
JOIN data d
ON d.articleid = a.id
AND d.propertyid = p.id
AND d.languageid = l.id;
+-----------+------------+------------+--------------------+
| articleid | propertyid | languageid | value |
+-----------+------------+------------+--------------------+
| 1 | 1 | 1 | Eine Kaffemaschine |
| 2 | 1 | 1 | NULL |
| 1 | 2 | 1 | NULL |
| 2 | 2 | 1 | NULL |
| 1 | 1 | 2 | NULL |
| 2 | 1 | 2 | A toaster |
| 1 | 2 | 2 | NULL |
| 2 | 2 | 2 | NULL |
+-----------+------------+------------+--------------------+
If you want all articles, you don't want a "full join". You want a left join that starts with the articles table. Further, you don't even need that, because all your articles have values in data.
But the question does specify all articles, so:
SELECT a.*, p.*, l.*, d.*
FROM article a LEFT JOIN
data d
ON d.ArticleID = a.ID LEFT JOIN
property p
ON d.PropertyID = p.id LEFT JOIN
language l
ON d.LanguageID = l.id;
I'm not sure what you mean by all articles and all languages (I missed the second part when I first read the question). If you want all combinations then:
SELECT a.*, p.*, l.*, d.*
FROM article a CROSS JOIN
languages l LEFT JOIN
data d
ON d.ArticleID = a.ID AND
d.LanguageID = l.ID LEFT JOIN
property p
ON d.PropertyID = p.id ;
I'm using MySQL 5.5. with two tables in it:
DROP TABLE IF EXISTS `events_dictionary`;
CREATE TABLE `events_dictionary` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `events_dictionary` VALUES (1, 'Light'),(2, 'Switch'),(3, 'on'),(4, 'off');
DROP TABLE IF EXISTS `events_log`;
CREATE TABLE `events_log` (
`log_id` bigint(20) NOT NULL AUTO_INCREMENT,
`event_name_id` int(11) NOT NULL DEFAULT '0',
`event_param1` int(11) DEFAULT NULL,
`event_value1` int(11) DEFAULT NULL,
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `events_log` VALUES (1, 1, 2, 3),(2, 1, 2, 4);
Table events_dictionary contains names for events_log events names,params and values.
So, my question is - how could i select data from event_log table with columns event_name_id, event_param1, event_value1 mapped to name values from events_dictionary table?
I tried to do this query:
SELECT name, event_param1, event_value1
FROM events_log
JOIN events_dictionary ON events_log.event_name_id = events_dictionary.id;
But, in this case i see only event_name_id replaced with values from events_dictionary, like this:
name | event_param1 | event_value1
Light | 1 | 1
Light | 1 | 2
And i want to replace event_param1, and event_value1 with names from events_dictionary too.
Thanks in advance!
You need to join to the events_dictionary multiple times
SELECT a.name, b.name, c.name
FROM events_log
JOIN events_dictionary a ON events_log.event_name_id = a.id
JOIN events_dictionary b ON events_log.event_param1 = b.id
JOIN events_dictionary c ON events_log.event_value1 = c.id;
PS
Your example for the event_log isn't that helpful , instead insert the values (1,1,2,3),(2,1,2,4) to turn the switch on and off for the light.
DS
You can use correlated subqueries:
SELECT name,
(SELECT t.name
FROM events_dictionary AS t
WHERE t.id = event_param1) AS param_name,
(SELECT t2.name
FROM events_dictionary AS t2
WHERE t2.id = event_value1) AS event_name
FROM events_log AS el
JOIN events_dictionary AS ed ON el.event_name_id = ed.id;
Demo here