mysql "and" logic within result set - mysql

Say I have a data set like the following:
table foo
id | employeeType | employeeID
-------------------------
1 | Developer | 1
2 | Developer | 2
3 | Developer | 3
4 | Manager | 1
5 | Manager | 4
6 | Manager | 5
7 | CEO | 1
8 | CEO | 6
and I wanted to run a query that would return all the employeeids (along with the employeeTypes) where there is a common employee id between all employeeTypes (that's the 'and' logic. ONly employeeIDs that have all employeeTypes will return. employeeType = Developer and employeeType=Manager and employeeType=CEO). For the data above the example output would be
result table
id | employeeType | employeeID
-------------------------
1 | Developer | 1
4 | Manager | 1
7 | CEO | 1
I was able to do this when I only had only TWO employeeTypes by self joining the table like this.
select * from foo as fooOne
join foo as fooTwo
on fooOne.employeeID = fooTwo.employeeID
AND
fooOne.employeeType <> fooTwo.employeeType
that query returns a result set with values from fooTwo when the 'and' logic matches, but again, only for two types of employees. My real use case scenario dictates that I need to be able to handle a variable number of employeeTypes (3, 4, 5, etc...)
Any thoughts on this would be greatly appreciated.

This should return the rows that you want:
SELECT foo.*
FROM
foo
WHERE
employeeID IN (
SELECT employeeID
FROM foo
GROUP BY employeeID
HAVING COUNT(DISTINCT employeeType) =
(SELECT COUNT(DISTINCT employeeType)
FROM foo)
)
Please see a fiddle here.
The inner query will return the number of distinct employee types:
(SELECT COUNT(DISTINCT employeeType) FROM foo)
The middle query will return all the employee IDs that have the maximum number of employee types:
SELECT employeeID
FROM foo
GROUP BY employeeID
HAVING COUNT(DISTINCT employeeType) =
(SELECT COUNT(DISTINCT employeeType) FROM foo)
and the outer query will return the whole rows.

You can try a subquery to make it dynamic
SELECT employeeID, employeeType
FROM foo
WHERE employeeID IN (
SELECT employeeID
FROM foo
GROUP BY employeeID
HAVING COUNT(DISTINCT employeeType) = (SELECT COUNT(DISTINCT employeeType) FROM foo)
)

I agree that this might be looked down as a very inefficient/hacky way of doing things, but this should still get the job done. And frankly, I can't see any other way out of this.
SELECT * FROM (
SELECT EMPLOYEE_ID, GROUP_CONCAT(DISTINCT EmployeeType ORDER BY EmployeeType) AS Roles
FROM EMPLOYEES GROUP BY EMPLOYEE_ID
) EMPLOYEE_ROLES
WHERE EMPLOYEE_ROLES.Roles = 'CEO,Developer,Manager';
Note that the comma separated list of roles provided in the end is in the alphabetical order.

Related

Recursive query in MySQL( find direct and indirect supervisor of Bob )?

There is one problem that I encountered and that can we solved using recursive query but my code is giving error in MySQL.
Problem is as follow:
person supervisor
Mary Susan
David Mary
Bob Alice
Alice David
Find all supervisor(direct and indirect) of Bob?
Here is my solution for above question:
create recursive view find_all(person,supervisor,depth) as
(
select person,supervisor,0 as depth from employee as e
where e.person = 'Bob'
union all
select in1.person,out1.supervisor,in1.depth+1
from find_all as in1,employee as out1
and in1.supervisor = out1.person
and in1.depth<=100
);
And for output:
select * from find_all;
Here's one approach. Just create a normal view. The view contains the recursive WITH clause.
CREATE VIEW find_all(person,supervisor,depth) AS
WITH RECURSIVE cte1 AS (
SELECT person, supervisor, 0 AS depth
FROM employee AS e
WHERE e.person = 'Bob'
UNION ALL
SELECT in1.person, out1.supervisor, in1.depth+1
FROM cte1 AS in1, employee AS out1
WHERE in1.supervisor = out1.person
AND in1.depth <= 100
)
SELECT * FROM cte1
;
Result:
select * from find_all;
+--------+------------+-------+
| person | supervisor | depth |
+--------+------------+-------+
| Bob | Alice | 0 |
| Bob | David | 1 |
| Bob | Mary | 2 |
| Bob | Susan | 3 |
+--------+------------+-------+
Alternate form:
The <from clause> contains a <table reference list> which is a comma separated list of <table reference>, <table reference>, .... This produces cross join behavior. We then use a WHERE clause to effectively inner join these table references.
It's commonly suggested to use a single <table reference>, for several good reasons, which can contain a <joined table>. The second UNION term below contains an example of a <joined table>.
A couple of advantages are ease of identifying (and not accidentally forgetting) the corresponding <join condition> and easier to change between join types.
CREATE VIEW find_all(person,supervisor,depth) AS
WITH RECURSIVE cte1 AS (
SELECT person, supervisor, 0 AS depth
FROM employee AS e
WHERE e.person = 'Bob'
UNION ALL
SELECT in1.person, out1.supervisor, in1.depth+1
FROM cte1 AS in1
JOIN employee AS out1
ON in1.supervisor = out1.person
WHERE in1.depth <= 100
)
SELECT * FROM cte1
;
In this case, the depth logic is not really part of the <join condition>. It's really a subsequent filter of the rows produced by the join.
The difference is seen when, for example, we have a left outer join. We don't want that logic to just generate more nulls for the right table columns. We want that logic to produce no further rows when depth is exceeded.

Sql multiple max value in different dimensions

I am trying to find a max number of the value but the thing is my code choose only max value even though values are equal so how can I find max value and if there are same make a two rows
Here is the example:
(Ps; data is fake just for example question please let me if something is wrong)
select class.name as className,
school.schoolName as schoolName,
count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, schoolName
order by schoolName
and result is:
className | SchoolNAme | TotalNum
-----------|------------|----------
Math | A | 9
Bio | A | 2
History | A | 7
Music | A | 9
Math | B | 22
English | B | 8
Music | B | 1
History | B | 2
Geo | B | 2
Bio | B | 3
Math | C | 15
Geo | C | 2
and I found each school max number like this:
select ClassName,
SchoolName,
max(totalNumber) as Total
from
(
select class.name as className,
school.schoolName as schoolName,
count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, schoolName
order by schoolName
) t1
group by schoolName
and this is the result I have:
className | SchoolNAme | Total
-----------|------------|----------
Math | A | 9
Math | B | 22
Math | C | 15
but the problem I have as we can see two total number is 9 for school A
so how can I show all max number if they are equal.
this is what I want;
className | SchoolNAme | Total
-----------|------------|----------
Math | A | 9
Bio | A | 9
Math | B | 22
Math | C | 15
Thanks
Max will show 1 row per group, as you are grouping by school name and there are 3 schools you will get 3 groups and therefore the 3 rows returned.
School A class Bio appears from your data to have only 2 rows (count counts the rows per group) so Bio A 9 will never be returned, so the result wanted will never be obtainable. I guess you really expect Music A 9 instead along with Math A 9.
The School table is actually redundant in this case (based upon interpretation of the join and the ensuing data i.e. count is count the number of joined rows and thus the number of class rows).
As such i believe the following produces the results that you are after :-
WITH
cte1 AS
(SELECT name,schoolName, count() AS cnt FROM class
GROUP BY name,schoolName),
cte2 AS
(SELECT name, schoolName, max(cnt) AS m FROM cte1
GROUP BY schoolname)
-- SELECT * FROM cte2
SELECT cte1.name, cte1.schoolName, cte1.cnt FROM cte1
WHERE cte1.cnt = (SELECT m FROM cte2 WHERE cte2.schoolname = cte1.schoolname)
ORDER BY schoolName, name
;
The above was tested using the following :-
DROP TABLE IF EXISTS school;
DROP TABLE IF EXISTS class;
CREATE TABLE IF NOT EXISTS school (schoolName TEXT, quantity INTEGER);
CREATE TABLE IF NOT EXISTS class (name TEXT, schoolName);
INSERT INTO school VALUES ('A',1),('B',1),('C',1);
INSERT INTO class VALUES
('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),('Math','A'),
('Bio','A'),('Bio','A'),
('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),('History','A'),
('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),('Music','A'),
('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),('Math','B'),
('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),('English','B'),
('Music','B'),
('History','B'),('History','B'),
('Geo','B'),('Geo','B'),
('Bio','B'),('Bio','B'),('Bio','B'),
('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),('Math','C'),
('Geo','C'),('Geo','C')
;
select class.name as className, school.schoolName as schoolName, count() as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname, school.schoolName
order by schoolName, totalNumber DESC
;
select ClassName, SchoolName, max(totalNumber) as Total
from
(
select class.name as className, school.schoolName as schoolName, count(school.quantity) as totalNumber from class
join School on school.schoolName = class.schoolName
group by classname,school.schoolname
order by schoolName
)
group by schoolName;
WITH
cte1 AS
(SELECT name,schoolName, count() AS cnt FROM class
GROUP BY name,schoolName),
cte2 AS
(SELECT name, schoolName, max(cnt) AS m FROM cte1
GROUP BY schoolname)
-- SELECT * FROM cte2
SELECT cte1.name, cte1.schoolName, cte1.cnt FROM cte1
WHERE cte1.cnt = (SELECT m FROM cte2 WHERE cte2.schoolname = cte1.schoolname)
ORDER BY schoolName, name
;
Results
First Query
The first query is your query, the results match the results in the question :-
Second Query
Again this matches the unwanted/wrong results from your question :-
Third Query
This I believe matches what you require :-

SQL - select rows that have the same value in two columns

The solution to the topic is evading me.
I have a table looking like (beyond other fields that have nothing to do with my question):
NAME,CARDNUMBER,MEMBERTYPE
Now, I want a view that shows rows where the cardnumber AND membertype is identical. Both of these fields are integers. Name is VARCHAR. Name is not unique, and duplicate cardnumber, membertype should show for the same name, as well.
I.e. if the following was the table:
JOHN | 324 | 2
PETER | 642 | 1
MARK | 324 | 2
DIANNA | 753 | 2
SPIDERMAN | 642 | 1
JAMIE FOXX | 235 | 6
I would want:
JOHN | 324 | 2
MARK | 324 | 2
PETER | 642 | 1
SPIDERMAN | 642 | 1
this could just be sorted by cardnumber to make it useful to humans.
What's the most efficient way of doing this?
What's the most efficient way of doing this?
I believe a JOIN will be more efficient than EXISTS
SELECT t1.* FROM myTable t1
JOIN (
SELECT cardnumber, membertype
FROM myTable
GROUP BY cardnumber, membertype
HAVING COUNT(*) > 1
) t2 ON t1.cardnumber = t2.cardnumber AND t1.membertype = t2.membertype
Query plan: http://www.sqlfiddle.com/#!2/0abe3/1
You can use exists for this:
select *
from yourtable y
where exists (
select 1
from yourtable y2
where y.name <> y2.name
and y.cardnumber = y2.cardnumber
and y.membertype = y2.membertype)
SQL Fiddle Demo
Since you mentioned names can be duplicated, and that a duplicate name still means is a different person and should show up in the result set, we need to use a GROUP BY HAVING COUNT(*) > 1 in order to truly detect dupes. Then join this back to the main table to get your full result list.
Also since from your comments, it sounds like you are wrapping this into a view, you'll need to separate out the subquery.
CREATE VIEW DUP_CARDS
AS
SELECT CARDNUMBER, MEMBERTYPE
FROM mytable t2
GROUP BY CARDNUMBER, MEMBERTYPE
HAVING COUNT(*) > 1
CREATE VIEW DUP_ROWS
AS
SELECT t1.*
FROM mytable AS t1
INNER JOIN DUP_CARDS AS DUP
ON (T1.CARDNUMBER = DUP.CARDNUMBER AND T1.MEMBERTYPE = DUP.MEMBERTYPE )
SQL Fiddle Example
If you just need to know the valuepairs of the 3 fields that are not unique then you could simply do:
SELECT concat(NAME, "|", CARDNUMBER, "|", MEMBERTYPE) AS myIdentifier,
COUNT(*) AS count
FROM myTable
GROUP BY myIdentifier
HAVING count > 1
This will give you all the different pairs of NAME, CARDNUMBER and MEMBERTYPE that are used more than once with a count (how many times they are duplicated). This doesnt give you back the entries, you would have to do that in a second step.

Count number of distinct rows for multiple values

Let's consider this table specifing how many times a person bought a property.
+--------+----------+
| user | property |
+--------+----------+
| john | car |
| john | car |
| john | house |
| peter | car |
| peter | car |
| amanda | house |
| amanda | house |
+--------+----------+
I need to know how many times a car was bought once, how many times a house was bought once, etc. Something like this:
+----------+---+---+
| property | 1 | 2 |
+----------+---+---+
| cars | 4 | 2 |
| house | 3 | 1 |
+----------+---+---+
How many times a car was bought? Four, two for peter and two for john.
How many times a car was bought twice? Two, for the same guys.
How many times a house was bought? Three, two for amanda and once for john.
How many times a house was bought twice? Only once, for amanda
Is this possible to do this only using SQL queries?
I don't care about performance or hackish ways.
There are more than two frequencies.
There's a fixed set of time a person can buy a property (5) so it's not problem to specify the columns manually in the query. I mean there's not problem doing something like:
SELECT /* ... */ AS 1, /* ... */ AS 2, /* ... */, AS 3 /* ... */
SELECT DISTINCT #pr := prop,
(SELECT COUNT(1) FROM tbl WHERE prop = #pr LIMIT 1),
(SELECT COUNT(1) FROM
(SELECT *, COUNT(*) cnt
FROM tbl
GROUP BY usr, prop
HAVING cnt = 2) as tmp
WHERE `tmp`.prop = #pr LIMIT 1)
FROM tbl;
Yes, it is not the best method; but hey, you get the answers as desired.
Also, it'll generate the results for any kind of property in your table.
The fiddle link lies here.
P.S.: 60 tries O_O
I am here since you posted the question. Good one...
Here is a way to do it exactly as you asked for, with just groups and counts.
The trick is that I concatenate the user and property columns to produce a unique "id" for each, if we could call it that. It should work independently of the count of purchases.
SELECT C.`property`, COUNT(C.`property`), D.`pcount` from `purchases` C
LEFT JOIN(
SELECT A.`property`, B.`pcount` FROM `purchases` A
LEFT JOIN (
SELECT `property`,
CONCAT(`user`, `property`) as conc,
COUNT(CONCAT(`user`, `property`)) as pcount
FROM `purchases` GROUP BY CONCAT(`user`, `property`)
) B
ON A.`property` = B.`property`
GROUP BY B.pcount
) D
ON C.`property` = D.`property`
GROUP BY C.`property`
SQL Fiddle
MySQL 5.5.30 Schema Setup:
CREATE TABLE Table1
(`user` varchar(6), `property` varchar(5))
;
INSERT INTO Table1
(`user`, `property`)
VALUES
('john', 'car'),
('john', 'car'),
('john', 'house'),
('peter', 'car'),
('peter', 'car'),
('amanda', 'house'),
('amanda', 'house')
;
Query 1:
select t.property, t.total, c1.cnt as c1, c2.cnt as c2, c3.cnt as c3
from
(select
t.property ,
count(t.property) as total
from Table1 t
group by t.property
) as t
left join (
select property, count(*) as cnt
from (
select
property, user, count(*) as cnt
from table1
group by property, user
having count(*) = 1
) as i1
group by property
) as c1 on t.property = c1.property
left join (
select property, count(*) as cnt
from (
select
property, user, count(*) as cnt
from table1
group by property, user
having count(*) = 2
) as i2
group by property
) as c2 on t.property = c2.property
left join (
select property, count(*) as cnt
from (
select
property, user, count(*) as cnt
from table1
group by property, user
having count(*) = 3
) as i3
group by property
) as c3 on t.property = c3.property
Results:
| PROPERTY | TOTAL | C1 | C2 | C3 |
-------------------------------------------
| car | 4 | (null) | 2 | (null) |
| house | 3 | 1 | 1 | (null) |
You may try following.
SELECT COUNT(TABLE1.PROPERTY) AS COUNT, PROPERTY.USER FROM TABLE1
INNER JOIN (SELECT DISTINCT PROPERTY, USER FROM TABLE1) AS PROPERTY
ON PROPERTY.PROPERTY = TABLE1.PROPERTY
AND PROPERTY.USER = TABLE1.USER
GROUP BY TABLE1.USER, PROPERTY.PROPERTRY
tested similar in MySQL
try this
SELECT property , count(property) as bought_total , count(distinct(user)) bought_per_user
FROM Table1
GROUP BY property
the output will be like that
PROPERTY | BOUGHT_TOTAL | BOUGHT_PER_USER
________________________________________________________
car | 4 | 2
house | 3 | 2
DEMO SQL FIDDLE HERE
You should be able to do this with sub-selects.
SELECT property, user, COUNT(*) FROM purchases GROUP BY property, user;
will return you the full set of grouped data that you want. You then need to look at the different frequencies:
SELECT property, freq, COUNT(*) FROM (SELECT property, user, COUNT(*) freq FROM purchases GROUP BY property, user) AS foo GROUP BY property, freq;
It's not quite in the format that you illustrated but it returns the data
I hope this can help u.....let us create one table first:
create table prop(user varchar(max),property varchar(max))
insert into prop values('john','car'),insert into prop values('john','car'),
insert into prop values('john','house'),insert into prop values('peter','car'),
insert into prop values('peter','car'),insert into prop values('amanda','house'),
insert into prop values('amanda','house')
1)how many times car was bought?
ANS: select count(property) from prop where property = 'car'
(4)
2)How many times a car was bought twice?
ANS: select user,COUNT(property) from prop where property = 'car' group by user
having COUNT(property) = 2
2-john
2-peter
3)How many times a house was bought?
ANS: select COUNT(property) from prop where property = 'house'
(3)
4)How many times a house was bought twice?
ANS: select user,COUNT(property) from prop where property='house' group by user
having COUNT(property)< =2
2-amanda
1-john

SQL Query - Not in a set of already in-use items

I am trying to select jobs that are not currently assigned to a user.
Users table: id | name
Jobs: id | name
Assigned: id | user_id | job_id | date_assigned
I want to select all the jobs that are not currently taken. Example:
Users:
id | name
--------------
1 | Chris
2 | Steve
Jobs
id | name
---------------
1 | Sweep
2 | Skids
3 | Mop
Assigned
id | user_id | job_id | date_assigned
-------------------------------------------------
1 | 1 | 1 | 2012-01-01
2 | 1 | 2 | 2012-01-02
3 | 2 | 3 | 2012-01-05
No two people can be assigned the same job. So the query would return
[1, Sweep]
Since no one is working on it since Chris got moved to Skids a day later.
So far:
SELECT
*
FROM
jobs
WHERE
id
NOT IN
(
SELECT
DISTINCT(job_id)
FROM
assigned
ORDER BY
date_assigned
DESC
)
However, this query returns NULL on the same data set. Not addressing that the sweep job is now open because it is not currently being worked on.
SELECT a.*
FROM jobs a
LEFT JOIN
(
SELECT a.job_id
FROM assigned a
INNER JOIN
(
SELECT MAX(id) AS maxid
FROM assigned
GROUP BY user_id
) b ON a.id = b.maxid
) b ON a.id = b.job_id
WHERE b.job_id IS NULL
This gets the most recent job per user. Once we have a list of those jobs, we select all jobs that aren't on that list.
You can try this variant:
select * from jobs
where id not in (
select job_id from (
select user_id, job_id, max(date_assigned)
from assigned
group by user_id, job_id));
I think you might want:
SELECT *
FROM jobs
WHERE id NOT IN (SELECT job_id
from assigned
where user_id is not null
)
This assumes that re-assigning someone changes the user id on the original assignment. Does this happen? By the way, I also simplified the subquery.
First you need to be looking at a list of only current job assignments. Ordering isn't enough. The way you have it set up, you need a distinct subset of job assignments from Assigned that are the most recent assignments.
So you want a grouping subquery something like
select job_id, user_id, max(date_assigned) last_assigned from assigned group by job_id, user_id
Put it all together and you get
select id, name from jobs
where id not in (
select job_id as id from (
select job_id, user_id, max(date_assigned) last_assigned from assigned
group by job_id, user_id
)
)
As an extra feature, you could pass up the value of "last_assigned" and it would tell you how long a job has been idle for.