Joining two tables with comma separated string id in one table - mysql

This is branch detail table which have the branch name and the employee id work on that branch.
CREATE TABLE Branch_detail
(`nid` int,`branch` varchar(15), `emp_with_department` varchar(15));
INSERT INTO Branch_detail
(`nid`,`branch`, `emp_with_department`)
VALUES
(1,'Mumbai', '1,2,4'),
(2,'Banglore', '4,5');
| nid | branch | emp_with_department |
|-----|----------|---------------------|
| 1 | Mumbai | 1,2,4 |
| 2 | Banglore | 4,5 |
This is employee detail table with their respective designations.
CREATE TABLE Emp
(`id` int, `name` varchar(10), `Designations ` varchar(10));
INSERT INTO Emp
(`id`, `name`,`Designations `)
VALUES
(1,'Rantesh', 'Executive'),
(2,'Keith', 'Art'),
(3,'Nikhil', 'Executive'),
(4,'Gauresh', 'Art'),
(5,'Sumit', 'Executive');
| id | name | Designations |
|----|---------|--------------|
| 1 | Rantesh | Executive |
| 2 | Keith | Art |
| 3 | Nikhil | Executive |
| 4 | Gauresh | Art |
| 5 | Sumit | Executive |
This query i tried to get atleast which id is Executive or Art employee
SELECT a.nid,
GROUP_CONCAT(b.Designations ORDER BY b.id) Designations_Name
FROM Branch_detail a INNER JOIN Emp b
ON FIND_IN_SET(b.id, a.emp_with_department) > 0
GROUP BY a.nid
| nid | Designations_Name|
|-----|-------------------|
| 1 | Executive,Art,Art |
| 2 | Art,Executive |
Output should be:
count of Executives for nid=1,
count of Art for nid=1,
count of executives for nid=2,
count of Art for nid=1
OR
| nid | count(Designations_Name)|
|-----|--------------------------|
| 1 | 1,2,2 |
| 2 | 1,1 |
I want the Output like there on n location, how many executive and art employee is present in count ?
this is sqlfiddle where the demo is present
http://sqlfiddle.com/#!9/27aa51/1

Although you should implement Normalization.
But still, here is a query which will work for you, utilizing Nested Queries:
select inner_nest.nid, GROUP_CONCAT(designations_count)
FROM (SELECT a.nid, b.Designations, COUNT(DISTINCT b.id) as designations_count
FROM Branch_detail a
INNER JOIN Emp b ON FIND_IN_SET(b.id, a.emp_with_department) > 0
WHERE 1 = 1
GROUP BY a.nid, b.Designations ) AS inner_nest
GROUP BY inner_nest.nid
It gets Count for Art first, then Executives (as alphabetically Art comes first)

Related

What does the equal sign mean in this SQL Join statement?

I'm new to sql and do not understand what this join statement is doing. Does this statement ON people.state_code=states.state_abbrev mean that people.state_code and states.state_abbrev are now one?
SELECT people.first_name,
people.state_code, states.division
FROM people
JOIN states ON people.state_code=states.state_abbrev;
It will take the columns first_name and state_code from the table people and the column division from the table states and put them together in a join table where the entries in the state_code and state_abbrev columns match. The join table is produced only for display in response to this query; the underlying tables with the data entries are not amended.
In this case the '=' means equal (like values are equal) and is part of the join condition based on which data is retrieved by the select statement. You are 'linking' the two tables based on a condition so you can retrieve related data...
Relational data base - there are relations between tables and between data.
For example:
table_1
PERSON_ID FIRST_NAME LAST_NAME ADDRESS_ID
1 |John |Doe |2
table_2
ADRESS_ID STREET
1 | 5th Avenue
2 | 1st Street
SELECT FIRST_NAME, STREET
FROM table_1 t1
JOIN table_2 t2 ON t1.ADDRESS_ID = t2.ADDRESS_ID;
will return
John, 1st Street
Does this statement ON people.state_code=states.state_abbrev mean that people.state_code and states.state_abbrev are now one?
Answer: NO. people.state_code and states.state_abbrev should be the same value on the respective tables.
Let me give you an example taken from https://www.mysqltutorial.org/mysql-join/
Suppose you have below tables:
CREATE TABLE members (
member_id INT AUTO_INCREMENT,
members_name VARCHAR(100),
PRIMARY KEY (member_id)
);
CREATE TABLE committees (
committee_id INT AUTO_INCREMENT,
committees_name VARCHAR(100),
PRIMARY KEY (committee_id)
);
Some data examples:
+-----------+--------+
| member_id | members_name |
+-----------+--------+
| 1 | John |
| 2 | Jane |
| 3 | Mary |
| 4 | David |
| 5 | Amelia |
+-----------+--------+
+--------------+--------+
| committee_id | committees_name |
+--------------+--------+
| 1 | John |
| 2 | Mary |
| 3 | Amelia |
| 4 | Joe |
+--------------+--------+
To do the INNER JOIN we can use members_name and committees_name not the id because they are auto_increment and the data are not related.
So the query would be:
SELECT
m.member_id,
m.members_name AS member,
c.committee_id,
c.committees_name AS committee
FROM members m
INNER JOIN committees c ON c.name = m.name;
Giving below result:
+-----------+--------+--------------+-----------+
| member_id | member | committee_id | committee |
+-----------+--------+--------------+-----------+
| 1 | John | 1 | John |
| 3 | Mary | 2 | Mary |
| 5 | Amelia | 3 | Amelia |
+-----------+--------+--------------+-----------+
Conclusion: The values of the columns are equaly the same

Mysql query to get max age by section and if two or more has same age return student with smallest id

I have a table of students with temporary test values like this:
Table students
+----+-------------+-------+-----------+
| id | section_id | age | name |
+----+-------------+-------+-----------+
| 1 | 1 | 18 | Justin |
+----+-------------+-------+-----------+
| 2 | 2 | 14 | Jillian |
+----+-------------+-------+-----------+
| 3 | 2 | 16 | Cherry |
+----+-------------+-------+-----------+
| 4 | 3 | 19 | Ronald |
+----+-------------+-------+-----------+
| 5 | 3 | 21 | Marie |
+----+-------------+-------+-----------+
| 6 | 3 | 21 | Arthur |
+----+-------------+-------+-----------+
I want to query the table such that I want to get all the maximum ages of each section. However, if two students have the same age, the table produced will return the student with smallest id.
Return:
+----+------------+-----+--------+
| id | section_id | age | name |
+----+------------+-----+--------+
| 1 | 1 | 18 | Justin |
+----+------------+-----+--------+
| 3 | 2 | 16 | Cherry |
+----+------------+-----+--------+
| 5 | 3 | 21 | Marie |
+----+------------+-----+--------+
I tried this query:
SELECT ANY_VALUE(id), ANY_VALUE(section_id), MAX(age), ANY_VALUE(name) FROM
(SELECT id, section_id, age, name FROM students ORDER BY id) as X
GROUP BY section_id
Unfortunately, there are instances that id does not match the age and name.
I have on my end:
sql_mode = only_full_group_by
and I don't have a privilege to edit that, hence the any_value function but I have no idea how to use it.
This will do what you want.
It starts by finding the maximum age per section (including duplicates).
Then it joins those results with the minimum id per section (to eliminate duplicates).
And finally, select all fields for the matching id and section combinations.
SELECT s3.*
FROM students s3
INNER JOIN (
SELECT MIN(s2.id) AS id, s2.section_id
FROM students s2
INNER JOIN (
SELECT s1.section_id, MAX(s1.age) AS age
FROM students s1
GROUP BY s1.section_id
) s1 USING (section_id, age)
GROUP BY s2.section_id
) s2 USING (id, section_id);
Working SQL fiddle: https://www.db-fiddle.com/f/aezgAYM6A5KnXykceB7At1/0
I would simply use a correlated subquery:
select s.*
from students s
where s.id = (select s2.id
from students s2
where s2.section_id = s.section_id
order by s2.age desc, s2.id asc
limit 1
);
This is pretty much the simplest way to express the logic. And with an index on students(section, age, id), it should be the most performant as well.

How to enhance my subquery with two tables to search with 3 tables if the main table have 2 primary keys?

The main table is 'M_Order' which contains orders and its details
M_order
row_id |Order_Num | Order_Status | created | created_by | Account_ID
--------------------------------------------------------------
1-a | 1 | complete | 2016-08-25 | jhon | 3-a
1-b | 2 | pending | 2016-08-26 | mike | 3-b
1-c | 3 | failed | 2015-08-27 | mike | 3-c
1-d | 4 | pending | 2015-08-25 | jhon | 3-d
1-e | 5 | failed | 2016-08-26 | mike | 3-e
1-f | 6 | New | 2017-03-27 | jhon | 3-f
The 2nd table is 'Order_itmes' which contain lists of products for each order
Order_itmes
row_id |Order_id | Product_ID
--------------------------------------------------------------
2-a | 1-a | Prod_1
2-b | 1-b | Prod_2
2-c | 1-c | Prod_1
2-d | 1-d | Prod_1
2-e | 1-e | Prod_1
2-f | 1-f | Prod_3
The row_id from 'M_order' table is unique and available in the table 'Order_items' as Order_id
I have a query which show the orders with specific product_id from 'Order_items'
Select Order_Num,Order_status,created ,created_by
from M_order where row_id in (select Order_id from Order_itmes where
Product_ID='Prod_1')
where Order_Status='Failed'
Order By created desc
If I have a 3rd table 'Accounts' which having column row_id as the same column in account_id from 'M_Order'
Accounts
row_id | Name
--------------------------------------------------------------
3-a | cutomer1
3-b | cutomer2
3-c | cutomer3
3-d | cutomer4
3-e | cutomer5
3-f | cutomer6
how can I enhance the above query to list the customer name in results and shows as the following
Order_Num | Order_status | created | created_by | name
-------------------------------------------------------------
5 | failed |2016-08-26 | mike | customer5
3 | failed |2015-08-27 | mike | customer3
I believe it's better to use JOIN in this case as sub queries will get complicated once we have more than 2 tables. A simple JOIN would work here, e.g.:
SELECT o.order_num, o.order_status, o.created, o.created_by, a.name
FROM m_order o JOIN order_items oi ON o.row_id = oi.order_id
JOIN accounts a ON o.account_id = a.row_id
WHERE oi.product_id = 'Prod_1';
You can add a join to Accounts
select o.Order_Num, o.Order_status, o.created, o.created_by, a.name
from M_order o
inner join Accounts a
on o.Account_ID = a.row_id
where o.row_id in (
select Order_id
from Order_items
where Product_ID='Prod_1'
)
and o.Order_Status='Failed'
order by o.created desc
How can table have 2 PK ?
I am not commenting on index becasue you hv posted the real table structure and real data type.
Wht is the purpose of IDs as 1-a or 2-a or 3-a ?
Table should auto increament id as CI.
Also Order table contain million of record so you can't simply query any thing without using paging.
Try this,
declare #M_order table(row_id varchar(30),Order_Num int,Order_Status varchar(30)
,created date,created_by varchar(30),Account_ID varchar(30))
insert into #M_order VALUES
('1-a',1,'complete','2016-08-25','jhon','3-a')
,('1-b',2,'pending','2016-08-26','mike','3-b')
,('1-c',3,'failed','2015-08-27','mike','3-c')
,('1-d',4,'pending','2015-08-25','jhon','3-d')
,('1-e',5,'failed','2016-08-26','mike','3-e')
,('1-f',6,'New ','2017-03-27','jhon','3-f')
declare #Order_itmes table(row_id varchar(30),Order_id varchar(30), Product_ID varchar(30))
insert into #Order_itmes VALUES
('2-a','1-a','Prod_1'),('2-b','1-b','Prod_2'),('2-c','1-c','Prod_1')
,('2-d','1-d','Prod_1'),('2-e','1-e','Prod_1'),('2-f','1-f','Prod_3')
declare #Accounts table(row_id varchar(30),Name varchar(30))
insert into #Accounts values
('3-a','cutomer1') ,('3-b','cutomer2') ,('3-c','cutomer3')
,('3-d','cutomer4') ,('3-e','cutomer5') ,('3-f','cutomer6')
select mo.Order_Num ,mo.Order_status , mo.created , mo.created_by ,name
from #M_order MO
inner join #Accounts A
on mo.Account_ID=a.row_id
where Order_status='failed'
and
exists(
select Order_id from #Order_itmes OI where
Product_ID='Prod_1' and oi.Order_id=mo.row_id
)

One to many relation in MYSQL produces wrong results

I have two tables- students and subjects. Student table stores list of all student and subject table stores all the subjects that these students have been enrolled into.
CREATE TABLE students
(`id` int, `name` varchar(7));
INSERT INTO students
(`id`, `name`)
VALUES
(1, 'Jason'),
(2, 'Matt'),
(3, 'Abram')
;
CREATE TABLE subjects
(`id` int,`student_id` int, `subject` varchar(15));
INSERT INTO subjects
(`id`,`student_id`, `subject`)
VALUES
(1,1, 'Math'),
(2,1, 'Physics'),
(3,2, 'Chemistry'),
(4,2, 'Math'),
(5,2, 'English'),
(6,3, 'Chemistry')
;
And upon executing the following query
SELECT STUD.id,STUD.name,SUB.subject
FROM students AS STUD
LEFT JOIN subjects AS SUB
ON STUD.id=SUB.student_id
;
gives a result set like this (which is not what I am trying to get)
++++++++++++++++++++++++++++++++++++
id | name | subject |
++++++++++++++++++++++++++++++++++++
| 1 | Jason | Math |
------------------------------------
| 1 | Jason | Physics |
------------------------------------
| 2 | Matt | Chemistry |
------------------------------------
| 2 | Matt | Math |
------------------------------------
| 2 | Matt | English |
------------------------------------
| 3 | Abram | Chemistry |
------------------------------------
The students gets repeated in the list.
And when I try
SELECT STUD.id,STUD.name,SUB.subject
FROM students AS STUD
LEFT JOIN subjects AS SUB
ON STUD.id=SUB.student_id
GROUP BY STUD.id
;
I get (which is again wrong)
++++++++++++++++++++++++++++++++++++
id | name | subject |
++++++++++++++++++++++++++++++++++++
| 1 | Jason | Math |
------------------------------------
| 2 | Matt | Chemistry |
------------------------------------
| 3 | Abram | Chemistry |
------------------------------------
My Desired result looks like this:
++++++++++++++++++++++++++++++++++++++++++++++++
id | name | subject |
++++++++++++++++++++++++++++++++++++++++++++++++
| 1 | Jason | Math, Physics |
------------------------------------------------
| 2 | Matt | Chemistry,Math ,English |
------------------------------------------------
| 3 | Abram | Chemistry |
------------------------------------------------
I am a novice in Relational databases.
Why is group by not giving correct result?
Thanks in advance.
SQL FIDDLE
You can achieve this by
SELECT STUD.id,STUD.name,GROUP_CONCAT(SUB.subject) as subject
FROM students AS STUD
LEFT JOIN subjects AS SUB
ON STUD.id=SUB.student_id
GROUP BY STUD.id;
Actually it gives correct result when you have group by some column in sql it will returns only first record in group so for your desired result you needs to used GROUP_CONCAT function which return all comma separated values in given column.

many-to-many and many-to-many intersections

Say I have a database that has people, grocery stores, and items you can buy in the store, like so:
Stores People Foods
----------------- ------------------ ------------------
| id | name | | id | name | | id | name |
----------------- ------------------ ------------------
| 1 | Giant | | 1 | Jon Skeet | | 1 | Tomatoes |
| 2 | Vons | | 2 | KLee1 | | 2 | Apples |
| 3 | Safeway | ------------------ | 3 | Potatoes |
----------------- ------------------
I have an additional table which keep track of which stores sell what:
Inventory
--------------------
| store_id| food_id|
--------------------
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
--------------------
And I have another table that has shopping lists on it
Lists
---------------------
| person_id| food_id|
---------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
---------------------
My question is, given a person, or their id, what is the best way to figure out what stores they can go to so they will get everything on their list. Is there a pattern for these types of computations in MySQL?
My attempt (very ugly and messy) is something like:
-- Given that _pid is the person_id we want to get the list of stores for.
SELECT stores.name, store_id, num, COUNT(*) AS counter
FROM lists
INNER JOIN inventory
ON (lists.food_id=inventory.food_id)
INNER JOIN (SELECT COUNT(*) AS num
FROM lists WHERE person_id=_pid
GROUP BY person_id) AS T
INNER JOIN stores ON (stores.id=store_id)
WHERE person_id=_pid
GROUP BY store_id
HAVING counter >= num;
Thanks for your time!
Edit SQL Fiddle with Data
If I were to solved the problem, I'll join the four tables with their linking column (specifically the foreign keys) then a subquery on the HAVING clause to count the number of items on the list for each person. Give this a try,
SET #personID := 1;
SELECT c.name
FROM Inventory a
INNER JOIN Foods b
ON a.food_id = b.id
INNER JOIN Stores c
ON a.store_id = c.id
INNER JOIN Lists d
ON d.food_id = b.id
WHERE d.person_id = #personID
GROUP BY c.name
HAVING COUNT(DISTINCT d.food_id) =
(
SELECT COUNT(*)
FROM Lists
WHERE person_ID = #personID
)
SQLFiddle Demo
#JohnWoo: why DISTINCT?
Another one...
SET #pid=2;
SELECT store_id, name
FROM inventory
JOIN lists ON inventory.food_id=lists.food_id
JOIN stores ON store_id=stores.id
WHERE person_id=#pid
GROUP BY store_id
HAVING COUNT(*)=(
SELECT COUNT(*)
FROM lists
WHERE person_id=#pid
);