Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have the following tables:
CREATE TABLE members (
member_id int(1) unsigned NOT NULL AUTO_INCREMENT,
logged_in tinyint(1) NOT NULL,
PRIMARY KEY (member_id),
UNIQUE KEY member_id (member_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO members (member_id, logged_in) VALUES
(1, 0),
(2, 1),
(3, 1),
(4, 1),
(5, 0);
+-----------------------+
| members |
+-----------+-----------+
| member_id | logged_in |
+-----------+-----------+
| 1 | 0 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 0 |
+-----------+-----------+
CREATE TABLE team_members (
team_id int(1) unsigned NOT NULL,
member_id int(1) unsigned NOT NULL,
PRIMARY KEY (team_id,member_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO team_members (team_id, member_id) VALUES
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(3, 5);
+---------------------+
| team_members |
+---------+-----------+
| team_id | member_id |
+---------+-----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 5 |
+---------+-----------+
And this is the output I need to achieve:
+---------+-----------------------+
| team_id | logged_in_users_count |
+---------+-----------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 0 |
+---------+-----------------------+
I am trying the following sql query but it is not returning the expected output:
SELECT
tm.team_id,
(SELECT COUNT(m.logged_in) FROM members m WHERE m.logged_in = 1) AS logged_in_users_count
FROM
members m
INNER JOIN team_members tm ON (m.member_id = tm.member_id)
GROUP BY
tm.team_id
SELECT
team_members.team_id,
COUNT(members.logged_in)
FROM
team_members LEFT JOIN members
ON (team_members.member_id = members.member_id AND members.logged_in = 1)
GROUP BY
team_members.team_id;
Related
I have a table people, a table departments and a table addresses.
I am wondering if there is a way in mySql to have some of the results as JSON rather than added rows or tables.
For this example:
people:
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(50) | YES | | NULL | |
| departmentId | int | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+
addresses
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| personId | int | YES | | NULL | |
| fullAddress | varchar(125) | YES | | NULL | |
+-------------+--------------+------+-----+---------+-------+
departments;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(50) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
Note: to create tables, run:
create table people ( id INT(11), name varchar(50), departmentId INT(11));
create table departments ( id INT(11), name varchar(50));
create table addresses ( id INT(11), personId INT(11), fullAddress varchar(125));
So a person can be linked to one specific department, but can have several addresses.
insert into departments values (0, 'sales');
insert into departments values (1, 'dev');
insert into people values (0, 'Chiara', 0);
insert into people values (1, 'Geoff', 0);
insert into people values (2, 'Tony', 1);
insert into people values (3, 'Raphael', 1);
insert into addresses values (0, 0, 'Street 0');
insert into addresses values (1, 0, 'Street 1');
insert into addresses values (2, 1, 'Street 2');
insert into addresses values (3, 1, 'Street 3');
insert into addresses values (4, 2, 'Street 4');
insert into addresses values (5, 2, 'Street 5');
insert into addresses values (6, 2, 'Street 6');
insert into addresses values (7, 2, 'Street 7');
insert into addresses values (8, 3, 'Street 8');
insert into addresses values (9, 3, 'Street 9');
QUESTION 1: JOIN 1:n as array rather than as multiple results
I can easily do a select and get all records in people:
select * from people left join addresses on people.id = addresses.personId where people.id = 2;
The result of this is:
+------+------+--------------+------+----------+-------------+
| id | name | departmentId | id | personId | fullAddress |
+------+------+--------------+------+----------+-------------+
| 2 | Tony | 1 | 7 | 2 | Street 7 |
| 2 | Tony | 1 | 6 | 2 | Street 6 |
| 2 | Tony | 1 | 5 | 2 | Street 5 |
| 2 | Tony | 1 | 4 | 2 | Street 4 |
+------+------+--------------+------+----------+-------------+
Is there an easy, straightforward way to have only ONE record as result (the contact ID 2) and have the addresses in an ARRAY (or, a JSON containing the addresses) instead?
QUESTION 2: JOIN 1:1 as a sub-object rather than adding columns
I can easily get the department information by using joins:
select * from people left join departments on departments.id = people.departmentId where people.id = 2 ;
Result:
+------+------+--------------+------+------+
| id | name | departmentId | id | name |
+------+------+--------------+------+------+
| 2 | Tony | 1 | 1 | dev |
+------+------+--------------+------+------+
However, what if I wanted a result like this instead:
+------+------+--------------+----------------------+
| id | name | departmentId | departmentIdRecord |
+------+------+--------------+----------------------+
| 2 | Tony | 1 | { id: 1, name: "dev"}|
+------+------+--------------+----------------------+
Basically having the departmentIdRecord column as a JSON containing the department record.
Is this even possible with MySql?
I've spent way too long thinking about the title of this question, so I hope the one I chose is kinda understandable. But let me explain:
CREATE TABLE IF NOT EXISTS `course` (
`course_id` int(11) NOT NULL,
`course_name` varchar(255) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
INSERT INTO `course` (`course_id`, `course_name`) VALUES
(1, 'Course A'),
(2, 'Course B'),
(3, 'Course C'),
(4, 'Course D'),
(5, 'Course E');
CREATE TABLE IF NOT EXISTS `course_category` (
`course_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `course_category` (`course_id`, `category_id`) VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 2),
(3, 1),
(3, 3),
(5, 1),
(5, 3);
CREATE TABLE IF NOT EXISTS `enrolment` (
`enrolment_id` int(11) NOT NULL,
`course_id` int(11) NOT NULL,
`status` int(11) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `enrolment` (`enrolment_id`, `course_id`, `status`) VALUES
(1, 1, 0),
(2, 1, 1),
(3, 1, 0),
(4, 3, 0),
(5, 3, 0),
(6, 3, 0),
(7, 4, 1),
(8, 4, 1),
(9, 4, 1),
(10, 4, 0);
ALTER TABLE `course`
ADD PRIMARY KEY (`course_id`);
ALTER TABLE `enrolment`
ADD PRIMARY KEY (`enrolment_id`);
ALTER TABLE `course`
MODIFY `course_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=9;
ALTER TABLE `enrolment`
MODIFY `enrolment_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=11;
Fiddle: https://www.db-fiddle.com/f/dc3bgfMS5jTxE7wmp3SJ/0
# Table `course`
+-----------+-------------+
| course_id | course_name |
+-----------+-------------+
| 1 | Course A |
| 2 | Course B |
| 3 | Course C |
| 4 | Course D |
| 5 | Course E |
+-----------+-------------+
# Table `course_category`
+-----------+-------------+
| course_id | category_id |
+-----------+-------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 3 | 1 |
| 3 | 3 |
| 5 | 1 |
| 5 | 3 |
+-----------+-------------+
# Table `enrolment`
+--------------+-----------+--------+
| enrolment_id | course_id | status |
+--------------+-----------+--------+
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 0 |
| 4 | 3 | 0 |
| 5 | 3 | 0 |
| 6 | 3 | 0 |
| 7 | 4 | 1 |
| 8 | 4 | 1 |
| 9 | 4 | 1 |
| 10 | 4 | 0 |
+--------------+-----------+--------+
(There's also a category table, but that's not important for the question.)
I am trying to get the following result:
All courses have to be in the result set
The category IDs of each course have to be a single string, separated by commas
The total number of enrolments whose status is 1 should be counted per course (Bear in mind that "1" is only an example here, it could also be another number or a string to be checked against)
+-------------+--------------+------------+
| course_name | category_ids | enrolments |
+-------------+--------------+------------+
| Course A | 3,1,2 | 1 |
| Course B | 2 | 0 |
| Course C | 1,3 | 0 |
| Course D | NULL | 3 |
| Course E | 1,3 | 0 |
+-------------+--------------+------------+
I have tried using SUM with a condition and COUNT with the DISTINCT keyword and a WHERE condition, but I can't get to have all the necessary points fulfilled.
Experiment with COUNT:
SELECT
course_name,
GROUP_CONCAT(DISTINCT category_id) AS category_ids,
COUNT(DISTINCT enrolment_id) as enrolments
FROM
course
LEFT JOIN course_category USING (course_id)
LEFT JOIN enrolment USING (course_id)
WHERE status = 1 OR status IS NULL
GROUP BY course_id
+-------------+--------------+------------+
| course_name | category_ids | enrolments |
+-------------+--------------+------------+
| Course A | 3,1,2 | 1 |
| Course D | NULL | 3 |
+-------------+--------------+------------+
In this case I only get the course if at least one enrolment with status = 1 or no enrolment for the course exists, otherwise the whole course is missing in the result.
Experiment with SUM:
SELECT
course_name,
GROUP_CONCAT(DISTINCT category_id) AS category_ids,
SUM(IF(status=1,1,0)) as enrolments
FROM
course
LEFT JOIN course_category USING (course_id)
LEFT JOIN enrolment USING (course_id)
GROUP BY course_id
+-------------+--------------+------------+
| course_name | category_ids | enrolments |
+-------------+--------------+------------+
| Course A | 3,1,2 | 3 |
| Course B | 2 | 0 |
| Course C | 1,3 | 0 |
| Course D | NULL | 3 |
| Course E | 1,3 | 0 |
+-------------+--------------+------------+
In this case all the courses are there, but the counting is wrong, since I am not able to count distinct enrolments.
Which apporach am I missing?
SELECT c.course_name,
GROUP_CONCAT(cc.category_id ORDER BY cc.category_id) category_ids,
COALESCE(e.status, 0) enrolments
FROM course c
LEFT JOIN course_category cc USING (course_id)
-- calculate statuses per course separately
LEFT JOIN (SELECT course_id, SUM(status) status -- maybe SUM(status=1)?
FROM enrolment
GROUP BY course_id ) e USING (course_id)
GROUP BY c.course_name, e.status
fiddle
I think this would be simpler expressed with correlated subqueries rather than outer aggregation:
select c.course_name,
(
select group_concat(cc.category_id order by cc.category_id)
from course_category cc
where cc.course_id = c.course_id
) category_ids,
(
select coalesce(sum(e.status), 0)
from enrolment e
where e.course_id = c.course_id
) enrolments
from course c
order by c.course_name
--
-- Table structure for table `emp`
--
CREATE TABLE `emp` (
`Emp_id` int(11) NOT NULL,
`Name` varchar(30) DEFAULT NULL,
`Sup_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `emp`
--
INSERT INTO `emp` (`Emp_id`, `Name`, `Sup_id`) VALUES
(1, 'Mark 18', NULL),
(2, 'Iron Man', 1),
(3, 'Hulk', NULL),
(4, 'Ant-Man', 2);
--
-- Indexes for dumped tables
--
--
-- Indexes for table `emp`
--
ALTER TABLE `emp`
ADD PRIMARY KEY (`Emp_id`),
ADD KEY `Sup_id` (`Sup_id`);
--
this is my table DESC
i want output as this
like in case of iron man
iwant out put like this
2 Iron Man Mark 18
this means it want to check the Emp_id and Dispaly correspond name of Emp _id
DESc Stucture
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| Emp_id | int(11) | NO | PRI | NULL | |
| Name | varchar(30) | YES | | NULL | |
| Sup_id | int(11) | YES | MUL | NULL | |
+--------+-------------+------+-----+---------+-------+
Values Inside Table
+--------+----------+--------+
| Emp_id | Name | Sup_id |
+--------+----------+--------+
| 1 | Mark 18 | NULL |
| 2 | Iron Man | 1 |
| 3 | Hulk | NULL |
| 4 | Ant-Man | 2 |
+--------+----------+--------+
Output format i want
+--------+----------+--------+
| Emp_id | Name | Sup_id |
+--------+----------+--------+
| 1 | Mark 18 |NULL |
| 2 | Iron Man |MArk 18 |
| 3 | Hulk | NULL |
| 4 | Ant-Man |Iron Man|
+--------+----------+--------+
Just LEFT JOIN table on itself:
SELECT e.Emp_id, e.Name, es.Name AS Sup_name FROM emp AS e LEFT JOIN emp AS es ON e.Sup_id = es.Emp_id
In my problem, i have two columns in my 'Person' database. One column stores the group the person belongs to and a column which stores that he is the admin of the group. Once group can have only one admin.
My thought is that i should put a unique contraint on both of the columns. But the problem is that for the other users which are not admins they unique contraint gets broken.
What should be the proper solution of he problem.
Example Database
----------------------------------
Person_id | Group_Id | Is_admin
----------------------------------
1 | 9 | null
2 | 9 | null
3 | 9 | null
4 | 9 | 1
5 | 9 | null
6 | 4 | null
7 | 4 | null
8 | 4 | null
9 | 4 | 1
Now if I apply unique contraint on Group_Id and Is_admin they are unique but not in the case when the person is not an admin.
Consider creating a new table to contain information about your groups, where group_id is the primary index and a column like admin_id that refers back to your person table as a foreign key. That way you eliminate the is_admin column completely and have a more relational design.
It works OK for me...
DROP TABLE IF EXISTS person_group;
CREATE TABLE person_group
(Person_id INT NOT NULL PRIMARY KEY
,Group_Id INT NOT NULL
,Is_admin TINYINT NULL
,UNIQUE (group_id,is_admin)
);
INSERT INTO person_group VALUES
(1 , 9 , null),
(2 , 9 , null),
(3 , 9 , null),
(4 , 9 , 1),
(5 , 9 , null),
(6 , 4 , null),
(7 , 4 , null),
(8 , 4 , null),
(9 , 4 , 1);
SELECT * FROM person_group;
+-----------+----------+----------+
| Person_id | Group_Id | Is_admin |
+-----------+----------+----------+
| 6 | 4 | NULL |
| 7 | 4 | NULL |
| 8 | 4 | NULL |
| 9 | 4 | 1 |
| 1 | 9 | NULL |
| 2 | 9 | NULL |
| 3 | 9 | NULL |
| 5 | 9 | NULL |
| 4 | 9 | 1 |
+-----------+----------+----------+
INSERT INTO person_group VALUES (10, 9, null);
Query OK, 1 row affected (0.00 sec)
INSERT INTO person_group VALUES (11, 9, null);
Query OK, 1 row affected (0.00 sec)
I am trying to get a list of product id's that do not have certain colors (database is mysql)
Here are my tables:
product
+------------+-------------+
| product_id | description |
+------------+-------------+
| 1 | Widget 1 |
| 2 | Widget 2 |
| 3 | Widget 3 |
| 4 | Widget 4 |
| 5 | Widget 5 |
| 6 | Widget 6 |
+------------+-------------+
color
+----------+-------+
| color_id | name |
+----------+-------+
| 1 | red |
| 2 | black |
| 3 | white |
| 4 | green |
| 5 | blue |
| 6 | pink |
+----------+-------+
product_color
+------------+----------+
| product_id | color_id |
+------------+----------+
| 1 | 1 |
| 1 | 4 |
| 1 | 5 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
| 5 | 3 |
| 5 | 6 |
| 6 | 1 |
| 6 | 5 |
| 6 | 6 |
+------------+----------+
I want to select all the products that don't have colors 4 (green), 5 (blue), or 6 (pink).
So from the tables above, products 2, 3, and 4 would appear in the result set.
The best I've been able to do is:
SELECT product.*, GROUP_CONCAT(product_color.color_id) as color_ids
FROM product
LEFT JOIN product_color USING (product_id)
GROUP BY product.product_id
Here is my result set:
--------------------------------------
product_id description color_ids
--------------------------------------
1 Widget 1 1,4,5
2 Widget 2 2,3
3 Widget 3 1,2,3
4 Widget 4 NULL
5 Widget 5 3,6
6 Widget 6 1,3,6
And then I filter them programmatically but I'd prefer for the database to do all the work, if it can.
Optimally, I'd like my result set to look like this (all I need is the product id's).
------------
product_id
------------
2
3
4
Here is the schema and data just case your kind soul would like to help me out.
CREATE TABLE IF NOT EXISTS `color` (
`color_id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(25) NOT NULL,
PRIMARY KEY (`color_id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
INSERT INTO `color` (`color_id`, `name`) VALUES
(2, 'black'),
(5, 'blue'),
(4, 'green'),
(6, 'pink'),
(1, 'red'),
(3, 'white');
CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(10) unsigned NOT NULL auto_increment,
`description` varchar(25) NOT NULL,
PRIMARY KEY (`product_id`),
UNIQUE KEY `description` (`description`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
INSERT INTO `product` (`product_id`, `description`) VALUES
(1, 'Widget 1'),
(2, 'Widget 2'),
(3, 'Widget 3'),
(4, 'Widget 4'),
(5, 'Widget 5'),
(6, 'Widget 6');
CREATE TABLE IF NOT EXISTS `product_color` (
`product_id` int(10) unsigned NOT NULL,
`color_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`product_id`,`color_id`),
KEY `color_id` (`color_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `product_color` (`product_id`, `color_id`) VALUES
(1, 1),
(3, 1),
(6, 1),
(2, 2),
(3, 2),
(2, 3),
(3, 3),
(5, 3),
(1, 4),
(1, 5),
(6, 5),
(5, 6),
(6, 6);
I think a sub query in the where clause should be sufficient.
SELECT DISTINCT product_id
FROM product
WHERE product_id NOT IN (SELECT product_id FROM product_color WHERE color_id IN (4, 5, 6));
EDIT: I just saw the many to many bit! This works.