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

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

Related

SQL Distinct a column with conditions

I'm working on a query where I need to count distinct CarId row when the column LocationId is not null and get all CarId if its null or 0 but the query that I tried distincts all the CarId even if its null
#LocId int
Select Count(distinct a.CarId) from VehicleDetails a
inner join VehicleDocuments b on a.DocId=b.DocId
left join VehicleShipmentDetails dpg on dpg.VehicleShipmentId= b.VehicleShipmentId
where b.LogicalDelete=0 and a.LogicalDelete=0
and (dpg.LocationId= #LocId or dpg.LocationId= 0 or dpg.LocationId is null)
| ID | CarId | LocationId | DateCreated |
|------+----------------+-----------------+---------------|
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | null | 01/14/2019 |
| 3 | 2 | 0 | 02/03/2019 |
| 4 | 2 | 5 | 12/30/2018 |
| 5 | 4 | 3 | 01/10/2019 |
| 6 | 3 | 5 | 02/14/2019 |
| 7 | 2 | 5 | 03/13/2019 |
Desired output:
| ID | CarId | LocationId | DateCreated |
+------+----------------+-----------------+---------------+
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | null | 01/14/2019 |
| 3 | 2 | 0 | 02/03/2019 |
| 4 | 2 | 5 | 03/13/2019 |
| 5 | 4 | 3 | 01/10/2019 |
| 6 | 3 | 5 | 02/14/2019 |
Current Output
| ID | CarId | LocationId | DateCreated |
+------+----------------+-----------------+---------------+
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | 5 | 01/14/2019 |
| 3 | 4 | 3 | 01/10/2019 |
| 4 | 3 | 5 | 02/14/2019 |
Im getting a count of 4 but i needed to have 6 as the Count
EDIT: My goal is to remove the row to Distinct CarId if the value of the LocationId is Null or 0 but on my Current code, It distincts all CarId that is null,0 and equals to #LocId
You can query something like this, replace your_table by your actual set of data.
SELECT ID, CardId, LocationId, DateCreated
FROM your_table as T
WHERE NOT EXISTS (SELECT *
FROM your_table as T1
WHERE T.ID > T1.ID AND T.CarID = T1.CarID)
In SQL, you can use the statement CASE to manage conditions (just like the "if then else" in other programming languages). In your case this function could help because you have two differents cases to handle.

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

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

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

Creating Select queries inside a query in MySQL

How can I display a distinct Field1 and a Field2 that contains the max value for field2?
For example, from the left table get the right table:
| Field1 | Field2 | | Field1 | Field2 |
-------------------- -------------------
| A | 1 | | A | 3 |
| A | 2 | | B | 2 |
| A | 3 | | C | 4 |
| B | 1 | -------------------
| B | 2 |
| C | 1 |
| C | 2 |
| C | 3 |
| C | 4 |
--------------------
I'm a student yet and still a newbie in MySQL :)
select field1, max(field2) as maxf2
from table
group by field1

MySQL multiple right joins

mysql> select * from product;
+------------+---------------+
| product_id | name |
+------------+---------------+
| 1 | Car |
| 2 | House |
| 3 | Cat |
| 4 | Blank Product |
+------------+---------------+
4 rows in set (0.00 sec)
mysql> select * from tag;
+--------+-----------+
| tag_id | name |
+--------+-----------+
| 1 | Expensive |
| 2 | Fast |
| 3 | Mean |
| 4 | Large |
| 5 | Small |
| 6 | Alive |
| 7 | Blank Tag |
+--------+-----------+
7 rows in set (0.00 sec)
mysql> select * from product_tag;
+------------+--------+
| product_id | tag_id |
+------------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
| 3 | 5 |
| 3 | 6 |
+------------+--------+
10 rows in set (0.00 sec)
Why does the following query return my blank tag but not my blank product?
mysql> select * from product_tag right join product using (product_id)
right join tag using (tag_id);
+--------+-----------+------------+-------+
| tag_id | name | product_id | name |
+--------+-----------+------------+-------+
| 1 | Expensive | 1 | Car |
| 1 | Expensive | 2 | House |
| 2 | Fast | 1 | Car |
| 2 | Fast | 3 | Cat |
| 3 | Mean | 1 | Car |
| 3 | Mean | 3 | Cat |
| 4 | Large | 1 | Car |
| 4 | Large | 2 | House |
| 5 | Small | 3 | Cat |
| 6 | Alive | 3 | Cat |
| 7 | Blank Tag | NULL | NULL |
+--------+-----------+------------+-------+
11 rows in set (0.00 sec)
You are using right join. In your query tags ids are the base where MySQL will start matching. The right join is evaluated from right to left. If you break your query into two parts. The first one will be:
select * from product_tag right join tag using (tag_id);
+--------+-----------+------------+
| tag_id | name | product_id |
+--------+-----------+------------+
| 1 | expensive | 1 |
| 1 | expensive | 2 |
| 2 | fast | 1 |
| 2 | fast | 3 |
| 3 | mean | 1 |
| 3 | mean | 3 |
| 4 | larg | 1 |
| 4 | larg | 2 |
| 5 | small | 3 |
| 6 | alive | 3 |
| 7 | blank tag | NULL |
+--------+-----------+------------+
As you see there is not product_id that matches with the blank tag. The explains why joining this result with the product table will give you the result you saw.
If you use left join instead you'll get this result:
select * from product_tag left join product using (product_id) left join tag using (tag_id);
+--------+------------+-------+-----------+
| tag_id | product_id | name | name |
+--------+------------+-------+-----------+
| 1 | 1 | car | expensive |
| 2 | 1 | car | fast |
| 3 | 1 | car | mean |
| 4 | 1 | car | larg |
| 1 | 2 | house | expensive |
| 4 | 2 | house | larg |
| 2 | 3 | cat | fast |
| 3 | 3 | cat | mean |
| 5 | 3 | cat | small |
| 6 | 3 | cat | alive |
+--------+------------+-------+-----------+
There is no row associating product id 4 with a tag. You need to add a row to the product_tag table like the following:
+------------+--------+
| product_id | tag_id |
+------------+--------+
| 4 | 7 |
+------------+--------+
You are using a RIGHT JOIN so all the rows in the right table i.e "tag" will be returned even if there is no match in the joined tables.