SQL Join commands to discover highest level of product - mysql

So basically what I am trying to do is this... My task is to find people who have "CSM", "LSP" or "CSD" but also joining information from Table "client_personal" that matches the "client_no" from the results of the search on "CSM", "LSP", or "CSD"
Im sure Im doing this all "bass ackwords" but end goal is to show customer information including other fields in table "client_personal" so sales can do a sales pitch for those who have "CSM" but not records of "LSP" or "CSD" (considered higher levels) - Unfortunately there is 1: an assload of mess, tons of dupes, and multiple entries and 2: theres not an easier database to work with that would have "highest level achieved".
SELECT client_no,product,status,paid_date
FROM client_educational
LEFT JOIN client_personal
ON client_educational.client_no = client_personal.client_no
WHERE product='CSM'
OR product='LSP'
OR product='CSD'
Heres the databases that I am interested in:
client_educational
client_no product status pd_date
500000 CSM pd-cert 2018-06-20
500001 CSM pd-cert 2018-06-20
500001 LSP pd-cert 2018-06-20
500002 CSM pd-cert 2018-06-20
500002 LSP pd-cert 2018-06-20
500002 CSD pd-cert 2018-06-20
client_personal
client_no name email phone
500000 John Doe mail#mail.com 555-555-5555
500001 John Shmoe mail#mail.com 555-555-5555
500002 John Howe mail#mail.com 555-555-5555
So what Im needing is not only to combine the results, but also display only those who only have product "CSM" but not "LSP" or "CSD" by using the "client_no" row as the unique identifier....
Something like this...
client_no product status pd_date name email phone
500000 CSM pd-cert 2018-06-20 John Doe mail#mail.com 555-555-5555

Try it use left join
select
T1.`client_no`,
T2.`product`,
T2.`status`,
T2.`pd_date`,
T1.`name`,
T1.`email`,
T1.`phone`
from client_personal T1
left join client_educational T2 on T1.`client_no` = T2.`client_no`
and T2.`product` = 'CSM'
left join client_educational T3 on T1.`client_no` = T3.`client_no`
and T3.`product` IN('LSP', 'CSD')
where T2.`client_no` is not null
and T3.`client_no` is null
| client_no | product | status | pd_date | name | email | phone |
| 500000 | CSM | pd-cert | 2018-06-20 00:00:00 | John Doe | mail#mail.com | 555-555-5555 |
TEST DDL
CREATE TABLE client_educational
(`client_no` int, `product` varchar(3), `status` varchar(7), `pd_date` datetime)
;
INSERT INTO client_educational
(`client_no`, `product`, `status`, `pd_date`)
VALUES
(500000, 'CSM', 'pd-cert', '2018-06-20 00:00:00'),
(500001, 'CSM', 'pd-cert', '2018-06-20 00:00:00'),
(500001, 'LSP', 'pd-cert', '2018-06-20 00:00:00'),
(500002, 'CSM', 'pd-cert', '2018-06-20 00:00:00'),
(500002, 'LSP', 'pd-cert', '2018-06-20 00:00:00'),
(500002, 'CSD', 'pd-cert', '2018-06-20 00:00:00')
;
CREATE TABLE client_personal
(`client_no` int, `name` varchar(10), `email` varchar(13), `phone` varchar(12))
;
INSERT INTO client_personal
(`client_no`, `name`, `email`, `phone`)
VALUES
(500000, 'John Doe', 'mail#mail.com', '555-555-5555'),
(500001, 'John Shmoe', 'mail#mail.com', '555-555-5555'),
(500002, 'John Howe', 'mail#mail.com', '555-555-5555')
;
DB Fiddle - TEST DEMO

You could use conditional aggregation checks for your criteria display only those who only have product "CSM" but not "LSP" or "CSD"
select cp.client_no,cp.name,cp.email,cp.phone
from client_personal cp
join client_educational ce on cp.client_no = ce.client_no
group by cp.client_no,cp.name,cp.email,cp.phone
having sum(ce.product = 'CSM') > 0
and sum(ce.product IN('LSP', 'CSD')) = 0
In mysql when an expression is used inside sum(a= b) it will result as a boolean 0/1 so you can get your conditional count using above.
Another way using exists
select cp.*
from client_personal cp
join client_educational ce on cp.client_no = ce.client_no
where ce.product = 'CSM'
and not exists (
select 1
from client_educational
where product IN('LSP', 'CSD')
and client_no = cp.client_no
)
Demo for both queries

Related

MySQL - Join if no duplicate

I want to Join to table. the condition is I want to only join those rows which have only one row to match. eg.
books:
id | name | price
1 | book1 | 19
2 | book2 | 19
3 | book3 | 30
price_offer:
id | offer | price
1 | offer1 | 19
2 | offer2 | 30
so now if I do select query on these table:
SELECT * FROM price_offer
JOIN books ON price_offer.price = books.price
I only want to join book with id 3 as it have only one match with price_offer table.
You could use a self join for books table to pick a book with only single match
select po.*, b1.*
from price_offer po
join books b1 on po.price = b1.price
join (
select price,max(id) id
from books
group by price
having count(*) = 1
) b2 on b1.id = b2.id
Demo
Try following query:
Sample data:
create table books(id int, name varchar(10), price int);
insert into books values
(1, 'book1', 19),
(2, 'book2', 19),
(3, 'book3', 30);
create table price_offer(id int, offer varchar(10), price int);
insert into price_offer values
(1, 'offer1', 19),
(2, 'offer2', 30);
Query:
select max(b.id)
from price_offer p
left join books b on b.price = p.price
where p.id is not null
group by b.price
having count(*) = 1;
If you want to avoid nesting queries where you have to use self-joins, you can use window-functions of MySQL 8.0.11, which are exactly for cases like this

MySQL Fetch Result Using Multiple Joins in one query?

I have below tables
tbl_user
uid first_name last_name email_id
1 steve martin steve1#gmail.com
2 mark lee mark1#gmail.com
3 nelson wise nelson23#gmail.com
tbl_tier
tier_id tier_name points_required
1 Silver 100
2 Gold 200
3 Platinum 300
tbl_tier_earned
id tier_id uid
1 1 1
2 2 1
3 3 1
4 1 2
5 2 2
6 1 3
I need unique users with their current tiers like:
first_name last_name email_id current_tier
steve martin steve1#gmail.com Platinum
mark lee mark1#gmail.com Gold
I have tried below query but it gives me only 1 result:
SELECT u.first_name,u.last_name,u.email_id, t.tier_name
FROM tbl_tier_earned AS tte
INNER JOIN tbl_user AS u
ON u.uid = tte.uid
INNER JOIN tbl_tier AS t
ON tte.tier_id = t.tier_id
WHERE u.email_id!=""
ORDER BY t.points_required DESC LIMIT 0,1
How can I retrieve above data using mysql query?
It appears that the current tier is given by the max tier_id in the tbl_tier_earned table, for each user. One approach here would be to join the user table to a subquery on the tbl_tier_earned table which finds the max tier.
SELECT
u.first_name,
u.last_name,
u.email_id,
COALESCE(t2.tier_name, 'NA') AS current_tier
FROM tbl_user u
LEFT JOIN
(
SELECT uid, MAX(tier_id) AS max_tier_id
FROM tbl_tier_earned
GROUP BY uid
) t1
ON u.uid = t1.uid
LEFT JOIN tbl_tier t2
ON t1.max_tier_id = t2.tier_id;
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE tbl_user
(`uid` int, `first_name` varchar(6), `last_name` varchar(6), `email_id` varchar(18))
;
INSERT INTO tbl_user
(`uid`, `first_name`, `last_name`, `email_id`)
VALUES
(1, 'steve', 'martin', 'steve1#gmail.com'),
(2, 'mark', 'lee', 'mark1#gmail.com'),
(3, 'nelson', 'wise', 'nelson23#gmail.com')
;
CREATE TABLE tbl_tier
(`tier_id` int, `tier_name` varchar(8), `points_required` int)
;
INSERT INTO tbl_tier
(`tier_id`, `tier_name`, `points_required`)
VALUES
(1, 'Silver', 100),
(2, 'Gold', 200),
(3, 'Platinum', 300)
;
CREATE TABLE tbl_tier_earned
(`id` int, `tier_id` int, `uid` int)
;
INSERT INTO tbl_tier_earned
(`id`, `tier_id`, `uid`)
VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1),
(4, 1, 2),
(5, 2, 2),
(6, 1, 3)
;
Query 1:
SELECT c.first_name, c.last_name, c.email_id,
(SELECT tier_name from tbl_tier WHERE points_required = max(b.points_required)) as current_tier
FROM tbl_tier_earned a
INNER JOIN tbl_tier b ON a.tier_id = b.tier_id
INNER JOIN tbl_user c ON a.uid = c.uid
GROUP BY c.first_name, c.last_name, c.email_id
Results:
| first_name | last_name | email_id | current_tier |
|------------|-----------|--------------------|--------------|
| mark | lee | mark1#gmail.com | Gold |
| nelson | wise | nelson23#gmail.com | Silver |
| steve | martin | steve1#gmail.com | Platinum |

Get rolled up sub total and total by joining multiple tables

Extending Rolling up addition using mysql
I can get rolling sum of rows https://stackoverflow.com/users/1529673/strawberry's answer for basic stuff so i tried to extend query by joining multiple tables but 1_keyworddefs.name='K2' is not affected. Getting same answer for 1_keyworddefs.name='K1' and K2.
Working query but directly by specifying 1_bugs.bug_id='2':
SELECT x.*, x.cf1 + x.cf2 sub_total, sum(y.cf1 + y.cf2) total FROM 1_bugs x INNER JOIN 1_bugs y ON y.bug_id <= x.bug_id INNER JOIN 1_keywords ON 1_keywords.bug_id = y.bug_id WHERE (x.bug_date BETWEEN '2016-07-19' AND '2016-07-22') AND (x.bug_id='2') AND (y.bug_status = 'VERIFIED' OR y.bug_status = 'CLOSED') AND (1_keywords.bug_id = x.bug_id) GROUP BY x.bug_id
Exact output (but i want to join table where 1_keywords.bug_id=1_bugs.bug_id matches instead of directly specifying 1_bugs.bug_id='2'):
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
Non-working query by joining tables (expecting answer like above):
SELECT x.*, x.cf1 + x.cf2 sub_total, sum(y.cf1 + y.cf2) total FROM 1_bugs x INNER JOIN 1_bugs y ON y.bug_id <= x.bug_id LEFT JOIN 1_keywords ON 1_keywords.bug_id = y.bug_id LEFT JOIN 1_keyworddefs ON 1_keyworddefs.id=1_keywords.keywordid AND 1_keyworddefs.name='K2' and 1_keywords.bug_id = y.bug_id WHERE (x.bug_date BETWEEN '2016-07-19' AND '2016-07-22') AND (y.bug_status = 'CLOSED' OR y.bug_status = 'VERIFIED') GROUP BY x.bug_id;
Expected:
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
Actual:
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
3 2016-07-22 2 2 CLOSED 4 7
** Here bug_id -> 3 row comes wrongly because 1_bugs.bug.id=1_keywords.bug_id doesn't match and there is no 1_keywords.bug_id='3' present in 1_keywords table.
DDLs:
-- 1_bugs table1 (master table) :
CREATE TABLE `1_bugs` (`bug_id` int(11) NOT NULL, `bug_date` date NOT NULL, `cf1` int(11) NOT NULL, `cf2` int(11) NOT NULL, `bug_status` varchar(200) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_bugs` (`bug_id`, `bug_date`, `cf1`, `cf2`, `bug_status`) VALUES (1, '2016-07-19', 3, 2, 'RESOLVED'), (2, '2016-07-19', 2, 1, VERIFIED'), (3, '2016-07-22', 2, 2, 'CLOSED');
-- 1_keywords table2 (having keyword ids):
CREATE TABLE `1_keywords` (`bug_id` int(11) NOT NULL, `keywordid` varchar(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_keywords` (`bug_id`, `keywordid`) VALUES (1, 'K1'), (2, 'K2');
-- 1_keyworddefs table3 (having keyword names according to keywordid):
CREATE TABLE `1_keyworddefs` (`id` int(11) NOT NULL, `name` varchar(200) NOT NULL, `description` varchar(200) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_keyworddefs` (`id`, `name`, `description`) VALUES (1, 'K1', 'My K1 item'), (2, 'K2', 'My K2 item');
Can someone please point me what i'm doing wrong?
Obviously things where not clear... after long discussions turns out what you wanted was to extract from bugzilla database a list of bugs for specific keyword grouped by date and a sum of cf1 + cf2 and the same sum of only 'opened' and 'verified' bugs.
Here we go
SELECT
b.lastdiffed,
d.name,
d.description,
SUM(IF(b.bug_status IN('CLOSED', 'VERIFIED'), b.cf1 + b.cf2, 0)) AS sub_total,
SUM(b.cf1 + b.cf2) AS running
FROM
bugs AS b
JOIN keywords AS k
ON ( k.bug_id = b.bug_id )
JOIN keyworddefs AS d
ON ( d.id = k.keywordid )
WHERE
1
AND d.name = 'SONY'
GROUP BY (b.lastdiffed)
ORDER BY b.lastdiffed ASC
Giving this result
+------------+------+-------------+-----------+---------+
| lastdiffed | name | description | sub_total | running |
+------------+------+-------------+-----------+---------+
| 2016-05-20 | SONY | sony items | 7 | 7 |
| 2016-06-20 | SONY | sony items | 11 | 17 |
| 2016-06-27 | SONY | sony items | 5 | 5 |
| 2016-06-29 | SONY | sony items | 5 | 5 |
+------------+------+-------------+-----------+---------+
Hope this helps.

how to do subquery with 3 tables and using where clause in multi values?

I want to do subquery with 3 tables and using where in multi values but I always get syntax error. I have to do reporting in Report Builder 3.0
Table A: record_id, Surname, Given Name
Table C: row_id, competency_code, competency_name
Table PC: link_id, record_id, row_id, attainment_date
I would like to join the tables into 1 table. One person will have some completion of competency_code and different with other person. the completion of competency_code based on the attainment_date. I also think to use iff function for attainment_date in competency_code value as complete/yes.
The table that I would like to create is:
Record_Id | Surname | GivenName | Code 1 | Code 2 | Code 3 | Code 4 | Code 5
01 | AA | AA | Complete | Complete | Complete | | Complete
02 | BB | BB | Complete | Complete | | Complete |
03 | CC | CC | | Complete | Complete | | Complete
here is the query that I tried to do.
select distinct a.id, a.surname, a.given_name
from all a
join
(
select pc.attainment_date
from personnel_competency pc
join
(
select c.code, c.name
from competency c)
competency c on (c.row_no = pc.linkid)
)
personnel_competency pc on (pc.id = a.id)
where c.code in ('ABC', 'BCD', 'ABE', 'DEA', 'DEF', 'POS', 'SAQ', 'LOP')
and pc.attainment_date < now()
order by a.record_id
My skill in SQL is very basic. Whether other ways to make the table like that?
Are you looking for a SQL to get your result. If so I think this is what you are looking for ..
It would help if you posted some sample data.
You can test it at
SQLFiddle
Here is the script ..
-- Generate schema and data
create table tableA (id int, surname varchar(30), given_name varchar(30));
create table tablePC (link_id int, id int, attainment_date datetime);
create table tableC (row_id int, competency_code varchar(20), Competency_name varchar(30));
insert into tableA (id, surname, given_name)
values (1, 'AA', 'AAgn')
, (2, 'BB', 'BBgn')
insert into tablePC (link_id, id, attainment_date)
values (1, 1, '2014-09-11')
, (2, 1, '2014-09-10')
, (3, 2, '2014-09-11')
insert into tableC (row_id, competency_code, Competency_name)
values (1, 'ABC', 'completed\Yes')
, (1, 'BCD', 'completed')
, (1, 'ABE', 'completed')
, (2, 'ABC', 'completed')
, (2, 'BCD', 'completed')
, (3, 'ABC', 'completed')
, (3, 'ABE', 'completed')
-- ===============
select *
from tableA TA
inner join tablePC PC
on TA.id = PC.id
inner join
(
select row_id, [ABC] as ABC, [BCD] as BCD, [ABE] as ABE
from tableC TC
pivot
(
max(Competency_name)
for Competency_code in ([ABC], [BCD], [ABE])
) as TCPVT
) TC
on PC.link_id = TC.row_id
where PC.attainment_date < GETDATE()

Columns with multiple values

I have one table called Employee that contains the following information like
ID Name Skills
1 xyz java,php,dotnet
2 abc ruby,java,python
Skills column saves comma seprated values. it could be one or more.
I want to design a query based on OR operate.When user search java, Database displays two employees likes xyz, abc.
I have tried this query but no result comes out:
SELECT m
FROM Employee m
Where m.Skills LIKE '%JAVA% MS PAINT%'
Any Suggestion?
Ideally you should not store the data in a comma-separated list. You should create a join table between the employees and the skills:
CREATE TABLE employees (`e_id` int, `e_name` varchar(3));
INSERT INTO employees (`e_id`, `e_name`)
VALUES
(1, 'xyz'),
(2, 'abc');
CREATE TABLE skills (`s_id` int, `s_name` varchar(6));
INSERT INTO skills (`s_id`, `s_name`)
VALUES
(1, 'java'),
(2, 'php'),
(3, 'dotnet'),
(4, 'ruby'),
(5, 'python');
CREATE TABLE employees_skills (`e_d` int, `s_id` int);
INSERT INTO employees_skills
(`e_d`, `s_id`)
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 4),
(2, 1),
(2, 5);
Then when you want to select from the tables you will use:
select *
from employees e
inner join employees_skills es
on e.e_id = es.e_id
inner join skills s
on es.s_is = s.s_id
where s.s_name in ('java', 'ruby')
Or you can use the OR clause:
select *
from employees e
inner join employees_skills es
on e.e_id = es.e_id
inner join skills s
on es.s_is = s.s_id
where s.s_name = 'java'
or s.s_name = 'ruby'
use like not good solution. Full scan and slow query.
Create new table with catalog of skills.
Create table user_skills
You should set up your tables like this:
Employee:
ID | Name
---+------
1 | xyz
2 | abc
Skill:
ID | Name
---+------
1 | java
2 | php
3 | dotnet
4 | ruby
5 | python
EmployeeSkills:
ID | EmployeeID | SkillID
---+------------+----------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 4
5 | 2 | 1
6 | 2 | 5
the query to find employees with skills in java would look like this
SELECT
E.Name
FROM
Employee AS E
INNER JOIN
EmployeeSkill AS ES
ON
ES.EmployeeID = E.ID
INNER JOIN
Skill AS S
ON
ES.SkillID = S.ID
WHERE
S.Name = 'java'
select name from table where skill like '%java%' should do