Query equivalent to two group by's without a subquery? - mysql

I'm trying to run a report on User ACL's. We use MYSQL and my we're prohibited from using subqueries for performance reasons. The goal is to turn this:
--------------------------------
| userName | folderID | roleID |
--------------------------------
| gronk | 1 | 1 |
| gronk | 2 | 2 |
| gronk | 4 | 2 |
| tbrady | 1 | 2 |
| jedelman | 1 | 1 |
| jedelman | 2 | 1 |
| mbutler | 1 | 2 |
| mbutler | 2 | 2 |
| bill | 1 | 3 |
| bill | 2 | 3 |
| bill | 3 | 3 |
| bill | 4 | 3 |
--------------------------------
Into this:
------------------------
| Lowest Role | Number |
------------------------
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
------------------------
I can see how to do it with a subquery. The inner query would do a group by on userName with a min(roleID). Then the outer query would do a group by on the lowest role and count(*). But I can't see how to do it without a subquery.
Also, if it helps I created a SQL Fiddle that has the data above.

I found a solution using a left join:
select UFM.roleID, count(distinct UFM.userName)
from UserFolderMembership UFM
left join UserFolderMembership UFM2 on
UFM.userName = UFM2.userName and
UFM.roleID > UFM2.roleID
where
UFM2.userName is null
group by
UFM.roleID

Related

How do I query a three-level structure in two joined tables?

There are two tables,
Table A has a three-level structure that looks like
| id | name | level | up_level_id |
| :------- | :-------: | :------: | ----------:|
| 1 | lv1_name1 | 1 | null |
| 2 | lv1_name2 | 1 | null |
| 3 | lv2_name1 | 2 | 1 |
| 4 | lv2_name2 | 2 | 2 |
| 5 | lv3_name1 | 3 | 3 |
| 6 | lv3_name2 | 3 | 3 |
| 7 | lv3_name3 | 3 | 4 |
| 8 | lv3_name4 | 3 | 4 |
Table B looks like
| amount | org_id |
| -------- | -------- |
| 12,000 | 5 |
| 15,000 | 6 |
| 20,000 | 7 |
| 18,000 | 8 |
Table A and Table B can be joined on A.id=B.org_id, but they are all at the level-3 of Table A(Only level-3 has their amount)
I want to query the top-level name,level-1 name, and the summary amount that looks like
| sum_amount | top_lvl_name |
| -------- | -------- |
| 27,000 | lv1_name1 |
| 38,000 | lv1_name2 |
For Testing, I have already accomplished the query of the level-1 name from the level-3 id in TableA
The SQL is as follows
SELECT name
FROM TableA
WHERE id IN (
SELECT up_level_id
FROM Table A
WHERE id IN (
SELECT up_level_id
FROM Table A
WHERE id=5) --query the id:5's top-level name
);
But when I join these two tables as follows, it goes wrong
SELECT sum(amount) AS sum_amount, name AS top_lvl_name
FROM TableA, TableB
WHERE id = org_id
AND id IN (
SELECT up_level_id
FROM TableA
WHERE id IN (
SELECT up_level_id
FROM TableA
WHERE TableA.id IN(
SELECT org_id
FROM TABLEB
)
)
);
I get nothing as above
What can I do to make this query to be correct?
Thanks for everyone's answer and comment.
Finally, I find it very difficult to query the result as I wish. So, I've come up with a shortcut——create a new table that a three-level structure recorded horizontally, which looks like
| lv1_id | lv2_name | lv2_id | lv2_name | lv3_id | lv3_name |
| :------- | :-------: | :------: | :----------:| :------: | :----------:|
| 1 | lv1_name1 | 3 | lv2_name1 | 5 | lv3_name1 |
| 1 | lv1_name1 | 3 | lv2_name1 | 6 | lv3_name2 |
| 2 | lv1_name2 | 4 | lv2_name1 | 7 | lv3_name3 |
| 2 | lv1_name2 | 4 | lv2_name1 | 8 | lv3_name4 |
As above,I can easily connect two tables

How do I join 2 tables to a third one which contains the primary key?

I have 4 tables: the first is the client table, which has customer info, and client_id as an auto-increment primary key.
The second and third are identical in structure: they are used to track attendance to 2 different therapy programs. They each have a primary key, and a client_id column to track the client. One of the fields contains units, which I want to sum.
The last table contains the therapists' info.
Basically I want to extract total amount of units for each client from the two attendance tables.
I have tried LEFT JOINS to no avail. I also tried a UNION ALL, but couldn't get it to sum the units.
This is how the tables look:
client:
+---------------------------------------+
| client_id | f_name | l_name | th_id |
|-----------|----------|--------|-------|
| 1 | sherlock | holmes | 1 |
| 2 | john | watson | 4 |
| 3 | hercule | poirot | 3 |
| 4 | jane | marple | 2 |
+---------------------------------------+
therapist:
+--------------------------+
| th_id | f_name | l_name |
|-------|---------|--------|
| 1 | james | kirk |
| 2 | mr | spock |
| 3 | bones | mccoy |
| 4 | nyota | uhura |
+--------------------------+
attendance it:
+-------------------------------+
| it_id | client_id | units |
|-----------|-----------|-------|
| 1 | 1 | 4 |
| 2 | 1 | 4 |
| 3 | 1 | 0 |
| 4 | 1 | 2 |
| 5 | 4 | 0 |
| 6 | 4 | 4 |
| 7 | 4 | 0 |
| 8 | 4 | 2 |
+-------------------------------+
attendance psr:
+-------------------------------+
| it_id | client_id | units |
|-----------|-----------|-------|
| 1 | 1 | 16 |
| 2 | 1 | 16 |
| 3 | 1 | 0 |
| 4 | 1 | 12 |
| 5 | 4 | 0 |
| 6 | 4 | 14 |
| 7 | 4 | 8 |
| 8 | 4 | 10 |
+-------------------------------+
The result should look like this:
+------------------------------------------------------------+
| client_id | total_units_it | total_units_psr | therapist |
|-----------|----------------|-----------------|-------------|
| 1 | 10 | 44 | james kirk |
| 4 | 6 | 32 | mr spock |
+------------------------------------------------------------+
Please excuse the primitive representations, and please don't ask why the tables are designed like that... ;-) Also, I obviously ignored many other fields which are not relevant to the question, such as dates, etc.
Any advice would be appreciated.
Thanks!
You can't use join or you will create Cartesian product and duplicate the rows.
Instead you do a subquery:
SELECT c.*
, (SELECT SUM(units) FROM attendance_it a WHERE a.client_id = c.client_id ) as total_units_it
, (SELECT SUM(units) FROM attendance psr a WHERE a.client_id = c.client_id ) as total_units_psr
, t.*
FROM client c
JOIN therapist t
ON c.th_id = t.th_id
use group by client_id to get the sum of each client. and no need to use join as you have already the ids in column.

How to get count of combinations from database?

How to get count of combinations from database?
I have to database tables and want to get the count of combinations. Does anybody know how to put this in a database query, therefore I haven't a db request for each trip?
Trips
| ID | Driver | Date |
|----|--------|------------|
| 1 | A | 2015-12-15 |
| 2 | A | 2015-12-16 |
| 3 | B | 2015-12-17 |
| 4 | A | 2015-12-18 |
| 5 | A | 2015-12-19 |
Passengers
| ID | PassengerID | TripID |
|----|-------------|--------|
| 1 | B | 1 |
| 2 | C | 1 |
| 3 | D | 1 |
| 4 | B | 2 |
| 5 | D | 2 |
| 6 | A | 3 |
| 7 | B | 4 |
| 8 | D | 4 |
| 9 | B | 5 |
| 10 | C | 5 |
Expected result
| Driver | B-C-D | B-D | A | B-C |
|--------|-------|-----|---|-----|
| A | 1 | 2 | - | 1 |
| B | - | - | 1 | - |
Alternative
| Driver | Passengers | Count |
|--------|------------|-------|
| A | B-C-D | 1 |
| A | B-D | 2 |
| A | B-C | 1 |
| B | A | 1 |
Has anybody an idea?
Thanks a lot!
Try this:
SELECT Driver, Passengers, COUNT(*) AS `Count`
FROM (
SELECT t.ID, t.Driver,
GROUP_CONCAT(p.PassengerID
ORDER BY p.PassengerID
SEPARATOR '-') AS Passengers
FROM Trips AS t
INNER JOIN Passengers AS p ON t.ID = p.TripID
GROUP BY t.ID, t.Driver) AS t
GROUP BY Driver, Passengers
The above query will produce the alternative result set. The other result set can only be achieved using dynamic sql.
Demo here

MySQL complex ORDER BY issue

I have a complicated ordering issue in my query.
Raw, Unordered Data:
+------+--------+-----------+
| id | job_id | action_id |
+------+--------+-----------+
| 1 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 1 |
| 4 | 2 | 3 |
| 5 | 4 | 1 |
| 6 | 1 | 2 |
| 7 | 3 | 1 |
| 8 | 3 | 2 |
| 9 | 4 | 2 |
+------+--------+-----------+
Required Ordering:
+------+--------+-----------+
| id | job_id | action_id |
+------+--------+-----------+
| 7 | 3 | 1 |
| 8 | 3 | 2 |
| | | | * blank lines added for clarity,
| 5 | 4 | 1 | not desired in actual data
| 9 | 4 | 2 |
| | | |
| 3 | 1 | 1 |
| 6 | 1 | 2 |
| | | |
| 1 | 2 | 1 |
| 2 | 2 | 2 |
| 4 | 2 | 3 |
+------+--------+-----------+
The theory behind this ordering:
the largest id is the most recently added entry
the most recent id with action_id of 1
followed by the entries with ascending action_ids that have the same job_id
then the next most recent action_id of 1
ad infinitum
EDIT: I'm not able to add columns to the table in order to aid in sorting, as I've seen in some other solutions to ordering questions.
Any help is greatly appreciated!
My best shot is this:
SELECT * FROM tbl
ORDER BY FIND_IN_SET(job_id,
(SELECT GROUP_CONCAT(job_id ORDER BY ID DESC)
FROM tbl WHERE action_id = 1));
I didn't find a way to do it easily, What do you think of the following code :
select c.id, c.job_id, c.action_id
from (select a.id, a.job_id, a.action_id, min(b.id) as related_id
from myTable a
inner join myTable b
on a.job_id=b.job_id
group by a.job_id) c
group by c.id
order by c.related_id desc, c.action_id

Complex MySQL Query for Many-to-Many

I have searched and gone through the available topics similar to mine. But, failed to find that satisfies my requirements. Hence, posting it here.
I have four tables as follows:
"Organization" table:
--------------------------------
| org_id | org_name |
| 1 | A |
| 2 | B |
| 3 | C |
"Members" table:
----------------------------------------------
| mem_id | mem_name | org_id |
| 1 | mem1 | 1 |
| 2 | mem2 | 1 |
| 3 | mem3 | 2 |
| 4 | mem4 | 3 |
"Resource" table:
--------------------------------
| res_id | res_name |
| 1 | resource1 |
| 2 | resource2 |
| 3 | resource3 |
| 4 | resource4 |
"member-resource" table:
--------------------------------------------
| sl_no | mem_id | res_id |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 4 | 3 |
| 5 | 3 | 4 |
| 6 | 2 | 3 |
| 7 | 4 | 3 |
I want to find out the total number of distinct resources according to organizations. Expected output is as follows:
| org_name | Total Resources |
| A | 3 |
| B | 1 |
| C | 1 |
I also want to find out the total number of shared resources according to organizations. Expected output is as follows:
| org_name | Shared Resources |
| A | 1 |
| B | 0 |
| C | 1 |
Any help in this regard will highly be appreciated.
Regards.
It is much simpler than you think, particularly because you don't even need the resource table:
SELECT o.org_name, COUNT(DISTINCT mr.res_id) TotalResources
FROM member_resource mr
JOIN members m ON mr.mem_id = m.mem_id
JOIN organization o ON m.org_id = o.org_id
GROUP BY o.org_id
Output:
| ORG_NAME | TOTALRESOURCES |
|----------|----------------|
| A | 3 |
| B | 1 |
| C | 1 |
Fiddle here.
Try this query below.
SELECT org_name, COUNT(DISTINCT res_id)
FROM organization, members, member-resource
WHERE members.mem_id = member-resource.mem_id
AND organization.org_id = members.org_id
GROUP BY org_id, org_name