How to select bi-directional mapped rows from a table in MySQL? - mysql

I have the MySQL table called table.
| ID |item_code| item_name |
| 1 | 12345 | abc |
| 2 | 12345 | xyz |
| 3 | 11221 | abc |
| 4 | 19261 | www |
| 5 | 12345 | abc |
| 6 | 62898 | abc |
| 7 | 92648 | xxx |
| 8 | 45678 | xxx |
| 9 | 1234 | pqrs |
| 10 | 2345 | defg |
| 11 | 1234 | pqrs |
I want the query which will results the below table:
> |item_code | item_name |
> | 12345 | abc |
> | 12345 | xyz |
> | 11221 | abc |
> | 62898 | abc |
> | 92648 | xxx |
> | 45678 | xxx |
I want the result considering of:
item_code mapped to multiple item_name. Here item_code 12345 is mapped with abc and xyz. item_code 1234 is mapped to pqrs.
item_name mapped to multiple item_code. Here item_name abc is mapped with three items: 12345,11221,62898. item_name xxx is mapped to 92648 and 45678.
Here is the SQL statement I tried:
(select a.item_code,a.item_name from table a
join ( select item_code
from table
group by item_code
having count(*) > 1 ) b
on a.item_code = b.item_code)
union
(select a.item_code,a.item_name from table a
join ( select Item_name
from table
group by Item_name
having count(*) > 1 ) b
on a.Item_name = b.Item_name);
But this SQL query does not generate the bi directional mapped keys. Can someone help me generate these bi-directional mappings?

SELECT DISTINCT t.item_code, t.item_name
FROM mytable t
INNER JOIN
(SELECT item_code, COUNT(DISTINCT item_name) num FROM mytable GROUP BY item_code) code
ON (t.item_code = code.item_code)
INNER JOIN
(SELECT item_name, COUNT(DISTINCT item_code) num FROM mytable GROUP BY item_name) name
ON (t.item_name = name.item_name)
WHERE code.num > 1 OR name.num > 1
The subqueries used in the two JOIN clauses are used to attach the counts for which each item code or name is repeated. The joined result set is filtered using the WHERE clause at the outermost level to include only those DISTINCT combinations where the code and/or the name was associated with multiple values for the opposite field.

Related

Get those items which are ordered after they have been delivered

I have two tables, namely itemOrders and itemDelivered.
itemOrders
+-------+---------+--------+
| id | orderid | itemid |
+-------+---------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 1 |
| 6 | 3 | 2 |
+-------+---------+--------+
And
itemDelivered
+-------+-------------+--------+
| id | orderId | itemid |
+-------+-------------+--------+
| 1 | 2 | 2 |
| 2 | 3 | 2 |
| 3 | 2 | 1 |
+-------+-------------+--------+
From the above scenario I want all those distinct items whose max orderId in the table itemDelivered is less than max orderId in the table itemOrders.
In the above example I should get itemid 1 as the result, as it's max orderid is 2 in table itemDelivered, which is less than its max orderid in table itemOrders which is 3.
I wrote the following query but it gives me both the items, 1 and 2 as item No. 2 doesn't have orderId 1 in itemDelivered table.
SELECT DISTINCT( itemid )
FROM itemorders
WHERE orderid NOT IN (SELECT orderid
FROM itemdelivered)
You can LEFT JOIN between the two table using itemid, and GROUP BY on the itemid.
Eventually use HAVING clause to consider only those itemid values, where MAX(itemdelivered.orderid) < MAX(itemorders.orderid)
View on DB Fiddle
SELECT io.itemid
FROM itemorders AS io
LEFT JOIN itemdelivered AS id
ON id.itemid = io.itemid
GROUP BY io.itemid
HAVING MAX(id.orderid) < MAX(io.orderid)
OR MAX(id.orderid) IS NULL
Result
| itemid |
| ------ |
| 1 |
Ok, manage to write a query which gives the desired output.
SELECT io.itemid
FROM itemorders as io
LEFT JOIN itemdelivered AS id ON io.orderid = id.orderid AND io.itemid = id.itemid
WHERE id.itemid IS NULL
HAVING MAX(id.orderid) IS NULL
ORDER BY io.id

Mysql delete similar rows according to specific columns except the ones with highest id

my table has duplicate row values in specific columns. i would like to remove those rows and keep the row with the latest id.
the columns i want to check and compare are:
sub_id, spec_id, ex_time
so, for this table
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 1 | 100 | 444 | 09:29 | 2 |
| 2 | 101 | 555 | 10:01 | 10 |
| 3 | 100 | 444 | 09:29 | 23 |
| 4 | 200 | 321 | 05:15 | 5 |
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i would like to get this result
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i was able to build this query to select all duplicate rows from multiple columns, according to this question
select t.*
from mytable t join
(select id, sub_id, spec_id, ex_time, count(*) as NumDuplicates
from mytable
group by sub_id, spec_id, ex_time
having NumDuplicates > 1
) tsum
on t.sub_id = tsum.sub_id and t.spec_id = tsum.spec_id and t.ex_time = tsum.ex_time
but now im not sure how to wrap this select with a delete query to delete the rows except for the ones with highest id.
as shown here
You can modify your sub-select query, to get maximum value of id for each duplication combination.
Now, while joining to the main table, simply put a condition that id value will not be equal to the maximum id value.
You can now Delete from this result-set.
Try the following:
DELETE t
FROM mytable AS t
JOIN
(SELECT MAX(id) as max_id,
sub_id,
spec_id,
ex_time,
COUNT(*) as NumDuplicates
FROM mytable
GROUP BY sub_id, spec_id, ex_time
HAVING NumDuplicates > 1
) AS tsum
ON t.sub_id = tsum.sub_id AND
t.spec_id = tsum.spec_id AND
t.ex_time = tsum.ex_time AND
t.id <> tsum.max_id

Data required from two tables in sql using group by

Table 1 shows subjects and its components.
Table 2 shows marks for each subject and its corresponding component.
Table 2 is related to Table 1 by its primary key.
The second image shows the expected output where each row shows the count of the components for the particular subject for that particular student and also the sum of marks that student secured in that subject( sum of marks of all components in that subject).
I've tried with many numbers of group by statements but still couldn't reach the required output.
It would be of great help if a valid solution is provided.
Thanks in advance.
Try the following queries:
Query-1:
SELECT sub_id, COUNT(component_id) t1_component_count
FROM table1
GROUP BY sub_id;
Output:
+--------+--------------------+
| sub_id | t1_component_count |
+--------+--------------------+
| s1 | 3 |
| s2 | 3 |
| s3 | 1 |
| s4 | 1 |
+--------+--------------------+
Query-2:
SELECT t2.stu_id, t1.sub_id, t3.comp_cnt AS t1_component_count,
count(t1.component_id) AS t2_component_count, sum(t2.stu_marks) total_marks
FROM table2 t2 INNER JOIN table1 t1 ON t2.t1_id = t1.t1_id
INNER JOIN
(SELECT sub_id, COUNT(component_id) comp_cnt FROM table1 GROUP BY sub_id)
AS t3 ON t1.sub_id = t3.sub_id
GROUP BY t2.stu_id, t1.sub_id;
Output:
+--------+--------+--------------------+--------------------+-------------+
| stu_id | sub_id | t1_component_count | t2_component_count | total_marks |
+--------+--------+--------------------+--------------------+-------------+
| stu1 | s1 | 3 | 3 | 100 |
| stu1 | s2 | 3 | 2 | 130 |
| stu2 | s3 | 1 | 1 | 50 |
| stu3 | s2 | 3 | 1 | 30 |
| stu4 | s1 | 3 | 1 | 90 |
+--------+--------+--------------------+--------------------+-------------+

Find the maximum value(s) from a column and selecting their rows

After looking at other examples I still have not been able to find a solution, that is why I am asking for some help.
My table structure:
V_id | name | group_id | other columns
----------------------
1 | | 1
2 | | 1
3 | | 2
4 | | 3
5 | | 3
I have been struggling to build a query, to select all the rows which have the maximum value from the group_id column.
therefore output should be like this:
V_id | name | group_id | other columns
----------------------
4 | | 3
5 | | 3
which I believe can be solved by selecting all records where group_id is the highest.
and also need a query to get all the other remaining rows.
which in this case, should be like this:
V_id | name | group_id | other columns
----------------------
1 | | 1
2 | | 1
3 | | 2
which I believe can be done by selecting all records where group_id < Max(group_id)
for the first part of the problem,
SELECT *
FROM tableName
WHERE group_id = (SELECT MAX(group_ID) FROM TableName)
and for the second part,
SELECT *
FROM tableName
WHERE group_id < (SELECT MAX(group_ID) FROM TableName)
You can use JOIN for that:
SELECT a.*
FROM Table1 a
JOIN (SELECT MAX(Group_ID) AS MAXID
FROM Table1) B
ON a.Group_id = B.MaxID;
Result:
| V_ID | NAME | GROUP_ID |
----------------------------
| 4 | (null) | 3 |
| 5 | (null) | 3 |
For the remaining rows use LEFT JOIN with a condition like this:
SELECT a.*
FROM Table1 a
LEFT JOIN (SELECT MAX(Group_ID) AS MAXID
FROM Table1) B
ON a.Group_id = B.MaxID
WHERE B.MaxID IS NULL;
Result:
| V_ID | NAME | GROUP_ID |
----------------------------
| 1 | (null) | 1 |
| 2 | (null) | 1 |
| 3 | (null) | 2 |
See this SQLFiddle

MySQL GROUP BY "and filter"

Let's say i have query like this:
SELECT name, GROUP_CONCAT(number)
FROM objects
GROUP BY name
And it outputs:
+----------+----------------------+
| NAME | GROUP_CONCAT(NUMBER) |
+----------+----------------------+
| false_1 | 2,1 |
| false_2 | 3,4 |
| true_1 | 4,3,2,1 |
| true_2 | 2,3 |
+----------+----------------------+
Now how can i return rows having 2 AND 3 as number?
Note: This query is grouped - table has 10 rows, like so:
+---------+--------+
| NAME | NUMBER |
+---------+--------+
| true_1 | 1 |
| true_1 | 2 |
| true_1 | 3 |
| ... | ... |
+---------+--------+
[Link to SQLFiddle]
SELECT name, GROUP_CONCAT(number)
FROM objects
WHERE number IN (2,3)
GROUP BY name
HAVING COUNT(*) = 2
SEE SQLFiddle Demo
or if you want to retain all value on which the name has,
SELECT a.name, GROUP_CONCAT(A.number)
FROM objects a
INNER JOIN
(
SELECT name
FROM objects
WHERE number IN (2,3)
GROUP BY name
HAVING COUNT(*) = 2
) b ON a.Name = b.Name
GROUP BY a.name
SEE SQLFiddle Demo