This must be quite easy, but I cannot find a good solution myself.
I have two tables:
file
+----+--------+
| id | system |
+----+--------+
| 1 | AA |
| 2 | AA |
| 3 | BB |
| 4 | AA |
+----+--------+
feature
+----+---------+------+
| id | file_id | name |
+----+---------+------+
| 1 | 1 | A |
| 1 | 2 | A |
| 1 | 2 | B |
| 1 | 3 | B |
| 1 | 3 | C |
| 1 | 4 | A |
| 1 | 4 | B |
| 1 | 4 | C |
+----+---------+------+
and I want to count how many times a feature was added to files with a specific system. For that, I have the following query:
SELECT f.name, COUNT(*) AS nr
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN (3157,3168,3192)
GROUP BY f.name
which gives the desired output:
+------+----+
| name | nr |
+------+----+
| A | 3 |
| B | 2 |
| C | 1 |
+------+----+
Now I also want to know the total amount of files with the same specific system. A simple separate query would be:
SELECT COUNT(*) FROM file WHERE system = 'AA' AND id NOT IN (3157,3168,3192)
I've added the extra AND id NOT IN (which is irrelevant for this example) just to show that the actual query is much more complex. If I use a separate query to get the total I would have to duplicate that complexity, so I want to avoid that by returning the total from the same query.
So how can I count the number of files in the first query?
Desired output:
+------+----+-------+
| name | nr | total |
+------+----+-------+
| A | 3 | 3 |
| B | 2 | 3 |
| C | 1 | 3 |
+------+----+-------+
Here is one way using Sub-query
SELECT f.NAME,
Count(*) AS nr,
(SELECT Count(*)
FROM FILE
WHERE system = 'AA'
AND id NOT IN ( 3157, 3168, 3192 )) as Total
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN ( 3157, 3168, 3192 )
GROUP BY f.NAME
Or Use CROSS JOIN
SELECT *
FROM (SELECT f.NAME,
Count(*) AS nr,
FROM dossier d
JOIN feature f
ON f.file_id = d.id
WHERE d.system = 'AA'
AND d.id NOT IN ( 3157, 3168, 3192 )
GROUP BY f.NAME) A
CROSS JOIN (SELECT Count(*) AS Total
FROM FILE
WHERE system = 'AA'
AND id NOT IN ( 3157, 3168, 3192 )) B
Related
I have this data in a table called PROD
| Project | Position | Status |
|---------|----------|--------|
| 1 | 1 | A |
| 1 | 2 | A |
| 2 | 1 | A |
| 2 | 2 | B |
| 3 | 1 | B |
| 3 | 2 | B |
| 4 | 1 | A |
| 4 | 2 | A |
I'm trying to get all the Projects that has at least one Position with Status = B.
| Project | Position | Status |
|---------|----------|--------|
| 2 | 1 | A |
| 2 | 2 | B |
| 3 | 1 | B |
| 3 | 2 | B |
I've tried using a JOIN like this:
SELECT * FROM PROD A JOIN PROD B ON A.PROD-Project = B.PROD-Project WHERE B.PROD-Status = 'B'
This give me an empty response.
With EXISTS:
SELECT p.* FROM PROD p
WHERE EXISTS (
SELECT 1 FROM PROD
WHERE Project = p.Project AND Status = 'B'
)
or with IN:
SELECT * FROM PROD
WHERE Project IN (SELECT Project FROM PROD WHERE Status = 'B')
If you want a solution with JOIN:
SELECT DISTINCT p.*
FROM PROD p JOIN PROD pp
ON pp.Project = p.Project
WHERE pp.Status = 'B'
See the demo.
Results:
> Project | Position | Status
> ------: | -------: | :-----
> 2 | 1 | A
> 2 | 2 | B
> 3 | 1 | B
> 3 | 2 | B
You could try using a join wit the subquery
select * from PROD
INNER JOIN (
select distinct project
from PROD
where status ='B';
) t on t.project = PROD.project
I'm trying to get all the Projects that has at least one Position with Status = B.
No need for a JOIN, just do:
SELECT DISTINCT PROD.Project WHERE PROD.Status='B'
i have a complaint table
|------------------------|
| cid | desc |
|------------------------|
| 1 | faulty |
| 2 | broken |
| 3 | spoiled |
|------------------------|
and an assignment table
|------------------------------------|
| aid | cid | empid |
|------------------------------------|
| 1 | 1 | 1 |
| 2 | 1 | 5 |
| 3 | 2 | 2 |
| 4 | 2 | |
| 5 | 3 | 2 |
| 6 | 3 | 7 |
|------------------------------------|
each complaint can be assigned to atmost two employees
i need to display a list in the below format
|---------------------------------------------------|
| cid | desc | emp1id | emp2id |
|------------------------|--------------------------|
| 1 | faulty | 1 | 5 |
| 2 | broken | 2 | |
| 3 | spoiled | 2 | 7 |
|------------------------|--------------------------|
i wrote the query like this
select c.cid, c.desc, a1.empid as emp1id, a2.empid as emp2id
from complaint c
left join (
select aid, cid, empid
from assignment aa
where aa.cid = c.cid
limit 0,1
) as a1 on a1.cid = c.cid
left join (
select aid, cid, empid
from assignment ab
where ab.cid = c.cid
limit 1,1
) as a2 on a2.cid = c.cid
but it is not working, i am getting error for c.cid in sub queries. how to do?
This might work? (I don't have mySql installed)
select c.cid,
c.desc,
(
select aid, cid, empid
from assignment aa
where aa.cid = c.cid
limit 0,1
) as emp1id,
(
select aid, cid, empid
from assignment ab
where ab.cid = c.cid
limit 1,1
) as emp2id
from complaint c
I'm banging my head with some SQL query and pretty much the logic behind it.
Let's assume we have these tables:
Table hotels
+----+---------+
| id | name |
+----+---------+
| 1 | Hotel A |
+----+---------+
Table hotel_rooms
+----+----------+-----------+
| id | hotel_id | room_type |
+----+----------+-----------+
| 1 | 1 | dbl | <- can be used as A,B,C,D,E,F
| 2 | 1 | dbl | <- can be used as B,C,D,E,F
| 3 | 1 | sng | <- can be used as A
| 4 | 1 | trp | <- can be used as D,E,F
+----+----------+-----------+
Table hotel_room_usages
+----+---------+-------+
| id | room_id | usage |
+----+---------+-------+
| 1 | 1 | B |
| 2 | 1 | C |
| 3 | 1 | A |
| 4 | 1 | D |
| 5 | 1 | E |
| 6 | 1 | F |
| 7 | 2 | B |
| 8 | 2 | C |
| 9 | 2 | D |
| 10 | 2 | E |
| 11 | 2 | F |
| 12 | 3 | A |
| 13 | 4 | D |
| 14 | 4 | E |
| 15 | 4 | F |
+----+---------+-------+
If I search for 2 rooms with usage A or 3 rooms with usage D as separate queries the result should be Hotel A with the corresponding IDs of the rooms.
The problem is if I search for 2 rooms with usage A and 3 rooms with usage D at the same time it returns also the hotel A because it doesn't count that some rooms can be used as A and D.
The rooms should be unique /total of 5/. The current example should not return a result because there are total of 4 rooms in the hotel.
does this help?
-- two rooms with usage a
select id from hotel_room_usages where usage = 'a'
-- three rooms with usage d
select id from hotel_room_usages where usage = 'd'
-- count of rooms with either
select count(distinct(room_id)) from hotel_room_usages where usage in ('a','d')
SELECT h.name AS hotel_name
, q.*
FROM
(
SELECT r.hotel_id
, COUNT(DISTINCT CASE WHEN ruA.room_id IS NOT NULL AND ruD.room_id IS NULL THEN ruA.room_id END) AS TotalRoomsOnlyA
, COUNT(DISTINCT CASE WHEN ruD.room_id IS NOT NULL AND ruA.room_id IS NULL THEN ruD.room_id END) AS TotalRoomsOnlyD
, COUNT(DISTINCT CASE WHEN ruA.room_id IS NOT NULL AND ruD.room_id IS NOT NULL THEN r.id END) AS TotalRoomsAandD
, COUNT(DISTINCT r.id) AS TotalRoomsAorD
FROM hotel_rooms AS r
LEFT JOIN hotel_room_usages AS ruA ON (ruA.room_id = r.id AND ruA.usage = 'A')
LEFT JOIN hotel_room_usages AS ruD ON (ruD.room_id = r.id AND ruD.usage = 'D')
WHERE (ruA.room_id IS NOT NULL OR ruD.room_id IS NOT NULL)
GROUP BY r.hotel_id
) q
JOIN hotels AS h ON (h.id = q.hotel_id)
CROSS JOIN (SELECT 2 AS a, 3 AS d) AS n
WHERE TotalRoomsAorD >= (a+d)
AND (
((TotalRoomsOnlyA + TotalRoomsAandD) >= a AND TotalRoomsOnlyD >= d) OR
(TotalRoomsOnlyA >= d AND (TotalRoomsOnlyD + TotalRoomsAandD) >= d) OR
((TotalRoomsOnlyA + TotalRoomsAandD/2) >= a AND (TotalRoomsOnlyD + TotalRoomsAandD/2) >= d)
)
ORDER BY h.name;
Test on db<>fiddle here
I have requirement of getting intersection of some results in mysql DB. But after googling came to know that there is no mysql intersect keyword available . Following are my sample tables.
gene table
+------+--------+---------+
| id | symbol | test_id |
+------+--------+---------+
| -1 | A | -1 |
| 8 | A | 3 |
| 9 | G | 3 |
| -1 | A | -1 |
| -2 | B | -1 |
| -3 | C | -1 |
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | C | 1 |
| 4 | B | 2 |
| 5 | C | 2 |
| 6 | D | 2 |
| 7 | E | 2 |
| 8 | A | 3 |
| 9 | G | 3 |
| 10 | F | 3 |
| 11 | C | 3 |
| 12 | C | 4 |
| 13 | G | 4 |
| 14 | F | 4 |
| 15 | M | 4 |
| 16 | N | 4 |
+------+--------+---------+
test table
+------+-------+
| id | name |
+------+-------+
| -1 | test0 |
| 3 | test3 |
| -1 | test0 |
| 1 | test1 |
| 2 | test2 |
| 3 | test3 |
| 4 | test4 |
+------+-------+
Now I want to formulate a query which will give me the tests which are common for provided genes. e.g. I will provide gene A, B, C and I should get the following result:
id name id symbol
---------------------------
-1 | test0 | -1 | A
-1 | test0 | -2 | B
-1 | test0 | -3 | C
1 | test1 | 1 | A
1 | test1 | 2 | B
1 | test1 | 3 | C
I just tried to form a query by following way but didn't work, getting empty resultset and if I use 'or' in where clause getting tests for all genes in where clause.
select distinct t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where g.symbol = 'A' and g.symbol='B' and g.symbol='C';
Please help me to construct the query.
The trick is to filter the records with your criteria, then group by test.id to check that it matches all the criteria:
SELECT t.id
FROM tests AS t
INNER JOIN genes AS g
ON t.id = g.test_id
WHERE g.symbol in ('A','B','C')
GROUP BY t.id
HAVING COUNT(DISTINCT g.symbol) = 3;
So the key line is here:
HAVING COUNT(DISTINCT g.symbol) = 3;
If, like test 2, there is only a match on 'B', then the count will return 1 and the test will be excluded. The number of items you are checking for must match the number in the HAVING clause.
If you then need to get the full data out, you just need to join back to your table:
SELECT t.id, t.name, g.id, g.symbol
FROM genes AS g
INNER JOIN
( SELECT t.id, t.name
FROM tests AS t
INNER JOIN genes AS g
ON t.id = g.test_id
WHERE g.symbol in ('A','B','C')
GROUP BY t.id, t.name
HAVING COUNT(DISTINCT g.symbol) = 3
) t
ON t.id = g.test_id;
Example on SQL Fiddle
Change those AND conditions to OR condition like below cause at any point in time g.symbol can hold only one value and not multiple value. that's why you are getting empty result set.
select t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where (g.symbol = 'A' or g.symbol='B' or g.symbol='C')
and g.test_id = 1;
(OR) use a IN operator like
select t.id, t.name, g.id, g.symbol from tests t
join genes g on t.id = g.test_id
where g.symbol in ('A','B','C')
and g.test_id = 1;
I have four tables and like to know how many locations and downloads a certain name has.
names and locations are connected via the names_locations table
Here are my tables:
Table "names"
ID | name
=========
1 | foo
2 | bar
3 | zoo
4 | luu
Table "locations"
ID | location
=============
1 | Hamburg
2 | New York
3 | Singapore
4 | Tokio
Table "names_locations"
ID | location_id | name_id
==========================
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
4 | 3 | 3
5 | 1 | 2
Table "downloads"
ID | name_id | timestamp
=========================
1 | 1 | 1394041682
2 | 4 | 1394041356
3 | 1 | 1394041573
4 | 3 | 1394041981
5 | 1 | 1394041683
Result should be:
ID | name | locations | downloads
=================================
1 | foo | 1 | 3
2 | bar | 3 | 0
3 | zoo | 1 | 1
4 | luu | 0 | 1
Here's my attempt (without the downloads column):
SELECT names.*,
Count(names_locations.location_id) AS location
FROM names
LEFT JOIN names_locations
ON names.ID = names_locations.name_id
GROUP BY names.ID
I think this would work.
SELECT n.id,
n.name,
COUNT(DISTINCT l.id) AS locations,
COUNT(DISTINCT d.id) AS downloads
FROM names n LEFT JOIN names_location nl
ON n.id = nl.name_id
LEFT JOIN downloads dl
ON n.id = dl.name_id
LEFT JOIN locations l
ON l.id = nl.location_id
GROUP BY n.id, n.name
All of those seem to work. here's another one.
SELECT
a.ID,
a.name,
COUNT(c.location) AS locations,
COUNT(d.timestamp) AS downloads
FROM names AS a
LEFT JOIN names_locations AS b on a.ID=b.name_id
LEFT JOIN locations AS c ON b.location_id=c.ID
LEFT JOIN downloads AS d ON a.ID=d.name_id
GROUP BY a.name
SELECT
t.id,t.n AS name,
count(location_id) AS locations,
t.downloads
FROM names_location
RIGHT JOIN
(SELECT
names.id AS id,
names.name AS n,
count(timestamp) AS downloads
FROM names
LEFT JOIN downloads ON names.id = downloads.name_id
GROUP BY id) AS t
ON t.id = names_location.name_id
GROUP BY t.id
Output:
+------+------+-----------+-----------+
| id | name | locations | downloads |
+------+------+-----------+-----------+
| 1 | foo | 1 | 3 |
| 2 | bar | 3 | 0 |
| 3 | zoo | 1 | 1 |
| 4 | luu | 0 | 1 |
+------+------+-----------+-----------+
4 rows in set (0.00 sec)