MySQL select only rows remaining - mysql

I have a table like:
id | name | type
1 | a | in
2 | a | out
3 | a | in
4 | a | out
5 | a | in
6 | b | in
7 | b | out
8 | b | in
9 | c | in
10 | c | out
I want to select all the in events that have no matching out event.
5 | a | in
8 | b | in
is it possible?
Edit:
ins and outs have same name don't have to follow each other.
id | name | type
1 | a | in
2 | a | out
3 | a | in
4 | a | out
5 | c | in
6 | b | in
7 | b | out
8 | b | in
9 | a | in
10 | c | out

One way is to use a not exists predicate with a correlated subquery that checks that there doesn't exists any 'out' row with a higher id than the 'in' row (where the name is the same).
select * from table1 a
where type = 'in'
and not exists (
select 1 from table1 b
where a.name = b.name
and b.type = 'out'
and b.id > a.id
);
Sample SQL Fiddle

here ya go....
SELECT t1.id,t1.name,t1.type
FROM `t` t1
JOIN `t` t2 on t1.id + 1=t2.id
where t2.type = t1.type
and here is the complete sql fiddle
http://sqlfiddle.com/#!9/90d87/6
I've matched the table to itself on an offset of 1: t1.id+1 = t2.id
then I pull only the ones that are the same: where t2.type = t1.type
this would match anything that is consecutively same, if outs happen as well and need to watch for that, use where t2.type = t1.type AND t1.type = 'in'

Related

MySQL find duplicates based on another column value

I have the following table
+----+------+-------+
| id | user | value |
+----+------+-------+
| 1 | 10 | A |
| 2 | 12 | B |
| 3 | 24 | A |
| 4 | 33 | C |
+----+------+-------+
I want to retreive all the duplicates users that have the same key
+----+------+-------+
| id | user | value |
+----+------+-------+
| 1 | 10 | A |
| 3 | 24 | A |
+----+------+-------+
I've tried that with no luck
SELECT DISTINCT A.user, A.value
FROM table as A
INNER JOIN ( SELECT value FROM table GROUP BY value HAVING COUNT(value) > 1 ) AS B
ON A.value = B.value
You may try below query -
SELECT id, user, value
FROM YUOR_TABLE T1
WHERE EXISTS (SELECT 1
FROM YOUR_TABLE T2
WHERE T1.value = T2.value
AND T1.user <> T2.user)

How to join tables on multiple records and multiple fields

I need a solution for a rather complex mySQL join:
Say we have 3 tables: T1, T2 and T3.
Table 1 contains the main ID and a name field.
Table1
| ID | Name |
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
Table 2 contains a column for the Table 1's ID and a value
Table2
| ID | oID | Value |
| 1 | 1 | aaa |
| 2 | 1 | bbb |
| 3 | 1 | ccc |
| 4 | 2 | ddd |
| 5 | 2 | eee |
| 6 | 3 | fff |
| 7 | 3 | ggg |
| 8 | 3 | hhh |
| 9 | 4 | iii |
So far so good, a simple JOIN will get me the Table2.Values linked to the Table1.Name
But there is also Table 3
Table3
| ID | oID | Condition | Value |
| 1 | 1 | color | red |
| 2 | 1 | brightness | 20 |
| 3 | 2 | color | green |
| 4 | 2 | brightness | 50 |
| 5 | 2 | saturation | 100 |
| 6 | 3 | color | green |
| 7 | 3 | brightness | 40 |
| 8 | 3 | saturation | 70 |
| 9 | 4 | color | purple|
What I need to do is get the T1.Name, with all the related T2.Values ONLY IF 2 (or more) conditions of T3 are OK:
For example:
All Names with matching T2.Values IF T3.Condition = 'green' AND T3.brightness = 50
As only ID 2 is green with a brightness of 50, that should give me
B ddd
B eee
I have tried several things:
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
t3.Condition = 'color'
AND
t3.Value = 'green'
AND
t3.Condition = 'brightness'
AND
t3.Value = 50
GROUP BY p.ID
Resulting in nothing of course, as no record has all four conditions
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
(t3.Condition = 'color' AND t3.Value = 'green')
AND
(t3.Condition = 'brightness'AND t3.Value = 50)
GROUP BY p.ID
Same result: nothing
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
(t3.Condition = 'color' AND t3.Value = 'green')
OR
(t3.Condition = 'brightness' AND t3.Value = 50)
GROUP BY p.ID
I get all records with brightness 50 and those with color green... but not those with both those conditions.
I also tried with conditions in the JOIN ON statement, LEFT JOINS, WHERE condition IN (..,..) and so on but without success.
The one who finds the solution gets eternal fame!
SELECT * FROM table3;
+----+-----+------------+--------+
| ID | oID | condition | value |
+----+-----+------------+--------+
| 1 | 1 | color | red |
| 2 | 1 | brightness | 20 |
| 3 | 2 | color | green |
| 4 | 2 | brightness | 50 |
| 5 | 2 | saturation | 100 |
| 6 | 3 | color | green |
| 7 | 3 | brightness | 40 |
| 8 | 3 | saturation | 70 |
| 9 | 4 | color | purple |
+----+-----+------------+--------+
9 rows in set (0.00 sec)
mysql>
SELECT x.oid
FROM table3 x
WHERE (x.condition,x.value) IN(('color','green'),('brightness','50'))
GROUP
BY x.oid
HAVING COUNT(*) = 2;
+-----+
| oid |
+-----+
| 2 |
+-----+
Note that condition is a reserved word, making it a less than ideal choice as a table/column identifier
What you are doing wrong is that you join table3 to the other 2 tables.
Instead you should join a subquery of table3 which returns the oIDs that meet your conditions:
SELECT t1.name, t2.value
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.oid
INNER JOIN (
SELECT oID FROM table3
GROUP BY oID
HAVING SUM(`condition` = 'color' AND value = 'green') > 0
AND SUM(`condition` = 'brightness' AND value = '50') > 0
) t3 ON t1.id = t3.oID
See the demo.
Results:
| name | value |
| ---- | ----- |
| B | ddd |
| B | eee |

get calculation from two tables related with a third table

i have this table
table1
| id | name |
| 1 | axe |
| 2 | bow |
| 3 | car |
| 4 | dart |
and these two tables
table2 table3
| t1_id | number | | t1_id | letter |
| 1 | 5 | | 1 | a |
| 1 | 6 | | 1 | b |
| 1 | 2 | | 1 | c |
| 2 | 2 | | 2 | a |
| 2 | 2 | | 2 | c |
| 2 | 3 | | 2 | r |
| 3 | 8 | | 3 | y |
| 3 | 3 | | 3 | i |
| 3 | 1 | | 3 | a |
| 4 | 8 | | 4 | a |
| 4 | 9 | | 4 | b |
| 4 | 10 | | 4 | c |
where in it t1_id is the table1 id
what i want to do is to get all table1 records having table3 letters a b c and the avg of their numbers like this order by the letter_count DESC first then by the avg_numbers DESC
| id | name | letter_count | avg_number |
| 4 | dart | 3 | 9 |
| 1 | axe | 3 | 4.3333333333 |
| 2 | bow | 2 | 2.3333333333 |
| 3 | car | 1 | 4 |
the query i expected to work properly was http://www.sqlfiddle.com/#!9/69086b/3/0
SELECT
t1.id,
t1.name,
COUNT(t3.letter) AS letter_count,
AVG(t2.number) AS avg_number
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id
LEFT JOIN
table3 t3
ON t3.t1_id = t1.id
AND t3.letter IN ('a', 'b', 'c')
GROUP BY
t1.id
ORDER BY
letter_count DESC,
avg_number DESC
but numbers are totally different and in accurate but the order is correct
i don't want to get the letter_count and avg_number values but i just want to order by them but their values are worrying me with the query performance
i wouldn't notice this weird values because my actual query is
SELECT
t1.id,
t1.name
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id
LEFT JOIN
table3 t3
ON t3.t1_id = t1.id
AND t3.letter IN ('a', 'b', 'c')
GROUP BY
t1.id
ORDER BY
COUNT(t3.letter) DESC,
AVG(t2.number) DESC
which only gives me proper ordery
| id | name |
| 4 | dart |
| 1 | axe |
| 2 | bow |
| 3 | car |
but after checking the values i was surprsied by the letter_count do i just ignore the values and it wouldn't affect the performance in my big table?
You are aggregating over two different dimensions. This causes a Cartesian product. One way to fix this is to aggregate before joining:
SELECT t1.id, t1.name, t2.letter_count, t2.avg_number
FROM table1 t1 INNER JOIN
(SELECT t2.t1_id, AVG(t2.number) as avg_number
FROM table2 t2
GROUP BY t2.t1_id
) t2
ON t2.t1_id = t1.id LEFT JOIN
(SELECT t3.t2_id, COUNT(t3.letter) as letter_count
FROM table3 t3
WHERE t3.letter IN ('a', 'b', 'c')
GROUP BY t3.t2_id
) t3
ON t3.t1_id = t1.id
ORDER BY t3.letter_count DESC, t2.avg_number DESC;
On the third table, IN-SELECT Subquery.
SELECT
t1.id,
t1.name,
(
SELECT count(letter)
FROM t3
where t3.t1_id = t1.id
) as lettercount,
AVG(t2.number) AS avg_number
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id

Get greatest rows based on multiple columns in MySQL

I have a table that looks like:
id | title | value | language
---+-------+-------+---------
1 | a | 1800 | NULL
2 | a | 1900 | NULL
3 | b | 1700 | NULL
4 | b | 1750 | NULL
5 | b | 1790 | 1
6 | c | 1892 | NULL
7 | c | 1900 | 1
8 | c | 1910 | 2
9 | d | 3020 | NULL
Would like to have the following result:
id | title | value | language
---+-------+-------+---------
2 | a | 1900 | NULL
4 | b | 1750 | NULL
5 | b | 1790 | 1
6 | c | 1892 | NULL
7 | c | 1900 | 1
8 | c | 1910 | 2
9 | d | 3020 | NULL
The point is to select the greatest value in value column of every language of every title - greatest being the latest. Secondly, would like to avoid Aggregate functions like MAX, DISTINCT or GROUP-BY as I am building a MySQL View using the MERGE algorithm, and don't want to end up creating a temporary table (See the bottom section of https://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html).
So far this works, but only returns greatest row per title:
SELECT t1.title
FROM table t1
LEFT OUTER JOIN table t2
ON t1.title = t2.title
AND t1.value < t2.value
WHERE t2.title IS NULL
How can I create one that takes language into account like the results above? Thanx.
You can do it with NOT EXISTS:
select t.*
from tablename t
where not exists (
select 1 from tablename
where
title = t.title and
coalesce(language, 0) = coalesce(t.language, 0) and
value > t.value
)
See the demo.
Results:
| id | title | value | language |
| --- | ----- | ----- | -------- |
| 2 | a | 1900 | NULL |
| 4 | b | 1750 | NULL |
| 5 | b | 1790 | 1 |
| 6 | c | 1892 | NULL |
| 7 | c | 1900 | 1 |
| 8 | c | 1910 | 2 |
| 9 | d | 3020 | NULL |
This answer assumes that you are using MySQL 8+, in which your query becomes very easy. MySQL 8 and later version support analytic functions, which were added with the intention to solve problems such as this.
We can try using ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY title, language ORDER BY value DESC) rn
FROM yourTable
)
SELECT id, title, value, language
FROM cte
WHERE rn = 1;
Demo
There is a way to handle this with earlier versions of MySQL, but it requires user variables, and tends to be very ugly. So maybe consider upgrading if you expect to have many queries similar to this one.
This should give you what you want.
SELECT t1.title, t1.value, t1.language
FROM [Table] t1
LEFT OUTER JOIN [Table] t2 ON
t1.title = t2.title AND
(IFNULL(t1.language, '') = IFNULL(t2.language, ''))
WHERE
t1.value > t2.value;

How can I select all parents hierarchically?

I have a table like this:
// tags
+----+--------------+-----------+---------+
| id | tag_name | parent_id | related |
+----+--------------+-----------+---------+
| 1 | programming | NULL | 1 |
| 2 | medical | NULL | 2 |
| 3 | juridical | NULL | 3 |
| 4 | HTML | 1 | 1 |
| 5 | PHP | 1 | 1 |
| 6 | function | 5 | 1 |
| 7 | ampoule | 2 | 2 |
| 8 | needle | 7 | 2 |
| 9 | CSS | 1 | 1 |
| 10 | echo | 5 | 1 |
| 11 | padding | 9 | 1 |
+----+--------------+-----------+---------+
Also I have this value: function. Now I want to select all its parents. So this is expected result:
+----+--------------+-----------+---------+
| id | tag_name | parent_id | related |
+----+--------------+-----------+---------+
| 1 | programming | NULL | 1 |
| 5 | PHP | 1 | 1 |
+----+--------------+-----------+---------+
How can I do that?
Note: related column has nothing to do with this question. It've added it because sometimes I need to select all related tags (both parents and children).
It might be better to get one level at a time from your code to get his data. But if you know how many levels you have in your hierarchies you can prepare code for this.
If you have a maximum of 4 hierarchies including the last level this code would work. Then you can add more unions for more levels or remove the last union if you dont have as many levels.
I have set the name 'tableName' on your table here and just wrote it together quickly but this should work. Let me know if it does not work and i will try to find a mysql server to run against.
SELECT t1.*
FROM tableName t1
JOIN tableName t2 ON t1.id = t2.parent_id
WHERE t2.tag_name = 'function'
UNION ALL
SELECT t1.*
FROM tableName t1
JOIN tableName t2 ON t1.id = t2.parent_id
JOIN tableName t3 ON t2.id = t3.parent_id
WHERE t3.tag_name = 'function'
UNION ALL
SELECT t1.*
FROM tableName t1
JOIN tableName t2 ON t1.id = t2.parent_id
JOIN tableName t3 ON t2.id = t3.parent_id
JOIN tableName t4 ON t3.id = t4.parent_id
WHERE t4.tag_name = 'function'
ORDER BY ID
(Changing 'function' to a variable is of course a good idea)